5
votes

The flip function in Haskell is used to switch the positions of the first two arguments of a function:

flip :: (a -> b -> c) -> b -> a -> c
flip f y x = f x y

Similarly we can write a function to rotate three arguments:

rot :: (a -> b -> c -> d) -> b -> c -> a -> d
rot f y z x = f x y z

Can this concept be extended to functions which curry arbitrary number of arguments?

Given a function of type a -> ... -> z is it possible to write a function of the following type?

(a -> ... -> z) -> ... -> a -> z

I know that the -> operator is right associative. Hence ... -> z can't be split. Nevertheless, I would like to know for sure.

2

2 Answers

7
votes

You're right, you can't do this. You'd have to pattern match against an arbitrary number of arguments, and there's no way to do that.

You could use Template Haskell to generate a set of rotate functions for different arities, but you'd always have to decide how many to generate in advance and it wouldn't be a truly generic function, just a shortcut for writing them.

You could do something like it if your functions happened to take their arguments as lists (eew), but that also has the notable disadvantage of requiring the argument types to be homogenous.

2
votes

Well, technically rot could be (but probably shouldn't) implemented using IncoherentInstances extension:

{-# LANGUAGE MultiParamTypeClasses, TypeFamilies,
  FlexibleInstances, FlexibleContexts,
  UndecidableInstances, IncoherentInstances #-}    

class Rotable a r where
    rot :: a -> r

instance (r ~ (b -> a -> c)) => Rotable (a -> b -> c) r where
    rot = flip

instance (Rotable (a -> c -> d) r', r ~ (b -> r')) => Rotable (a -> b -> c -> d) r where
    rot f b = rot (`f` b)

Example of usage:

*Main> rot (-) 1 2
1
*Main> :t rot foldr
rot foldr :: b -> [a] -> (a -> b -> b) -> b
*Main> :t (rot . rot) foldr
(rot . rot) foldr :: [a] -> (a -> b -> b) -> b -> b
*Main> (rot . rot) foldr [1..5] (+) 0
15