Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change the order of arguments?

Tags:

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?

like image 675
Yrogirg Avatar asked Aug 26 '12 09:08

Yrogirg


People also ask

Does the order of arguments matter?

Yes, it matters. The arguments must be given in the order the function expects them.

What is order of parameters in Java?

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.

What are the different types of arguments in programming?

5 Types of Arguments in Python Function Definition:keyword arguments. positional arguments. arbitrary positional arguments. arbitrary keyword arguments.


2 Answers

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.

like image 150
AndrewC Avatar answered Sep 22 '22 21:09

AndrewC


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 
like image 45
kosmikus Avatar answered Sep 24 '22 21:09

kosmikus