tomjaguarpaw / Arrows2

Ideas for the next generation of Haskell's Arrow notation
12 stars 0 forks source link

Convincingly demonstrate that Arrows are actually needed #2

Open tomjaguarpaw opened 4 years ago

tomjaguarpaw commented 4 years ago

Until recently, the only uses of Haskell arrows that I was aware of were my own library Opaleye and the (defunct) Netwire FRP library. Recently Tweak has introduced Funflow, a computational workflow library. I believe @lexi-lambda is working on something similar.

Are arrows really needed in these cases? In Opaleye's case, it seems that a monadic API would allow constructing SQL queries that are invalid in the absence of LATERAL JOIN (although the theory to support this observation is lacking). I'm not familiar enough with the practice of resumable workflows or build systems to know why arrows are needed there. I believe the original impetus for the introduction of arrows was to rule out certain pathological behaviours in parsers.

There's a huge amount of inertial resistance to arrows in the community. Can we convincingly demonstrate that arrows really are necessary?

lexi-lambda commented 4 years ago

I believe in the value of arrows. I have believed in the value of arrows for a little while, but this is the first time I’ve really sat down and started working with them. Past pet projects of mine have used arrows for parsers where Monad was too powerful and an explicit type parameter for the parser’s input was useful.

Until very recently, I thought perhaps arrows were an unnecessary abstraction, albeit a useful one due to compiler support for proc notation. I figured that Applicative + Selective probably got you all the expressive power of Arrow + ArrowChoice, so the need for Arrow in theory seemed questionable. However, literally within the past week, I’ve realized there is a very significant thing that arrows can do that Applicative cannot, and that’s provide effect composition.

A great many monad transformation, such as StateT and ExceptT, require a Monad instance on the underlying functor just to implement an Applicative instance. This is because they fundamentally need a kind of linearization, and Applicative cannot provide that power. Arrows, on the other hand, do provide that power, even with plain Arrow alone. This is exhibited in the fact that the Category instance for Kleisli m requires a full-blown Monad instance on m—morphism composition introduces the same data dependency as >>=, without the dynamic choice.

Is it possible to extend the Applicative hierarchy to add that kind of thing back? I’m not sure. I think that, fundamentally, doing that gives you Arrow (or at least something close to it). Consider: you need the ability for the output of one action to depend on the output of a previous action, but you need the actual graph of connections to be fully traversable without ever running any of them. This means you need a way to connect “function-like things” end-to-end, but you aren’t allowed to use the output of previous things to determine how the connections are made. At the bare minimum, that’s Category; adding some basic necessary combinators yields Arrow (or at least “Prearrow”).