Fantasy-land: Arrow specification

Created on 7 Sep 2017  路  16Comments  路  Source: fantasyland/fantasy-land

Is there any reason not to add an Arrow specification?

https://wiki.haskell.org/Typeclassopedia#Arrow

Happy to work on it if it hasn't been picked up before somewhere I haven't seen.

Arrow:

Generalization of a computation a i o with input i and output o.

Given it depends on Category it will have id and compose (<<<).
Then we need arr, first, second, split (aka *) and fanout (aka &&&).

ArrowChoice

Depends on Arrow. Let us choose which arrows to execute, as opposed to all of them

Adds left, right, multiplex (aka +++), fanin (aka |||).

ArrowApply

Depends on Arrow. Let us use an arrow resulting from a previous computation in our execution path.

Adds app.

ArrowLoop

Depends on Arrow. Let us use arrows which feed output themselves, enabling recursion.

Adds loop.

Most helpful comment

@puffnfresh Profunctor and Category are already specified. Looks like Strong needs a spec though.

All 16 comments

This would be huge for any Fantasy Land users who work with streams (esp. duplex streams like in Mithril or Flyd) and other reified actions a lot. In particular, functions are trivially arrows, and duplex streams also make sense as arrows (and not just profunctors).

Note: when you define the hierarchy, arrows are subtypes of categories, but arrows are also subtypes of profunctors, specifically, they are the category of strong profunctors. (Haskell doesn't have this in its hierarchy for historical reasons - arrows came before profunctors, so it was hard to add in retrospect.)


As for the type classes:

  • Arrow only needs arr and first - the rest is trivially derived. See the Haskell implementation for how this works.
  • ArrowChoice requires an Either or similar data type to begin with, so that needs to be kept in mind.
  • ArrowApply is theoretically equivalent to monads, just with a widely varying syntax that's sometimes more convenient when combined with applicatives. The docs on ArrowMonad give a quick summary of why.
  • ArrowLoop has quite a few laws around it, and JavaScript's impurity makes it less critical than it would be otherwise.

Thanks for the reply and for your comments.
I'm interested in having an Arrow specification because I'd like to use it in an upcoming library myself.

I'd definitely keep in mind arrows being strong profunctors.
I'll provide a minimal implementation spec:

  • Arrow: arr, first
  • ArrowChoice: left

I'd like to see us take the the Profunctor approach to adding what arrows would have provided. We should learn from the past rather than following Haskell verbatim.

Perhaps add Profunctor p => Strong p providing first and (Category p, Profunctor p) => PreArrow p providing arr?

Then (Strong a, PreArrow a) => Arrow a?

Strong would require some notion of a product type which FL currently doesn't provide any specs for.

I always thought of Arrow as more an implementation, or specific type that implements Profunctor, Category and Semigroupoid. Would this be the first occurrence in the spec that defines a type?

Wouldn't the way to handle this from a dev point of view (for like use with streams as @isiahmeadows pointed out), is to just use one of the already defined Arrows in the many ADT libs out there? To me adding Arrow would be like adding Tuple or List. I mean, what would stop us from then adding Up or Down Star? (although I guess they would still have ArrowChoice)

Although, I am not a good Haskeller so I may have the wrong intuition on Arrow

(or was this your point @joneshf?)

EDIT: Just read the Haskell bits again, and I do not think my comment is valid. But I will leave it here just for fun. 馃帀

Except in a couple of extreme cases, Arrow is exactly Strong Profunctor with Category. In practice, Arrow is legacy.

Proof that Arrow is different to Strong and Category:

https://www.eyrie.org/~zednenem/2017/07/twist

But you can see it's not by much. Strong and Profunctor are the more practical tools.

@gabejohnson Scott encode the Tuple

Follow up actions:

  • [ ] Specify Profunctor
  • [ ] Specify Strong
  • [ ] Specify Category

馃憤

@puffnfresh Profunctor and Category are already specified. Looks like Strong needs a spec though.

@isiahmeadows

This would be huge for any Fantasy Land users who work with streams (esp. duplex streams like in Mithril or Flyd)

I don't think so. An FRP library like Flyd is very different from arrow-based FRP. Classic FRP and arrow based FRP are two very different things. And classic FRP libraries do not benefit from the arrow abstraction. This can be witnessed by noting that FRP libraries in Haskell like Reactive Banana, Reflex, etc. do not implement Arrow.

If one wanted to implement arrow-based FRP in JavaScript (which I've never seen) they would benefit from an array abstraction. But then they would suffer from the big problem with Arrows which is that they're extremely cumbersome to work with unless the language provides arrow specific syntax.

I appreciate the discussion, I hadn't realised how much arrows were out of fashion.

@paldepind:
I think the point from @isiahmeadows's comment is that an Arrow can be a good abstraction for a stream (think of an Automaton Arrow like in Netwire or Auto), independently from the fact that you're implementing classic FRP or arrowized FRP (which is exactly where I want to get in the near future).

A strong profunctor is a good abstraction is the same way and - given that in JS arrows don't have a special syntax - picking one or another doesn't change much from an implementer point of view.

@framp

I think the point from @isiahmeadows's comment is that an Arrow can be a good abstraction for a stream (think of an Automaton Arrow like in Netwire or Auto), independently from the fact that you're implementing classic FRP or arrowized FRP.

How would you do that? I've never seen a classic FRP library that implemented arrows or used arrows in any way. For classic FRP monads seems to be a much better fit.

In arrowized FRP it's not the streams themselves that are Arrows since streams are not exposed directly in A-FRP. Instead it's the signal transformers that implement Arrow and they're a quite different abstraction from streams. Similairly for Auto. It's API exposed _stream transformers_ but not the streams themselves.

which is exactly where I want to get in the near future

Classic FRP or arrowized FRP? If classic FRP then you may be interested in this JavaScript implementation of classic FRP with continuous time which I'm one of the authors of. If arrowized FRP then I think that sounds like a very cool project 馃憤 It would be interesting if you could get something like Netwire or Auto to work in JavaScript.

@paldepind To clarify, I'm speaking of arrowized, transform-based FRP, which is IMHO far easier to use than classic unidirectional FRP.

Probably a couple better example of where arrows have proven useful in the Haskell world is with netwire (an arrowized FRP library) and HXT (an XML processing toolkit)

I specifically singled out Mithril streams and Flyd because they're full duplex all the time, unlike normal streams. They can be used as transforms with a little bit of boilerplate, unlike most FRP libraries in the JS world. You could also trivially wrap an object-mode Transform stream to become arrowized, but they're already treated like that in practice (see Gulp for a good example of this). Here's an explanation of each types' methods:

Category:

Arrow:

  • arr is mostly third-party through2.
  • first/second, split/***, and fanout/&&& are all pretty uncommon in the world of Node streams, but they're all pretty easily definable. Usually, mutable state and raw "data"/"end"/"error" events are handled instead in the cases where those would normally be used.

ArrowChoice:

ArrowApply:

  • app is definable in terms of through2, although it's not quite trivial due to the need to flatten.

ArrowLoop:

  • loop is usually just state + end.pipe(start), but it could be implemented in terms of a raw Duplex stream.

(Also, in my experience, transforms are easier to use and compose than classic unidirectional streams.)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wenkanglin picture wenkanglin  路  4Comments

justin-calleja picture justin-calleja  路  7Comments

leeoniya picture leeoniya  路  6Comments

davidchambers picture davidchambers  路  9Comments

ulfryk picture ulfryk  路  4Comments