sgd :: (Monad m, MonadTrans t, Monad (t (Pipe a (Scalar w, w) m)), Monad (t m), MFunctor t, VectorSpace w)
    => (Scalar w -> w -> w -> t m w) -- update function
    -> (w -> a -> m (Scalar w, w)) -- cost function gradient
    -> w -- initial weights
    -> t (Pipe a (Scalar w, w) m) () -- streams out cost weights for each iteration
sgd update cost_grad w_0 =
  distribute $ sgd' update cost_grad w_0
sgd :: (Monad m, MonadTrans t, Monad (t (Pipe a (Scalar w, w) m)), Monad (t m), MFunctor t, VectorSpace w)
    => (Scalar w -> w -> w -> t m w) -- update function
    -> (w -> a -> m (Scalar w, w)) -- cost function gradient
    -> w -- initial weights
    -> t (Pipe a (Scalar w, w) m) ()
sgd update cost_grad w_0 =
  distribute $ sgd' update cost_grad w_0
sgd update cost_grad w_0 =
  distribute $ sgd' update cost_grad w_0