[Haskell-cafe] Currying function using values from array

Ryan Ingram ryani.spam at gmail.com
Fri Aug 8 17:08:22 EDT 2008


2008/8/7 Sukit Tretriluxana <tretriluxana.s at gmail.com>:
> How in Haskell that I can create a function that curries any other function,
> which receives multiple parameters, by using a the input from a list (same
> data type) or a tuple (mixed data type) such that it either returns another
> closure (if not all parameters are curried) or the final value of the
> computation (when all parameters are known)?

Here's a solution that uses tuples only (no lists, they are somewhat
more difficult to typecheck correctly), along with several tests.

The payoff is functions with signatures like this:

-- tncurry :: (a -> b -> c -> r) -> (a,b,c) -> r
-- trcurry :: ((a,b,c) -> r) -> a -> b -> c -> r

-- ncurry :: (a -> b -> c -> r) -> (a, (b, (c, ()))) -> r
-- rcurry :: ((a, (b, (c, ()))) -> r) -> a -> b -> c -> r

but that work on any number of arguments.
(disclaimer: I've only implemented tncurry & trcurry up to 4-tuples;
additional tuple sizes require 3 lines of boilerplate code each).

   -- ryan

{-# OPTIONS_GHC -fglasgow-exts -fallow-undecidable-instances
                -fno-monomorphism-restriction
  #-}
module Curry where

-- no-monomorphism-restriction is just so we don't
-- have to put dummy arguments on the tests.

class IsFunction f a r | f -> a r, a r -> f where
   apply :: f -> a -> r

instance IsFunction (a -> b) a b where
   apply = ($)

class NCurry f a r | f a -> r where
   ncurry :: f -> a -> r

instance NCurry r () r where
   ncurry x _ = x

instance (IsFunction f' b f, NCurry f a r) => NCurry f' (b,a) r where
   ncurry f (b,a) = ncurry (apply f b) a

class RCurry t x r | t x -> r where
   rcurry :: (t -> x) -> r

instance RCurry () r r where
   rcurry f = f ()

instance RCurry t x r => RCurry (a,t) x (a -> r) where
   rcurry f x = rcurry (\t -> f (x,t))

-- some tests

test1 = ncurry (+) (5, (10, ()))
-- test1 :: Num f => f
-- test1 => 15

plus :: Num a => (a, (a, ())) -> a
plus = ncurry (+)

test2 = rcurry plus
-- test2 :: Num a => a -> a -> a

test_broken = rcurry (ncurry (+))
-- test_broken :: (Num a, NCurry (a -> a -> a) t r, RCurry t r r1) => r1
{-
test_broken 5 10 =>
    No instances for (NCurry (a -> a -> a) t r,
                      RCurry t r (t1 -> t2 -> t3))

This is an instance of the "read.show" problem; ncurry (+) has many types:
    ncurry (+) :: Num a => () -> a -> a -> a
    ncurry (+) :: Num a => (a,()) -> a -> a
    ncurry (+) :: Num a => (a,(a,())) -> a

Even though rcurry would work on any of these, it's ambiguous which one
to choose, so the type inferencer gives up.
-}

test3 = test2 5 10
-- test3 :: Num t => t
-- test3 => 15

-- stupid constant function
dumb a b c d = c

test4 = ncurry dumb ("wrong", (5, ("correct", ([1..50], ()))))
-- test4 :: [Char]
-- test4 => "correct"

dumb2 (a, (b, (c, (d, ())))) = c

test5 = rcurry dumb2
-- test5 :: t -> t1 -> r -> t2 -> r

test6 = rcurry dumb2 "wrong" 5 "correct" [1..50]
-- test6 :: [Char]
-- test6 => "correct"

-- We can also use "real" tuples instead of tuple-lists, with
-- some boilerplate...

class TupleChange n t | n -> t, t -> n where
   toTuple :: n -> t
   fromTuple :: t -> n

-- Haskell doesn't have a "1-tuple", so make it ourselves
data Tuple1 x = Tuple1 x deriving (Eq, Show, Ord)

instance TupleChange () () where
   toTuple = id
   fromTuple = id

instance TupleChange (a, ()) (Tuple1 a) where
   toTuple (a, ()) = Tuple1 a
   fromTuple (Tuple1 a) = (a, ())

instance TupleChange (a, (b, ())) (a, b) where
   toTuple (a, (b, ())) = (a,b)
   fromTuple (a,b) = (a, (b, ()))

instance TupleChange (a, (b, (c, ()))) (a,b,c) where
   toTuple (a, (b, (c, ()))) = (a,b,c)
   fromTuple (a,b,c) = (a, (b, (c, ())))

instance TupleChange (a, (b, (c, (d, ())))) (a,b,c,d) where
   toTuple (a, (b, (c, (d, ())))) = (a,b,c,d)
   fromTuple (a,b,c,d) = (a, (b, (c, (d, ()))))

tncurry f = ncurry f . fromTuple
trcurry f = rcurry (f . toTuple)

-- Tests of tncurry/trcurry & show closures

test7 = tncurry (+) (Tuple1 5)
-- test7 :: Num a => a -> a
-- test7 10 => 15

dumb3 (a,b,c,d) = c

test8 = trcurry dumb3
-- test8 :: t -> t1 -> r -> t2 -> r

-- test9 creates a closure waiting for more
-- arguments...
test9 = tncurry dumb ("foo", "bar", "hat")
-- test9 :: t -> [Char]
-- test9 "baz" => "hat"

-- although you do have to use tncurry again
-- with each call if you want to keep applying
-- it with tuples
test10 = tncurry (tncurry dumb ("foo", "bar", "hat")) (Tuple1 "baz")
-- test10 :: [Char]
-- test10 => "hat"


More information about the Haskell-Cafe mailing list