Partial application in Haskell with multiple arguments

For some function f (x1, x2, x3, ..., xN) it is often useful to apply it partially in several places. For example, for N = 3, we could define g (x) = f (1, x, 3). However, the standard partial application in Haskell does not work that way and allows us to partially apply a function by fixing its first arguments (because all functions actually take only one argument). Is there an easy way to do something like this:

g = f _ 2 _
g 1 3

      

with the output value f 1 2 3

? We could of course make a lambda function

g=(\x1 x3 -> f x1 2 x3)

      

but I find it quite unreadable. For example, in Mathematica it works in a way that I find pretty nice:

g=f[#1,2,#2]&
g[1,3]

      

with an exit f[1,2,3]

.

Edit: Maybe I should say a little about motivation. I would like to use such partially applied functions in dot style compositions, i.e. In such expressions:

h = g. f _ 2 . k

      

to receive h 3 = g(f(k(3),2))

.

+3


source to share


5 answers


You can read this question on how to change the order of the arguments and then use a partial application, but actually the cleanest and clearest way to do it currently in Haskell is straight forward:



g x y = f x 2 y

      

+2


source


No, the easiest way is to define a lambda. You might try to play with flip

, but I doubt it would be cleaner and simpler than a lambda. Especially for a longer argument list.



+1


source


The easiest (and canonical) way is to define a lambda. It is much more readable if you possibly use meaningful argument names

getCurrencyData :: Date -> Date -> Currency -> IO CurrencyData
getCurrencyData fromDate toDate ccy = {- implementation goes here -}

      

you can define your new function with lambda syntax

getGBPData = \from to -> getCurrencyData from to GBP

      

or without it

getGBPData from to = getCurrencyData from to GBP

      

or you can use combinators, but I think this is pretty ugly

getGBPData = \from to -> getCurrencyData from to GBP
           = \from to -> flip (getCurrencyData from) GBP to
           = \from    -> flip (getCurrencyData from) GBP
           = \from    -> (flip . getCurrencyData) from GBP
           = \from    -> flip (flip . getCurrencyData) GBP from
           =             flip (flip . getCurrencyData) GBP

      

+1


source


There is no general way to do what you ask, but sometimes you can use infix sections as an alternative flip

. Using the last example:

g . (`f` 2) . k

      

Also, I would like to point out that sometimes it can help if you change the order of the arguments that your functions do. For example, if you have a function that will often be partially applied to a single argument, in particular, you should probably make that the first argument.

Suppose you are implementing a data structure that represents a two-dimensional game pad (for example, for a Chess program), you probably want the first argument to your function to getPiece

be a chessboard and the second argument to a Location. I think it's likely that the checked location will change more often than the fee. Of course, this does not fix the general problem (you might want to check the same location in the board list), but it might make it easier. When I decide the order of the arguments, that's the main thing I think.

+1


source


Other things to consider:

Define helper functions for the partial application templates (locally or globally if you use a small number of templates multiple times).

fix_2 f a = \x -> f x a
fix1_3 f a b = \x -> f a x b

h = g . fix_2 f 2 . k

      

Not as good as your hypothetical "empty" syntax, but ok; you can read fix_2

as a tag identifying the private schema of application 1 in use .

Note that you do not need any partial application schemas that do not capture the last argument; fixing the first and third arguments of a function with four arguments (leaving the two argument functions) is the same as fixing the first and third arguments of a function with 3 arguments.

As a more attractive idea, I believe you should write a Template Haskell quasi-locator that actually implements your "underscores are parameters of the implied function" pseudosyntax. This will allow you to write expressions:

h = g . [partial| f _ |] . k

      

More syntactic overhead than a helper function and you still need to include an alias (to identify the quasi-locator), but it might be easier to read if your partial application schema is much more complex:

h = g . [partial| f 1 _ 3 4 _ 6 |] . k

      

That means a lot of work implementing the Haskell template code, although I've never done that, I don't know how much. But then you have arbitrary partial application diagrams, whereas the helper function route requires manual definition for each template.


1 Note that the fix only the second argument already has a standard auxiliary functions flip

. Partly applying to the second argument may not sound intuitively like replacing the first two arguments, but thanks to lazy evaluation and currying, they're actually the same thing!

+1


source







All Articles