[Haskell-cafe] Composition of n-arity functions
Bas van Dijk
v.dijk.bas at gmail.com
Sat Aug 29 14:03:51 EDT 2009
Hello,
In the levmar binding[1][2] me and my brother are working on, I need a
function composition operator that is overloaded to work on functions
of any arity. Basically its type needs to be something like the
following:
(.*) :: (b -> c) -> NFunction n a b -> NFunction n a c
where 'NFunction n a b' represents the function 'a_0 -> a_1 -> ... -> a_n -> b'
I have written the following implementation:
(my question to this list is below)
-- Begin -----------------------------------------------------------------------
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FlexibleContexts #-}
module ComposeN where
--------------------------------------------------------------------------------
-- Type-level naturals (from an idea by Ryan Ingram)
--------------------------------------------------------------------------------
data Z = Z
newtype S n = S n
type N0 = Z
type N1 = S N0
type N2 = S N1
type N3 = S N2
class Nat n where
caseNat :: forall r.
n
-> (n ~ Z => r)
-> (forall p. (n ~ S p, Nat p) => p -> r)
-> r
instance Nat Z where
caseNat _ z _ = z
instance Nat n => Nat (S n) where
caseNat (S n) _ s = s n
induction :: forall p n. Nat n
=> n
-> p Z
-> (forall x. Nat x => p x -> p (S x))
-> p n
induction n z s = caseNat n isZ isS
where
isZ :: n ~ Z => p n
isZ = z
isS :: forall x. (n ~ S x, Nat x) => x -> p n
isS x = s (induction x z s)
newtype Witness x = Witness { unWitness :: x }
witnessNat :: forall n. Nat n => n
witnessNat = theWitness
where
theWitness = unWitness $ induction (undefined `asTypeOf` theWitness)
(Witness Z)
(Witness . S . unWitness)
--------------------------------------------------------------------------------
-- N-arity functions
--------------------------------------------------------------------------------
-- | A @NFunction n a b@ is a function which takes @n@ arguments of
-- type @a@ and returns a @b at .
-- For example: NFunction (S (S (S Z))) a b ~ (a -> a -> a -> b)
type family NFunction n a b :: *
type instance NFunction Z a b = b
type instance NFunction (S n) a b = a -> NFunction n a b
-- | @f .* g@ composes @f@ with the /n/-arity function @g at .
(.*) :: forall n a b c. (ComposeN n) => (b -> c) -> NFunction n a b ->
NFunction n a c
(.*) = compose (witnessNat :: n) (undefined :: a)
infixr 9 .* -- same as .
class Nat n => ComposeN n where
compose :: forall a b c. n -> a ->
(b -> c) -> NFunction n a b -> NFunction n a c
-- Note that the 'n' and 'a' arguments to 'compose' are needed so that the type
-- checker has enough information to select the right 'compose' instance.
instance ComposeN Z where
compose Z _ = ($)
instance ComposeN n => ComposeN (S n) where
compose (S n) (_ :: a) f g = compose n (undefined :: a) f . g
--------------------------------------------------------------------------------
-- Test
--------------------------------------------------------------------------------
foo :: NFunction N3 Integer Integer
foo x y z = x + y + z
bar :: Integer -> Integer
bar k = k - 1
test1 = compose (witnessNat :: N3)
(undefined :: Integer)
bar foo 1 2 3
test2 = (bar .* foo) 1 2 3
-- The End ---------------------------------------------------------------------
The problem is test1 type checks and evaluates to 5 as expected but
test2 gives the following type error:
Couldn't match expected type `NFunction n a Integer'
against inferred type `Integer -> Integer -> Integer -> Integer'
In the second argument of `(.*)', namely `foo'
However if I ask ghci to infer the type of (bar .* foo) I get:
*ComposeN>:t (bar .* foo)
(bar .* foo)
:: (Integer -> Integer -> Integer -> Integer
~
NFunction n a Integer,
ComposeN n) =>
NFunction n a Integer
Here we see that the context contains the type equality:
(Integer -> Integer -> Integer -> Integer ~ NFunction n a Integer
So why is ghci unable to match the expected type `NFunction n a Integer'
against the inferred type `Integer -> Integer -> Integer -> Integer'
while the context contains just this equality?
regards,
Bas
[1] http://code.haskell.org/~basvandijk/code/bindings-levmar/
[2] http://code.haskell.org/~basvandijk/code/levmar/
More information about the Haskell-Cafe
mailing list