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
default unc1 :: (b -> a) -> (b -> a)
unc1 = id
type Uncurried a
type Uncurried a = a
unc :: a -> Uncurried a
default unc :: a -> a
unc = id
instance Uncurry y => Uncurry (x -> y) where
type Uncurried1 z (x -> y) = Uncurried1 (z, x) y
unc1 = unc1 . uncurry
type Uncurried (x -> y) = Uncurried1 x y
unc = unc1You 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 DoubleUncurry1 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.