Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I define a Haskell Arrow instance in terms of arr and *** / &&&

Tags:

haskell

arrows

I'm still getting to grips with defining and using Arrows in Haskell. While defining new arrows, it is much easier for me to think in terms of *** or &&& rather than first and second, as most of the time I want special processing for when two arrows are combined.

However the Arrow class does not allow defining the arrow in terms of arr and *** or &&&, and requires a definition of first to be included. This means that I am forced to write code like the following -

instance Arrow X where
  arr f = ...
  f (***) g = ...
  first f = f *** arr id

It seems to me that there would have been no harm in including a default definition of 'first' as well in the Control.Arrow module. That would have allowed us to choose between defining either first or ***.

Is there a good reason why a default definition of first was not included in the Arrow class? The only reason that I can think of is that the user may leave out the definition of first and *** both and then you would have circular definitions, but is that the only reason?

like image 354
Anupam Jain Avatar asked Jun 02 '11 18:06

Anupam Jain


People also ask

What does -> mean in Haskell?

(->) is often called the "function arrow" or "function type constructor", and while it does have some special syntax, there's not that much special about it. It's essentially an infix type operator. Give it two types, and it gives you the type of functions between those types.

What does the arrow do in Haskell?

The Arrow (either (->) or MyArr ) is an abstraction of a computation. For a function b -> c , b is the input and c is the output. For a MyArr b c , b is the input and c is the output.

What does left arrow mean in Haskell?

The left arrow gets used in do notation as something similar to variable binding, in list comprehensions for the same (I'm assuming they are the same, as list comprehensions look like condensed do blocks), and in pattern guards, which have the form (p <- e). All of those constructs bind a new variable.


2 Answers

Data.Monoid has a similar missing alternative minimal-complete-definition: mconcat. Like with Arrow, it is missing this alternative out of performance reasons. mappend a b = mconcat[a,b] is inefficient because of the pattern-matching in mconcat.

With Arrows, the inefficiency is less obvious but more brutal: Think about not being able to optimize arr id away:

Using CleisliArrow as example Arrow, it is like this:

first f (x,y) = do
    x' <- f x
    return (x',y)

And it would be:

first f (x,y) = do
    x' <- f x
    y' <- arr id y
    return (x',y')

The arr function cannot pattern-match the id function and has therefore to introduce additional overhead to wrap that function.

like image 187
comonad Avatar answered Sep 28 '22 07:09

comonad


I actually believe it's the circularity that stopped someone from writing the default methods. But as @camccann pointed out, this should stop anyone. Suggest a change!

like image 45
augustss Avatar answered Sep 28 '22 07:09

augustss