Roman Cheplyaka

Generic uncurry

Published on January 29, 2013

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 = unc1

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.