What if I want to change the order of arguments in a function?
There is flip
:
flip :: (a -> b -> c) -> b -> a -> c
but I don't see how to make it work for a larger number of arguments. Is there a general method to permute the arguments?
Yes, it matters. The arguments must be given in the order the function expects them.
In a method or constructor invocation or class instance creation expression, argument expressions may appear within the parentheses, separated by commas. Each argument expression appears to be fully evaluated before any part of any argument expression to its right.
5 Types of Arguments in Python Function Definition:keyword arguments. positional arguments. arbitrary positional arguments. arbitrary keyword arguments.
If you feel like editing functions after they're written, you really really should read Conal Elliott's excellent blog post semantic editor combinators
http://conal.net/blog/posts/semantic-editor-combinators
In fact, everyone should read it anyway. It's a genuinely useful method (which I'm abusing here). Conal uses more constructs than just result
and flip
to very flexible effect.
result :: (b -> b') -> ((a -> b) -> (a -> b')) result = (.)
Suppose I have a function that uses 3 arguments
use3 :: Char -> Double -> Int -> String use3 c d i = c: show (d^i)
and I'd like to swap the first two, I'd just use flip use3
as you say, but if I wanted to swap the seconnd and third, what I want is to apply flip
to the result of applying use3
to its first argument.
use3' :: Char -> Int -> Double -> String use3' = (result) flip use3
Let's move along and swap the fourth and fifth arguments of a function use5
that uses 5.
use5 :: Char -> Double -> Int -> (Int,Char) -> String -> String use5' :: Char -> Double -> Int -> String -> (Int,Char) -> String use5 c d i (n,c') s = c : show (d ^ i) ++ replicate n c' ++ s
We need to apply flip
to the result of applying use5
to it's first three arguments, so that's the result of the result of the result:
use5' = (result.result.result) flip use5
Why not save thinking later and define
swap_1_2 :: (a1 -> a2 -> other) -> (a2 -> a1 -> other) swap_2_3 :: (a1 -> a2 -> a3 -> other) -> (a1 -> a3 -> a2 -> other) --skip a few type signatures and daydream about scrap-your-boilerplate and Template Haskell swap_1_2 = flip swap_2_3 = result flip swap_3_4 = (result.result) flip swap_4_5 = (result.result.result) flip swap_5_6 = (result.result.result.result) flip
...and that's where you should stop if you like simplicity and elegance. Note that the type other
could be b -> c -> d
so because of fabulous Curry and right associativity of ->
, swap_2_3 works for a function which takes any number of arguments above two. For anything more complicated, you should really write a permuted function by hand. What follows is just for the sake of intellectual curiosity.
Now, what about swapping the second and fourth arguments? [Aside: there's a theorem I remember from my algebra lectures that any permutation can be made as the composition of swapping adjacent items.]
We could do it like this: step 1: move 2 next to 4 (swap_2_3
)
a1 -> a2 -> a3 -> a4 -> otherstuff a1 -> a3 -> a2 -> a4 -> otherstuff
swap them there using swap_3_4
a1 -> a3 -> a2 -> a4 -> otherstuff a1 -> a3 -> a4 -> a2 -> otherstuff
then swap 4 back to position 2 using swap_2_3
again:
a1 -> a3 -> a4 -> a2 -> otherstuff a1 -> a4 -> a3 -> a2 -> otherstuff
so
swap_2_4 = swap_2_3.swap_3_4.swap_2_3
Maybe there's a more terse way of getting there directly with lots of results and flips but random messing didn't find it for me!
Similarly, to swap 1 and 5 we can move 1 over to 4, swap with 5, move 5 back from 4 to 1.
swap_1_5 = swap_1_2.swap_2_3.swap_3_4 . swap_4_5 . swap_3_4.swap_2_3.swap_1_2
Or if you prefer you could reuse swap_2_4
by flipping at the ends (swapping 1 with 2 and 5 with 4), swap_2_4 then flipping at the ends again.
swap_1_5' = swap_1_2.swap_4_5. swap_2_4 .swap_4_5.swap_1_2
Of course it's much easier to define
swap_1_5'' f a b c d e = f e b c d a
which has the benefit of being clear, consise, efficient and has a helpful type signature in ghci without explicitly annotating it.
However, this was a fantastically entertaining question, thanks.
The best way in general is to just do it manually. Assume you have a function
f :: Arg1 -> Arg2 -> Arg3 -> Arg4 -> Res
and you would like
g :: Arg4 -> Arg1 -> Arg3 -> Arg2 -> Res
then you write
g x4 x1 x3 x2 = f x1 x2 x3 x4
If you need a particular permutation several times, then you can of course abstract from it, like flip
does for the two-argument case:
myflip :: (a4 -> a1 -> a3 -> a2 -> r) -> a1 -> a2 -> a3 -> a4 -> r myflip f x4 x1 x3 x2 = f x1 x2 x3 x4
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With