Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rotating arguments in Haskell

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.

like image 925
Aadit M Shah Avatar asked Mar 14 '13 10:03

Aadit M Shah


2 Answers

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.

like image 186
Matthew Walton Avatar answered Oct 23 '22 04:10

Matthew Walton


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
like image 25
Ed'ka Avatar answered Oct 23 '22 04:10

Ed'ka