[Autograd] Does forward need to be a method?
In the autograd the `forward` method is never used outside of the file that defines it.

And within that file the actual implementation to call is always known at compile-time this means that for `forward` we pay the cost of dynamic dispatch without benefits:

```Nim
type AddGate* {.final.} [TT] = ref object of Gate[TT]

method forward*[TT](self: AddGate[TT], a, b: Variable[TT]): Variable[TT] {.inline, locks:0.}=
  new result

  result.context = a.context
  result.value = a.value + b.value

method backward*[TT](self: AddGate[TT], gradient: TT): SmallDiffs[TT] {.noInit, inline, locks:0.}=
  result[0] = gradient
  result[1] = gradient

proc `+`*[TT](a, b: Variable[TT]): Variable[TT] =
  when compileOption("boundChecks"):
    check_ctx(a, b)

  # Gate
  var gate: AddGate[TT]
  new gate
  gate.nb_grads = 2

  # Node
  var node: Node[TT]
  new node

  node.gate = gate
  node.parents[0] = a.weakRef
  node.parents[1] = b.weakRef

  a.context.push(node)

  # Resulting var
  result = gate.forward(a, b)
  node.payload = result

  # Caching for backprop
  if a.is_grad_needed or b.is_grad_needed:
    result.grad = zeros[getSubType(TT)](result.value.shape)
    result.requires_grad = true
```

Instead we could use a template (not sure a proc would work due to runtime type issue).

## Caveats

It might be that hessian or high-order derivatives computations require the `forward` pass.