Generic uncurry
Published on
For the next version of SmallCheck I needed a generic uncurry function in Haskell. To my surprise, I couldn’t google anything simple and ready to use.
So here it is, for future reference.
{-# LANGUAGE TypeFamilies, DefaultSignatures, UndecidableInstances #-}
class Uncurry a where
type Uncurried1 b a
type Uncurried1 b a = b -> a
unc1 :: (b -> a) -> Uncurried1 b a
unc1 :: (b -> a) -> (b -> a)
default= id
unc1
type Uncurried a
type Uncurried a = a
unc :: a -> Uncurried a
unc :: a -> a
default= id
unc
instance Uncurry y => Uncurry (x -> y) where
type Uncurried1 z (x -> y) = Uncurried1 (z, x) y
= unc1 . uncurry
unc1
type Uncurried (x -> y) = Uncurried1 x y
= unc1 unc
You also need to make the final type an instance of Uncurry
. For
example, if you need to uncurry Int -> Bool -> Double
,
Double
has to be an instance of Uncurry
. But
thanks to default
method signatures and associated
type synonym defaults, it is as easy as
instance Uncurry Double
Uncurry1
and
unc1
are implementation details. The useful parts are Uncurry
, which
converts the type to its uncurried version, and unc
, which uncurries the function
itself.
After loading the code in ghci, you can see that it works by:
*Uncurry> :kind! Uncurried (Int -> Bool -> Char -> Double)
Uncurried (Int -> Bool -> Char -> Double) :: *
= ((Int, Bool), Char) -> Double
*Uncurry> :t unc $ \x y z -> (x + y * z :: Double)
unc $ \x y z -> (x + y * z :: Double)
:: ((Double, Double), Double) -> Double
*Uncurry> unc (\x y z -> (x + y * z :: Double)) ((1,2),3)
7.0
I tested this with GHC 7.6, but I think it should work with GHC 7.4 as well.