-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactoring the Arrow hierarchy #8
Comments
Examples where I've needed transformers and arrows would not do:
As a general note to the people participating: following discussions in the GHC repo, I can see that a lot of this work is rooted in mathematical background I do not have or just don't dominate with enough proficiency. Although it may slow everyone else down (and I really hope I'm not a drag), I'd appreciate and think I could be a better asset in this line of work if I understood the discussion well. A lot of this responsibility rests on me (I have to study and read), but I'd appreciate if the concepts were explained for an audience that is not necessarily well-versed in category theory, and potentially, if we have online meetings to discuss some technical details. One more thing: I've always found the need for |
Not at all, in fact I think it’s crucial that we take things slowly and make sure everyone is on the same page in terms of both theory and practical implementation within GHC—moving slowly and deliberately, sharing many examples, is how we achieve that. As a happy side effect, we might even get some high-quality documentation out of it, something that’s sadly lacking about arrows generally.
Imagine we could magically implement whatever notation you wanted—what would it look like? The syntax seems to need a few parts:
But there are a few possible avenues for improvement:
The former feels tractable to me, but probably has some subtleties that @lexi-lambda would understand better than me, being in the thick of it right now. The latter seems like a benign surface-level change, but I get the sense it could have a lot of follow-on effects, and it’s not clear to me where to stop if we give that mouse that cookie. For example, to disambiguate lambda abstractions from arrow abstractions, we might have to figure out a consistent semantics of |
I’ve been thinking about how to arrive at a robust implementation of desugaring using something like the above class hierarchy. I think the general structure of
Here’s a worked example borrowed from the GHC user’s guide:
I find the dataflow of such programs easier to visualise with a notation I devised for concatenative programs, a “dataflow table”, in which:
(Unfortunately, this example requires odd overlapping colspans that HTML table renderers don’t support very well, so ASCII art it is…) Below I’ve also included some additional info:
Associativity is left implicit here (i.e., the input and output of
Removing the table and stack state, leaving just the combinators, that becomes:
Which can be simplified by using
Unfortunately, I believe the path leading to the dead-end
Critically, through this entire process, we never had to reason “under” All of the “shuffling” operations that previously required Next up, I’m still trying to work out how to desugar the more complex components of The first order of business is desugaring The second order (ha) is the desugaring of |
I think the existing https://hackage.haskell.org/package/profunctor and https://hackage.haskell.org/package/category classes are all that we need. @ivanperez-keera they way I think about it is anyone can, and should, cargo cult the cateogry theory text books on this stuff. Expertise is required to justify deviating from those definitions. The problems with arrows today, essentially, is that they deviate without a mathematically-rigorous argument why. (e.g. a lot of the command stack stuff I don't think deserves the sugar and ad-hoc rules it already has, and would be better served with just a |
@Ericson2314: Could you expand a bit onn how those classes would provide the features we need? In particular, it seems like I think some finer-grained structure is warranted because Each of the classes I proposed has a direct categorical analogue, while the existing extension either doesn’t call this out explicitly, or deviates from it. We should certainly depend on existing classes like If there’s a simpler way of handling command abstractions, I’m all for it, because the whole feature seems extremely delicate, and in fact I’m still learning how it all comes together—I figured I would just sit with it for a while and see if my right brain can find a nice pattern. |
As @evincarofautumn notes, arr f = fmap f id
dimap f g h = arr g . h . arr f It would definitely be nice to get |
For what it’s worth, here’s the definition we use: class Arrow p => ArrowReader r p | p -> r where
askA :: p a r
localA :: p a b -> p (a, r) b
newtype ReaderA r p a b = ReaderA { runReaderA :: p (a, r) b }
instance Arrow p => ArrowReader r (ReaderA r p) where
askA = ReaderA (arr snd)
localA (ReaderA f) = ReaderA (f . arr fst) It’s useful, though I can’t say I know what @Ericson2314 has in mind when he says
as you generally want to use control operator syntax when calling More generally, when you write
I am not sure what you are alluding to. I suppose it’s true that all the environment/stack-threading business is bespoke, but that’s just part of arrow notation, not arrows proper. |
Yes, as @lexi-lambda says the Cateogry classes avoid
Sorry, I was conflating the arrow class and arrow notation. (The arrow class as a grab-bag of axioms is also suspect, but that's already been discussed.) I still think Arrow/Arrow notation today is an overly-complex dead end and a new arrows-like thing should start from scratch (though I do think your proposal improves arrows as they exist today).
So your tutorial (and that's the first I really know of these banana brackets!) It seems like their original use-case was "smuggling" data into the arguments of functions that transform arrows since closures are not available. But one can just use Perhaps there are other use-cases developed by those who have actually have used banana brackets I am ignorant of. |
Yes, and one can also do it by passing more arguments to functions. Why have abstractions at all? The usefulness of arrow notation is that it provides a pointful, lexically-scoped embedded language that compiles to the arrow operators, the latter of which are user-specified. You can get lots of useful behaviors by swapping the arrow operators with different ones, without completely giving up on a familiar, intelligible programming model. But arrows are not always cartesian closed, so arrow notation is distressingly first-order. The illusion breaks down as soon as you need to apply a transformation to an arrow itself: if you exit the notation and re-enter it, none of the “bindings” are in scope. So banana brackets are a syntax to specifically accommodate this “quick exiting and re-entering” so the arrow notation compiler knows to treat the sub-computation as an extension of the enclosing computation. You can’t really get around this. You can argue the merits of the particular details of the design that was eventually decided upon, but any notation for arrows (or arrow-like things) will face this challenge. You can’t solve this inadequacy of the embedded language with any amount of cleverness in the host language because you aren’t in the host language. Banana brackets are the escape hatch back into the host language. |
do a <- foo -< ()
xs <- bar -< ()
runReaderA (mapA (ReaderA $ proc (a, x) -> do
b <- baz -< x
qux -< (a, b))) -< (xs, a) Yeah, it's not the same I think the arrow notation's stack is not unlike a stack of reader effects. I bet somebody could make a |
I’ve been a bit stuck on the desugaring of
A
Needs to desugar to something that selects one of a series of continuations to apply:
(Note that this takes the continuations as arguments so that it doesn’t require cartesian closure, which is needlessly strong for this.) Maybe the data type itself could be desugared into sums and products (à la
(Of course, I’m sure there are subtleties I’m completely missing.) I’m also assuming that each continuation takes all of the fields of the corresponding constructor, and then explicitly drops those that are unused, rather than having the Also, this only handles matching each constructor once, with no guards; it’s possible to desugar more complicated |
This is just to get a conversation going—I plan to add examples and detail as I have time.
I often run into types that are almost, but not quite, an
Arrow
—but for the fact that they have no implementation ofarr
usually, but also sometimes because they lack notions of copying, dropping, or swapping (that is, contraction, weakening, and exchange), or most recently because Arrow isn’t polykinded (as a result of containingarr
). Sometimes I can work around this with a free arrow over a bifunctor—essentially building a graph with the free arrow where the nodes are annotated with my effects:But often I can’t get away with this formulation. That’s frustrating, because I really like
proc
notation and the structural guarantees I can get from it. So I’d like to start a discussion about how we want to refactor the class hierarchy going forward, with an eye to increasing flexibility and preserving backward compatibility as much as possible, with a suitable deprecation plan—e.g. adding a language extension to desugarproc
notation in terms of the new hierarchy.I think a good starting point is the monoidal category formulation that @cgibbard brought up in the Constraint-Based Arrow Notation proposal (rephrased slightly):
In particular, figuring out how to divide that up into a hierarchy that’s within a surmountable distance from the current state of affairs. For example, without abstracting over the tensor product and sum types, and just using
(,)
(as above) andEither
:If anyone has examples of cases that are not
Arrow
s but whereproc
notation would be really useful, please share them here, as that’s exactly the underserved market I’m hoping to address—and with luck, popularise arrow notation more broadly by making it more useful.The text was updated successfully, but these errors were encountered: