[Haskell-cafe] arrow notation

Ertugrul Söylemez es at ertes.de
Mon Feb 11 00:27:15 CET 2013


Ross Paterson <ross at soi.city.ac.uk> wrote:

> I'd like to hear from anyone who's using arrow notation as supported
> by GHC, because I'm planning a couple of changes to parts of it.

I'm making heavy use of arrow notation, so I'd like to propose a set of
small improvements, not only syntactical.


## Ignored input values

Many computations ignore their input value.  You can recognize them by
their type:  If the input type is fully polymorphic and the output type
is unrelated, the computation cannot use its input value.  In that case
it would make sense to just pass whatever is the cheapest thing you
could pass without requiring me to spell it out:

    comp1 :: Arr a Int
    comp2 :: Arr a Double

Before:

    proc x1 -> do
        x2 <- comp1 -< x1
        x3 <- comp2 -< x2
        id -< (x2, x3)

After:

    proc _ -> do
        x1 <- comp1
        x2 <- comp2
        id -< (x1, x2)

Then the arrow notation compiler could just pass whatever is most
convenient at that spot.  In this case it would just compose with
'&&&':

    comp1 &&& comp2


## returnA

We don't need it anymore, and it has quite a stupid definition.  Get rid
of it in favor of 'id'.


## Operators

I often need to mix regular arguments with computation arguments in
banana notation:

    let f c = f' x y c z
    (| f (comp -< v) |)

Since parentheses are required for computation arguments you could relax
the syntax to allow regular arguments in simple cases as well:

    (| f' x y (comp -< v) z |)


## PreArrow

All sensible arrows form a family of functors:

    instance (Arrow a) => Functor (a b) where
        fmap f = (arr f .)

But they do more:  Every arrow is a profunctor as defined in the
'profunctors' package:

    instance (Arrow a) => Profunctor a where
        lmap f = (. arr f)
        rmap = fmap

That's just what you called PreArrow, so there is no need to reinvent
the wheel.  Get Profunctor into base.


## Applicative

One of the main bottlenecks of arrows is the heavy tuple handling, but
most (if not all) arrows form a family of applicative functors.  I
noticed a huge speedup by moving from arrow style to applicative style
where possible:

    liftA2 (+) (lmap f c) (fmap g d)

is often much faster than:

    arr (uncurry (+)) . (c . arr f &&& arr g . d)

Besides being more readable it sometimes improved the performance of my
code by an order of magnitude.  So perhaps check to see if the category
forms an applicative functor.  If it does, you can get along without
Arrow entirely.

In fact I propose to generalize all the Arrow* classes to Category*
classes.  The ultimate goal is to get rid of arrows.  We don't really
need them anymore.  I'd rather like to see SHE's idiom brackets in
Haskell and use a more lightweight syntax for stuff like ArrowChoice and
ArrowLoop (or CategoryChoice and CategoryLoop), although I don't yet
know what it would look like.


Greets,
Ertugrul

-- 
Not to be or to be and (not to be or to be and (not to be or to be and
(not to be or to be and ... that is the list monad.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20130211/e650d130/attachment.pgp>


More information about the Haskell-Cafe mailing list