Fundeps: n-ary functions (Multi.hs)
{-# OPTIONS -fglasgow-exts #-}
{-# OPTIONS -fallow-undecidable-instances #-}
{-# GHC_OPTIONS -fcontext-stack400 #-}
module Multi where
import Prelude hiding (sum)
{-
Suppose we want to define n-ary sum.
We might define a family of functions:
Sum[0] = Int
Sum[n+1] = Int -> Sum[n]
sum[n] :: Sum[n]
sum[n] = sum'[n] 0 where
sum'[n] :: Integer -> Sum[n]
sum'[0] acc = acc
sum'[n+1] acc = \x -> sum[n] (x + acc)
What are their types?
Sum is a family of types -- even more, it's a function from
nat -> types in this family:
-}
-- We need Peano numerals:
data Z = Z
data S n = S n
class Sum nat x | nat -> x where
sumacc :: nat -> Integer -> x
instance Sum Z Integer where
sumacc Z acc = acc
instance Sum nat x => Sum (S nat) (Integer -> x) where
sumacc (S n) acc = \x -> sumacc n (acc + x)
sum :: Sum nat x => nat -> x
sum n = sumacc n 0
-- we don't even need the values:
class Sum' nat x | nat -> x where
sumacc' :: nat -> Integer -> x
instance Sum' Z Integer where
sumacc' _ acc = acc
instance Sum' nat x => Sum' (S nat) (Integer -> x) where
sumacc' _ acc = \x -> sumacc' (bot::nat) (acc + x)
sum' (_::t) = sumacc' (bot::t) 0
bot = bot
{-
Prolog:
add(zero, B, B).
add(succ(A), B, C) :- add(A, succ(B), C).
mul(zero, A, zero).
mul(succ(A), B, C) :- mul(A, B, D), add(B, D, C).
These are Horn-clause programs. The class declaration provides
a declaration and "type" for the predicate, and the fundeps are
in/out modes.
-}
class Add a b c | a b -> c where
(/+/) :: a -> b -> c
instance Add Z a a where
Z /+/ n = n
instance Add a b c => Add (S a) b (S c) where
S a /+/ b = S (a /+/ b)
class Mul a b c | a b -> c where
(/*/) :: a -> b -> c
instance Mul Z a Z where
Z /*/ _ = Z
instance (Mul a b ab, Add b ab c) => Mul (S a) b c where
S a /*/ b = b /+/ (a /*/ b)
class Reify x f r | f -> r where
reify :: f -> r -> x -> r
instance Reify Z (r -> r) r where
reify f r _ = r
instance Reify n (r -> r) r => Reify (S n) (r -> r) r where
reify f r _ = f (reify f r (bot::n))
toNum :: (Num a, Reify x (a -> a) a) => x -> a
toNum = reify (+1) 0
class Add' a b c | a b -> c where
(/++/) :: a -> b -> c
(/++/) = bot
instance Add' Z a a where
instance Add' a b c => Add' (S a) b (S c) where
class Mul' a b c | a b -> c where
(/**/) :: a -> b -> c
(/**/) = bot
instance Mul' Z a Z where
instance (Mul' a b ab, Add' b ab c) => Mul' (S a) b c where
class Fact a b | a -> b where
fact :: a -> b
fact = bot
instance Fact Z (S Z) where
instance (Fact a c, Mul' (S a) c b) => Fact (S a) b where
p0 = Z
p1 = S p0
p2 = S p1
p3 = S p2
p4 = S p3
p5 = S p4
p20 = p4 /**/ p5
-- Now I can "conveniently" make a nine-place sum:
sum9' = sum' (S (S (S Z)) /**/ S (S (S Z)))
sum9'' = sum' (S (S (S (S (S (S (S (S (S Z)))))))))
class FoldN nat a r x | nat a r -> x where
foldlN, foldrN :: nat -> (a -> r -> r) -> r -> x
foldrNk :: nat -> (a -> r -> r) -> r -> (r -> r) -> x
foldrN n f z = foldrNk n f z id
instance FoldN Z a r r where
foldlN _ _ r = r
foldrNk _ _ z k = k z
instance FoldN nat a r x => FoldN (S nat) a r (a -> x) where
foldlN _ f z a = foldlN (bot::nat) f (f a z)
foldrNk _ f z k a = foldrNk (bot::nat) f z (\r -> k (f a r))
sumN n = foldlN n (+) 0
prodN n = foldlN n (*) 1
reverseN n = foldlN n (:) []
-- How would we write listN? We need foldrN
listN n = foldrN n (:) []
-- Extensible printf -- cool. Uses accumulator like foldrNk.
-- now zipWithN:
class ZWH nat a x | nat a -> x where
zwh :: nat -> a -> x
instance ZWH Z [a] [a] where
zwh _ lst = lst
instance ZWH n [b] x => ZWH (S n) [a -> b] ([a] -> x) where
zwh _ l1 l2 = zwh (bot::n) (zipWith ($) l1 l2)
zipWithN n f = zwh n (repeat f)
n1 = zipWithN p1 (+2) [1,2,3,4]
n2 = zipWithN p2 (+) [1,2,3,4] [50,40,30,20]
n3 = zipWithN p3 (,,) [True, False, False] ['a', 'c', 'e'] [6,7,8]
n4 = zipWithN p4 (reverseN p4) [1,2,3,4] [5,6,7,8] [9,10,11,12] [13,14,15,16]
-- Do we need N? Not if we're willing to specify write a type:
class ZWA a x | x -> a where
zwa :: a -> x
instance ZWA [a] [a] where
zwa lst = lst
instance ZWA [b] (c x) => ZWA [a -> b] ([a] -> c x) where
zwa l1 l2 = zwa (zipWith ($) l1 l2)
zipWithA f = zwa (repeat f)
a1, a2 :: [Bool]
a3 :: [(Bool, Char, Ordering)]
a4 :: [[Integer]]
a1 = zipWithA not [True, False, False, True]
a2 = zipWithA (&&) [True, True, False] [True, False, False]
a3 = zipWithA (,,) [True, False, False] ['a', 'c', 'e'] [EQ, LT, LT]
a4 = zipWithA (reverseN p4) [1,2,3,4] [5,6,7,8] [9,10,11,12] [13,14,15,16]
a5 = concat (zipWithA (:) "ABCD" ["bcd", "cde", "def", "efg"])