Search code examples
haskellfunctional-programmingvariadic-functionspolyvariadic

haskell - chain up elements with an associative binary operation


I am an intermediate schemer, but only a haskell beginner. Here is my problem:

Suppose you have an associative binary operation, says (>>=). Is there a polyvariadic function p such that p (>>=) h g f e = h >>= g >>= f >>= e?

I am asking this question because this question says it is possible if the binary operation takes inputs of the same type. I wonder if this can be generalized.

EDIT-1: I try to modify the code in http://okmij.org/ftp/Haskell/vararg-fn.lhs (the section of Variable number of variably typed arguments) with little progress.

EDIT-2: Simplify the code a bit.

{-# LANGUAGE FunctionalDependencies, FlexibleInstances #-}


module Main where


class Lfold f a b | b -> a where 
  lfold :: (a -> (f a) -> (f a)) -> (f a) -> a -> b

instance Lfold f a (f a) where
  lfold op rid x = op x rid

instance Lfold f a b => Lfold f a (a -> b) where
  lfold op rid x y = lfold op (op x rid) y


test :: [String]
test = lfold (:) [] "a" "b" "c"

main :: IO ()
main = putStrLn $ show test

Solution

  • Yes, you can create such a function. It is very ugly however, and you will need to explicitly type every argument you are going to pass to make the compiler find the correct instance.

    Starting from the polyvariadic function template you linked, I arrived at

    {-# LANGUAGE FlexibleInstances, InstanceSigs, MultiParamTypeClasses #-}
    
    class ImplicitChain m a r where
      p :: m a -> r
    
    instance Monad m => ImplicitChain m a (m a) where
      p :: m a -> m a
      p x = x
    
    instance (Monad m, ImplicitChain m b r) => ImplicitChain m a ((a -> m b) -> r) where
      p :: m a -> (a -> m b) -> r
      p x f = p (x >>= f)
    
    
    h :: Int -> [Int]
    h = replicate 2
    g :: Int -> [Int]
    g = (:[])
    f :: Int -> [Int]
    f = flip enumFromTo 2
    
    test :: [Int]
    test = p [1::Int] h g f
    

    But you were asking whether we can do more generic, so that the binary operation is an argument as well. Yes:

    {-# LANGUAGE FlexibleInstances, InstanceSigs, MultiParamTypeClasses, UndecidableInstances #-}
    
    class ImplicitVariadic a b r where
      p :: (a -> b -> a) -> r
    
    instance ImplicitVariadic a b (a -> a) where
      p :: (a -> b -> a) -> a -> a
      p _ x = x
    
    instance (ImplicitVariadic a b (a -> r)) => ImplicitVariadic a b (a -> b -> r) where
      p :: (a -> b -> a) -> a -> b -> r
      p f x y = p f (f x y)