6

I'm trying to do find a way to do something like this:

(head, last) `someFunction` [1, 2, 3]

to produce the tuple (1, 3) as output.

It seems similar in theory to an applicative functor, but a little backwards. I'm guessing there's a similar function that does this (or some way to make one), but I can't seem to find it/figure it out.

I tried defining a function like this:

fmap' :: ((a -> b), (a -> b)) -> [a] -> (b, b)
fmap' (f1, f2) xs = (f1 xs, f2 xs)

but GHC won't actually compile this.

Any help would be great; thanks!

Edit (a whole year later!):

My fmap' wouldn't compile because the type signature was wrong. Obviously there are better ways to do what I was doing, but the type of my fmap' should instead be:

fmap' :: ((a -> b), (a -> b)) -> a -> (b, b)

In that case, it compiles and runs just fine.

Benjamin Kovach
  • 3,190
  • 1
  • 24
  • 38
  • 4
    It turns out that `someFunction` is `uncurry (liftM2 (,))`, according to `pointfree`. Of course, you could leave out the `uncurry`, and just use the rest: `liftM2 (,) head last [1, 2, 3]`. – Dietrich Epp Jul 27 '12 at 19:37

4 Answers4

15

I think you can do this with arrows.

head &&& last $ [1,2,3]

will return (1,3).

Jeff Burka
  • 2,571
  • 1
  • 17
  • 30
  • I'm not familiar with arrows yet, but I've been hoping to learn about and use them more. This is nice and readable. Thanks! – Benjamin Kovach Jul 27 '12 at 19:46
  • Does anyone know how to generalize this to more than two functions? For example, `head &&& last &&& tail` would give you `(1,(3,[2,3]))`, which isn't ideal. Is there a simple way to flatten that to one tuple? – Jeff Burka Jul 27 '12 at 19:55
  • 1
    @JeffreyBurka I also experienced this. I think that a `tuple`-flattening function would violate Haskell's strict type system, since `n-tuples` are `n-tuples` and nothing more. The type signature for that function wouldn't make sense (you'd have to output a dynamically sized `tuple`, which isn't possible.) – Benjamin Kovach Jul 27 '12 at 20:01
  • Yeah there definitely isn't a `concat` for tuples, but I was thinking there might be a simple way to express the arrow as one that returns a 3-tuple instead of a nested tuple. If that's not possible or easy then I would just use Daniel Wagner's applicative solution. – Jeff Burka Jul 27 '12 at 20:27
  • Actually I think that might be simple enough using [arrow do notation](http://www.haskell.org/arrows/syntax.html). I don't have GHC right now and Codepad uses Hugs unfortunately, so I can't test at the moment. – Jeff Burka Jul 27 '12 at 20:49
9

It seems similar in theory to an applicative functor, but a little backwards.

Actually, it's a boring old forwards applicative functor; specifically, the reader ((->) r).

Prelude Control.Applicative> liftA2 (,) head last [1,2,3]
(1,3)

Or, if you're into that kind of thing:

Prelude Control.Applicative> let sequenceA [] = pure []; sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
Prelude Control.Applicative> [head, last] `sequenceA` [1,2,3]
[1,3]
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
4

The type of fmap' is wrong. It should be

fmap' :: ([a] -> b, [a] -> b) -> [a] -> (b, b)

or, it can be more generalized

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

It doesn't really resemble fmap :: (a -> b) -> f a -> f b.

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • Ah! I actually tried a couple of things that didn't work, but I just realized I was calling `fmap` instead of `fmap'`. I was wondering why it wouldn't work properly. Thank you for the quick response! – Benjamin Kovach Jul 27 '12 at 19:23
3

Something to try in this situation is to omit the type signature and check what GHC infers.

Doing so and asking GHCi :t fmap' yields the signature

fmap' :: (t2 -> t, t2 -> t1) -> t2 -> (t, t1)

which is identical to KennyTM's generalized version, and will give you the behaviour you're looking for.

jtobin
  • 3,253
  • 3
  • 18
  • 27