4

The following code is an attempt to write a variadic function that acts like this:

  • bind_variadic mx f = mx >>= f
  • bind_variadic mx my f = do { x <- mx; y <- my; f x y }

I can write it if one expresses the "rest of binding" as a variable k, but in order to write a typeclass I need to write one function in terms of the other. To be precise, I want to express l1 in terms of l0, l2 in terms of l1, etc.

import Prelude hiding ((>>=), (>>), Monad, return)

-- override the default monad so we don't get confusing
-- instances like "Monad (->)".
class Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  (>>) :: m a -> m b -> m b
  return :: a -> m a
  fail :: String -> m a

h :: Monad m => m a -> (t -> m b) -> (a -> t) -> m b
h mx k f = mx >>= \x -> k (f x)

l0 = h (return 3) id (\x -> return x)
l1 = h (return 3) (h (return 4) id) (\x y -> return x)
l2 = h (return 3) (h (return 4) (h (return 5) id)) (\x y z -> return x)

Perhaps the solution involves another continuation?

edit

here's an idea that requires an additional join...

-- if not using Control.Monad, use this
join :: Monad  =>  ( α) ->  α
join mx = mx >>= id

-- idea: get side effects of evaluating first arguments first
h' mz k f = k f >>= \f' -> mz >>= (return . f')

l1' = h' (return 3) return
unary = join (l1' (\x -> return x))
l2' = h' (return 4) l1'
binary = join (l2' (\x y -> return x))
l3' = h' (return 5) l2'
ternary = join (l3' (\x y z -> return x))
gatoatigrado
  • 16,580
  • 18
  • 81
  • 143
  • 2
    You might find Daniel Fridlender and Mia Indrika's "An n-ary zipWith in Haskell" a better start point than the code you are working with. It provides a design pattern for variadic functions in Haskell. Personally I'd avoid variadic functions altogether - they are slow and complicated whereas an arity family like liftM, liftM2 ... is direct and fast (with minor syntactic cruft). – stephen tetley Oct 16 '11 at 07:32
  • @stephentetley -- they seem to be doing what John L is suggesting, not using a type class. – gatoatigrado Oct 16 '11 at 08:27

1 Answers1

1

If you want to express this:

ap_variadic mx f = mx >>= f
ap_variadic mx my f = do { x <- mx; y <- my; f x y }

I would use Control.Applicative instead. Then:

join (f <$> mx)
join (f <$> mx <*> my)
join (f <$> mx <*> my <*> mz)

I think this is better (simpler, more maintainable) than any polyvariadic solution would be.

John L
  • 27,937
  • 4
  • 73
  • 88
  • Yes, I'll go with that for my real code. I think the question is still an interesting typing problem. – gatoatigrado Oct 16 '11 at 08:28
  • Your solution assumes `f` is pure, which it is not. My fault for using "apply" as the name. – gatoatigrado Oct 17 '11 at 19:34
  • 1
    @gatoatigrado: this solution assumes that `f :: a -> ... -> m x`, that is it takes some number of arguments and returns a result in a monad. Then `f <$> mx :: m (m x)`, which is reduced by the outer `join`. – John L Oct 18 '11 at 14:31