Let's take a look at an example given in the hackage documentation for stateful
do
smp <- start (stateful "" (:))
res <- forM "olleh~" smp
print res
the output is:
["","o","lo","llo","ello","hello"]
What it does is push one character after the next into the signal network, first 'o', then 'l', 'l' again, and so on.
The state transformation function given to stateful ((:)
, aka cons
) takes an element and a list of elements and returns the list with the single element prepended. (In Haskell a String
is a list of Char
s)
What happens is this
(:) 'o' "" -- gives "o", then
(:) 'l' "o" -- gives "lo", etc
This is all quite straightforward, but did you notice there is a '~' at the end of the input string? So why does it not give
["","o","lo","llo","ello","hello", "~hello"]
^^^^^^
?
Probably because it gives the initial value first, then the result of the first invocation of (:)
second, etc. So if we push in six characters (including '~') we get out "" after five characters have been prepended. The string "~hello"
is actually in there, but on sampling the signal we get the old state (right before it is discarded).
I expected the following two (contrived) examples to produce the same output, but they do not:
-- 1
do
smp <- start $ do
n <- input
return (n*2)
res <- forM [1,2,3] smp
print res -- prints [2,4,6]
-- 2
do
smp <- start $ stateful 0 (\n _ -> n*2) -- ignore state
res <- forM [1,2,3] smp
print res -- prints [0,2,4]
-- edit: 3
-- to make the trio complete, you can use transfer to do the same
-- thing as 1 above
-- there is of course no reason to actually do it this way
do
smp <- start $ transfer undefined (\n _ _ -> n*2) (pure undefined)
res <- forM [1,2,3] smp
print res
So my questions are:
edit: After Alexander pointed me to transfer I came up with this:
do
smp <- start (stateful' "" (:))
res <- forM "olleh" smp -- no '~'!
print res -- prints ["o","lo","llo","ello","hello"]
stateful' :: a -> (p -> a -> a) -> SignalGen p (Signal a)
stateful' s f = transfer s (\p _ a -> f p a) (pure undefined)
stateful' seems to be very roughly 25% slower than stateful on my machine.
Probably because it gives the initial value first, then the result of the first invocation of (:) second, etc. So if we push in six characters (including '~') we get out "" after five characters have been prepended. The string "~hello" is actually in there, but on sampling the signal we get the old state (right before it is discarded).
That's exactly how stateful
works. Quoting the documentation:
The initial state is the first output, and every following output is calculated from the previous one and the value of the global parameter
That answers the first of your questions.
As for the second, yes, an alternative function could return the new state immediately. In fact, there's such a function already, called transfer
:
The current input affects the current output, i.e. the initial state given in the first argument is considered to appear before the first output, and can never be observed.
(It also carries state, which you in your examples should just ignore.)
With this function, the "hello" example can be rewritten like this:
do
smp <- start (transfer
""
(\character _ state -> character:state)
(pure undefined))
res <- forM "olleh" smp
print res