Access to variables of a customized Gate while doing forward pass
Hello!

I implemented a simplified version of BatchNorm layer (1-dimensional input only, no momentum), by using Linear layer as the reference example.

So I was able to add this layer into DemoNet mnist example (right after the 'hidden') and to compare resulting loss with original - loss was decreasing faster :)

But when I tried to add some features to this implementation I stumbled upon a couple of difficulties. I need my Gate to hold a running_mean and running_var (simple [D,1] tensors, I suppose they shouldn't be wrapped into `Variable[...]` so no tracking of the gradient). So I have a definition of Gate as follows:

```nim
type BatchNormGate*[TT] {.final.} = ref object of Gate[TT]
  input, gamma, beta: Variable[TT]
  running_mean, running_var: TT 
  momentum: float32
```

Also corresponding functions in nn_dsl/dsl_initialization.nim and nn_dsl/dsl_types.nim are modified to initialize these values.

So if I check a simple BN model with expandMacros, these values are where they should be (supposedly): 

```nim
type
  TestNet_25090001 = object
    bn1: BatchNormLayer[Tensor[float32]]

proc init(ctx`gensym25085539: Context[Tensor[float32]];
         model_type`gensym25085540: typedesc[TestNet_25090001]): TestNet_25090001 =
  result.bn1.gamma = ctx.variable(ones[float32]([3, 1]), requires_grad = true)
  result.bn1.beta = ctx.variable(zeros[float32]([3, 1]), requires_grad = true)
  result.bn1.running_mean = zeros[float32]([3, 1])
  result.bn1.running_var = zeros[float32]([3, 1])
  result.bn1.momentum = 1.0

proc forward(self: TestNet_25090001; x: Variable[Tensor[float32]]): Variable[
    Tensor[float32]] =
  result =
    template bn1(x`gensym25085524: Variable): Variable =
      x`gensym25085524.batch_norm(self.bn1.gamma, self.bn1.beta,
                                  self.bn1.running_mean, self.bn1.running_var,
                                  self.bn1.momentum)

    batch_norm(x, self.bn1.gamma, self.bn1.beta, self.bn1.running_mean,
               self.bn1.running_var, self.bn1.momentum)
```

But I can access them only from `batch_norm_backward_ag[TT](self: BatchNormGate[TT], payload: Payload[TT]) : SmallDiffs[TT]`  function. 

I saw the `gru_forward(...)` function but honestly it's way of of handling cached_inputs wasn't quite clear for me.

What is the correct way to access Gate fields while doing the forward pass - `batch_norm*[TT](input, gamma, beta: Variable[TT], running_mean, running_var: TT, momentum:float32): Variable[TT])` ?

Tried to find an example of such usage in code/issues but only found few old (now refactored) examples [here](https://github.com/mratsim/Arraymancer/issues/331) where MeanGate/GRU had `.forward(self: Gate, ...)` 

Second question is about general scheme of implementation. I decided to code the backward gradients calculation to ameliorate my understanding of the ML concepts. But at first I thought that BN layer could be implemented as a combination of already defined Gates (mean, sum) which could use the functionality of autograd. Is there an example of such decomposition with arraymancer primitives ? 

Btw, I'd like to thank you for your work on this lib. Awesome job!
