[Haskell-beginners] arrows and 2-tuples

Michael Orlitzky michael at orlitzky.com
Tue Feb 16 15:22:15 UTC 2016


On 02/16/2016 03:06 AM, Dennis Raddle wrote:
> I am a Haskell beginner. I have used arrows occasionally to move
> 2-tuples around, but don't understand more than that. 
> 
> I'm interested in know what's the connection between arrows and
> 2-tuples. I don't really understand most of the Control.Arrow docs, but
> it seems that a lot of stuff about arrows doesn't mention 2-tuples. Yet
> the operators (***), (&&&), first, and second seem to be common. Is
> there some way to explain the link?
> 
> Also, the main instance of Arrow seems to be (->). There is also
> something about Kleisli monads, but I don't know what those are. Is
> there an another big use case for arrows besides (->)? Don't worry about
> explaining it all, just a quick mention would be fine and I can
> investigate it myself.
> 

An arrow is basically a function and it doesn't hurt to think of them
that way. In mathematics, "function" means something very specific, so
they needed a new name (arrow) for a thing that's a lot like a function
but isn't one.

You can construct some weird situations where arrows aren't very much
like mathematical functions at all, but it still doesn't hurt to think
of them that way. Mentally I prefer to embiggen my concept of "function"
rather than give it up entirely in the new setting. You're probably
already used to this: the random() "function" is not a mathematical
function, but we all call it one.

There's no specific connection between arrows and two-tuples. It's
extremely common to use functions on two-tuples, and every function is
an arrow, so the fact that all of those useful (***) and (&&&) live in
Control.Arrow is a bit of a premature abstraction. Frequently you'll
need to take a function f and a pair (x,y) and want to compute the pair
(f x, f y). There should really be a combinator for that! But when you
write one, it turns out that it works just as well for arrows. Since all
functions are arrows, they just used the more general type.

The Kleisli arrow/monad thing isn't wrong, it's just useless without an
example. Kleisli arrows are just plain old arrows (think: functions) in
a monad. Suppose I want to read a file and then print its contents to
the screen. How would I do that? In pseudo-code, it's something like,

  -- Read a file and print its contents to the screen
  (putStr . readFile) "/path/to/file.txt"

But here, "print" and "readFile" aren't mathematical functions, so we
can't compose them! The "." operator only works on functions. It would
be great if there were something that was a lot like a function but
could be composed in this manner...

  -- Read a file and print its contents to the screen
  ghci> import Control.Monad ( (<=<) )
  ghci> (putStr <=< readFile) "hw.txt"
  Hello, world!

Another useful example is when you want to "automatically" concatenate a
list of results. Let's write a stupid function that duplicates its
argument in a list:

  ghci> let twice x = [x, x]
  ghci> twice 3
  [3,3]

So far so good. But now I want four things. Can I compose "twice" with
itself?

  ghci> (twice . twice) 3
  [[3,3],[3,3]]
  ghci> :t (twice . twice)
  (twice . twice) :: t -> [[t]]

Crap, that's giving me a list of lists. I just want one list! By
thinking of "twice" as a multi-valued function (a special type of
arrow), I can get what I want:

  ghci> (twice <=< twice) 3
  [3,3,3,3]

That trick is using the Monad instance for lists, but composition in
monads is done with arrows (not mathematical functions). It's put to
good use in e.g. HXT where you can say "give me all children of <p>
elements that live in a <div> that live in a <body>" and you only want
one list back. Without that funny arrow composition, you'd be stuck with
lists of lists of lists of lists...



More information about the Beginners mailing list