What I mean by first-order constraint
First, I'll explain what I mean by first-order constraint on arrows: Due to the way arrows desugar, you cannot use a locally bound name where an arrow command is expected in the arrow do-notation.
Here is an example to illustrate:
proc x -> f -< x + 1
desugars to arr (\x -> x + 1) >>> f
and similarly proc x -> g x -< ()
would desugar to arr (\x -> ()) >>> g x
, where the second x
is a free variable. The GHC user guide explains this and says that when your arrow is also a monad you may make an instance of ArrowApply
and use app
to get around this. Something like, proc x -> g x -<< ()
becomes arr (\x -> (g x, ())) >>> app
.
My Question
Yampa defines the accumHold
function with this type: a -> SF (Event (a -> a)) a
.
Due to this first-order limitation of arrows, I'm struggling to write the following function:
accumHoldNoiseR :: (RandomGen g, Random a) => (a,a) -> g -> SF (Event (a -> a)) a
accumHoldNoiseR r g = proc f -> do
n <- noiseR r g -< ()
accumHold n -< f
The definition above doesn't work because n
is not in scope after desugaring.
Or, similarly this function, where the first part of the pair to SF
is meant to be the initial value passed to accumHold
accumHold' :: SF (a,Event (a -> a)) -> a
accumHold' = ...
Is there some combinator or trick that I'm missing? Or is it not possible to write these definitions without an ArrowApply
instance?
tl;dr: Is it possible to define accumHoldNoiseR :: (RandomGen g, Random a) => (a,a) -> g -> SF (Event (a -> a)) a
or accumHold' :: SF (a,Event (a -> a)) -> a
in yampa?
Note: There is no instance of ArrowApply
for SF
. My understanding is that it doesn't make sense to define one either. See "Programming with Arrows" for details.
This is a theoretical answer. Look to Roman Cheplyaka's answer to this question, which deals more with the practical details of what you're trying to achieve.
The reason n
is out of scope is that for it to be in scope to use there, you would have the equivalent of bind
or >>=
from monads. It's the use of the results of a previous computation as a functional input to the next which makes something as powerful as a monad.
Hence you can supply n
as a function argument to a subsequent arrow exactly when you can make an ArrowApply instance.
Chris Kuklewicz correctly points out in his comment that -<<
would bring n
into scope - it also uses app
, so you need an ArrowApply instance.
Not unless you use ArrowApply. This is what ArrowApply is for.
noiseR
is a signal function; it produces a stream of random numbers, not just one random number (for that, you'd just use randomR
from System.Random
).
On the other hand, the first argument of accumHold
is just one, initial, value.
So this is not just some limitation — it actually prevents you from committing a type error.
If I understand correctly what you're trying to do, then simply using randomR
should do the trick. Otherwise, please clarify why you need noiseR
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With