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:
- Why does it do that? Is it important enough we get the initial value that we just have to accept this delay?
- Could an alternative function return the next value as soon as it is available? (In other words, could this alternative function return "hello" right after the 'h' was sent in, so the '~' could be removed?) The resulting signal would then never produce the initial value.
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.