mcomp: polyvariadic composition. If f:: a1->a2->.... ->cp and g:: cp->d then f `mcomp` g:: a1->a2->.... ->d This specification is ambiguous, because x->y->z can be 'parsed' either as a1->a2->cp (with cp being z) or as a1->cp (with a1 being x and cp being y->z). However, if we assume that cp is not a functional type, the parse is unique. The algorithm: induction: Base case: the ordinary composition f::(a1->cp) `mcomp` g::(cp->d) :: a1->d = g . f where cp is not a functional type. Inductive case: f::(a1->(a2->...cp)) `mcomp` g::(cp->d) = \xa1 -> (f xa1) `mcomp` g The following is a literate Haskell98 code _with extensions_. We need the extension of multi-parameter classes and functional dependencies. In the following class definition, we assume that either: -- f2 is a non-functional type and f2 === cp -- f2 is of the form a1->a2->...->cp The instance declarations should abide (and thus enforce) the first constraint. The sole inductive definition abides by (and enforces) the second constraint. > class MCompose f1 f2 cp gresult result | f1 f2 cp gresult -> result, f2->cp > where > mcomp:: (f1->f2) -> (cp->gresult) -> result Class instances. Please keep in mind that cp must be a non-functional type and f2 and cp must be the same. These instances enumerate the base cases. > instance MCompose a (Maybe b) (Maybe b) c (a->c) where > --mcomp f::(a->(Maybe b)) g::((Maybe b)->c) :: a->c > mcomp f g = g . f > instance MCompose a [b] [b] c (a->c) where > --mcomp f::(a->[b]) g::([b]->c) :: a->c > mcomp f g = g . f > instance MCompose a Int Int c (a->c) where > --mcomp f::(a->Int) g::(Int->c) :: a->c > mcomp f g = g . f you can add more instances like that. The inductive case: > instance (MCompose f1 f2 cp gresult result) => > MCompose a (f1->f2) cp gresult (a->result) where > mcomp f g = \a -> mcomp (f a) g Test cases: > f1 x = Just x > f2 x y = Just (f1 y) > f3 x y z = Just (f2 y z) > f4 x y z u = Just (x + y + z + u) > > g (Just x) = show x Therefore: mcomp f1 g :: Show a => a -> [Char] mcomp f2 g :: Show (Maybe a) => b -> a -> [Char] mcomp f3 g :: Show (Maybe (Maybe a)) => b -> c -> a -> [Char] mcomp f4 g :: Num a => a -> a -> a -> a -> [Char] Types seem correct, and so do the results: Main> mcomp f1 g () --==> "()" Main> mcomp f2 g () 1 --==> "Just 1" Main> mcomp f3 g () 1 'c' --==> "Just (Just 'c')" Main> mcomp f4 g 1 2 3 4 --==> "10" > s1 x = x > s2 x y = x * y > s3 x y z = x * y * z > s4 x y z u = x * y * z * u Main> mcomp s3 show (1::Int) 2 3 --==> "6" Main> (s4 `mcomp` show) (1::Int) 2 3 4 --==> "24" Yet another example: Main> ((\a b c d -> [a,b,c,d]) `mcomp` sum) 1 2 3 4 --==> 10