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.