Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make sense of the Haskell type signature for Control.Arrow's '&&&' operator

Tags:

syntax

haskell

I'm trying to wrap my head around how Haskell's Control.Arrow's &&& works, but fear I'm on the verge of losing my way.

Specifically, I'm confused (as a beginner) by how to makes sense of its behavior from the type signature

(&&&) :: a b c -> a b c' -> a b (c, c')

in

import Control.Arrow
(negate &&& (+5)) <$> [1,2,3]

or even just

(negate &&& (+5)) 5

for example, the first argument is "missing" b and c, while the second is missing just c', and the result looks to me like (c, c'), not a b (c, c').

Can someone walk me through how &&& works, in the context of its type?

like image 884
orome Avatar asked Sep 13 '15 13:09

orome


2 Answers

I always think of &&& as a split and apply operation. You've got an Arrow value, and you're going to apply two functions (sorry, arrows, but it works with functions and makes explanations easier) to it, and keep both results, thus splitting the stream.

Dead simple example:

λ> (succ &&& pred) 42
(43,41)

Walking the type there, we've got

succ &&& pred :: Arrow a, Enum b => a b (b,b)

A more complex example where it's not all b:

show &&& (== 42) :: Arrow a, Show b, Eq b, Num b => a b (String,Bool)

So in plain english: &&& takes two functions, and combines them into a single function that takes it input, applies both functions to it, and returns the result pair.

But it's defined on arrows, not functions. Yet it works exactly the same: it takes two arrows, and combines them into a single arrow that takes its input, applies both arrows to it, and returns the result pair.

arrowOne :: Arrow a => a b c
arrowTwo :: Arrow a => a b c'
arrowOne &&& arrowTwo :: Arrow a => a b (c,c')

Addendum: part of what seems to confuse you is whether or not the a type still appears in the type signatures. The rule of thumb here is that it works the same as for when you see the -> in function types: it shows as long as it's not applied.

I recall reading some Arrow literature that wrote arrows as b ~> c (note the tilde not dash) instead of a b c, to make the parallel with functions more apparent.

like image 110
JB. Avatar answered Oct 02 '22 12:10

JB.


The signature says,

(&&&) :: Arrow a => a b c -> a b c' -> a b (c, c')

and (->) is an instance of Arrow type class. So, rewriting the signature specialized for (->) type:

(&&&) :: ((->) b c) -> ((->) b c') -> ((->) b (c, c'))

in, infix form it will look like:

(b -> c) -> (b -> c') -> (b -> (c, c'))

which simply means

(&&&) :: (b -> c)   -- given a function from type `b` to type `c`
      -> (b -> c')  -- and another function from type `b` to type `c'`
      -> (b -> (c, c')) -- returns a function which combines the result of
                        -- first and second function into a tuple

a simple replication would be:

(&:&) :: ((->) b c) -> ((->) b c') -> ((->) b (c, c'))
f &:& g = \x -> (f x, g x)

which would work the same way:

\> (negate &:& (+5)) <$> [1, 2, 3]
[(-1,6),(-2,7),(-3,8)]
like image 41
behzad.nouri Avatar answered Oct 02 '22 14:10

behzad.nouri