简体   繁体   中英

Is there a way to capture a tuple of higher-order functions in Haskell?

I understand that it's impossible to pattern match functions in Haskell, and I fully understand why. However, I have two closely related questions. First, in cases where you'd like to partially apply functions for use later, is there a way of defining and capturing the return if it's a tuple? Or am I wrong, and this is still trying to pattern match functions under my nose?

For example, suppose I'm trying to get the quotient and remainder of a value with various multiples of ten. Then, how would I write something like this?

q, r :: Integral a => a -> a
(q, r) = (12345 `quotRem`)

I realize here, there are separate functions that exist, so I could do this instead:

q, r :: Integral a => a -> a
q = (12345 `quot`)
r = (12345 `rem`)

However, that's a very specific case, and there are unlimited other examples of functions that return tuples that would be nice to generalize. For example, a function that returns the number of evens and odds in a list.

evens, odds :: Integral a => [a] -> Int
(evens, odds) = (length . (filter even), length . (filter odd))

This leads me to my second question. The above works just fine in GHCi.

Prelude> let (evens, odds) = (length . (filter even), length . (filter odd))
Prelude> :t evens
evens :: Integral a => [a] -> Int
Prelude> evens [1..10]
5

What's even more confusing is it even works by "pattern-matching" in the same way that I was playing with (q, r) in the beginning:

Prelude> let evensOdds = (length . (filter even), length . (filter odd))
Prelude> :t evensOdds
evensOdds :: (Integral a1, Integral a) => ([a1] -> Int, [a] -> Int)
Prelude> let (ev,od) = evensOdds
Prelude> :t ev
ev :: Integral a1 => [a1] -> Int
Prelude> ev [1..10]
5

It also works just fine in an actual file loaded into GHCi, even though (evens, odds) doesn't. Why are these two different, and why does the second one work in GHCi at all if it doesn't work normally? Can what's different here be leveraged in some way?

You never pattern matched on a function. You always pattern matched on the pair-constructor (,) . Your (even, odds) example

(evens, odds) = (length . (filter even), length . (filter odd))

just works like

(first, second) = (x, y)

It doesn't matter what type x and y have at that point.


Your (q, r) example doesn't work due to quotRem 's type. Let's recall it and compare it with (q, r) 's type:

quotRem       :: Integral n => n -> n -> (n     , n)
quotRem 12345 :: Integral n =>      n -> (n     , n)
(q, r)        :: Integral n =>           (n -> n, n -> n)

As you can see, the pair (q, r) 'type differs from quotRem 's one. Still, it's possible to write your function:

pairify :: (a -> (b, c)) -> (a -> b, a -> c)
pairify f = (fst . f, snd . f)

(q,r) = pairify (quotRem 12345)

But as you can see we don't gain too much from pairify . By the way, partition from Data.List provides your (even, odds) functionality:

(even, odds) = pairify (partition even)

Look at the type of (12345 `quotRem`) :

Integral a => a -> (a, a)

It's a single function that returns a tuple. If you want to make this into a tuple of functions, you can compose it with fst and snd :

(q, r) = (fst . f, snd . f)
  where f = (12345 `quotRem`)

If you want to do this in a point-free way, one way is to use the &&& combinator from Control.Arrow . Its fully general type is:

Arrow a => a b c -> a b d -> a b (c, d)

Specialised to the -> arrow, that's:

(b -> c) -> (b -> d) -> b -> (c, d)

So it takes two functions, each taking a value of type b , and returns both their results (of types c and d ) in a tuple. So here you can do something like this:

split = (fst .) &&& (snd .)
(q, r) = split (12345 `quotRem`)

Whereas if you look at the type of (length . filter even, length . filter odd) , it's a tuple already,

(Integral a, Integral b) => ([a] -> Int, [b] -> Int)

Which is why of course you can destructure this tuple to bind evens and odds .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM