On Tue, Oct 31, 2006 I wrote:
Consider the following sequence of functions
that replace a single element in an n-dimensional
list:

replace0 :: a -> a -> a
replace1 :: Int -> a -> [a] -> [a]
replace2 :: Int -> Int -> a -> [[a]] -> [[a]]

Generalize this using type classes.

Thanks to everyone for the refernces about the
variadic composition operator.

However, that technique only provides a variable
number of arguments at the end of the argument
list (like in C, etc.). The puzzle as stated requires
them at the beginning.

Below is a proposed full solution. Unfortunately,
it compiles neither in Hugs nor in GHC. But I don't
understand why not.

GHC says:

   Functional dependencies conflict between instance declarations:
     instance Replace Zero a a (a -> a -> a)
     instance (...) => Replace (Succ n) a [l] f'

Not true. The type constraints on the second instance
prevent any overlap.

Hugs says:

ERROR "./Replace.hs":63 - Instance is more general than a dependency allows
*** Instance         : Replace (Succ a) b [c] d
*** For class        : Replace a b c d
*** Under dependency : a b -> c d

Not true. The type constraints limit the scope to within the fundeps.

Here is the program:

{-# OPTIONS_GHC -fglasgow-exts -fallow-undecidable-instances #-}

We will need ordinals to count the number of initial function arguments.

data Zero = Zero
data Succ o = Succ o

class Ordinal o where
  ordinal :: o

instance Ordinal Zero where
  ordinal = Zero

instance Ordinal n => Ordinal (Succ n) where
  ordinal = Succ ordinal

Args is a model for functions with a variable number of
initial arguments of homogeneous type.

data Args a b = Args0 b | ArgsN (a -> Args a b)

instance Functor (Args a) where
  fmap f (Args0 x) = Args0 $ f x
  fmap f (ArgsN g) = ArgsN $ fmap f . g

constN is a simple example of an Args. It models a variation
on const (well, flip const, actually) that ignores a variable
number of initial arguments.

class Ordinal n => ConstN n where
  constN :: n -> b -> Args a b

instance ConstN Zero where
  constN _ = Args0

instance ConstN n => ConstN (Succ n) where
  constN (Succ o) = ArgsN . const . constN o

We can convert any Args into the actual function that it represents.
(The inverse is also possible, but we do not need that here.)

class Ordinal n => ArgsToFunc n a b f where
  argsToFunc :: n -> Args a b -> f

instance ArgsToFunc Zero a b b where
  argsToFunc _ (Args0 b) = b

instance ArgsToFunc n a b f => ArgsToFunc (Succ n) a b (a -> f) where
  argsToFunc (Succ o) (ArgsN g) = argsToFunc o . g

When the return type is itself a function, we will need to flip
arguments of the internal function out of the Args.

flipOutArgs :: Args a (b -> c) -> b -> Args a c
flipOutArgs (Args0 f) = Args0 . f
flipOutArgs (ArgsN f) x = ArgsN $ flip flipOutArgs x . f

flipInArgs is the inverse of flipOutArgs. It requires an ordinal, because
we need to know how far in to flip the argument.

class Ordinal n => FlipInArgs n where
  flipInArgs :: n -> (b -> Args a c) -> Args a (b -> c)

instance FlipInArgs Zero where
  flipInArgs _ f = Args0 $ argsToFunc Zero . f

instance FlipInArgs n => FlipInArgs (Succ n) where
  flipInArgs (Succ o) f = ArgsN $ flipInArgs o . g
    where g x y = let ArgsN h = f y in h x

Now we are ready to construct replace.

class ArgsToFunc n Int (a -> l -> l) f =>
      Replace n a l f | n a -> l f, f -> n a l
  where
    replaceA :: n -> Args Int a
    replace :: f

instance Replace Zero a a (a -> a -> a) where
  replaceA _ = Args0 const
  replace = const

instance (Replace n a l f, FlipInArgs n, ConstN n,
          ArgsToFunc (Succ n) Int (a -> [l] -> [l]) f') =>
         Replace (Succ n) a [l] f' where
  replaceA (Succ o) = ArgsN mkReplace
    where
      mkReplace i = flipInArgs o $ flipInArgs o . mkRepl o i
      mkRepl o i x xs
       | null t    = constN o h
       | otherwise = fmap (h ++) $ fmap (: tail t) $
                     flipOutArgs (flipOutArgs (replaceA o) x) xs
       where (h, t) = splitAt i xs
  replace = argsToFunc ordinal $ replaceA ordinal
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to