How do you curry the 2nd (or 3rd, 4th, …) parameter in F# or any functional language?

房东的猫 提交于 2020-01-12 13:47:36

问题


I'm just starting up with F# and see how you can use currying to pre-load the 1st parameter to a function. But how would one do it with the 2nd, 3rd, or whatever other parameter? Would named parameters to make this easier? Are there any other functional languages that have named parameters or some other way to make currying indifferent to parameter-order?


回答1:


Typically you just use a lambda:

fun x y z -> f x y 42

is a function like 'f' but with the third parameter bound to 42.

You can also use combinators (like someone mentioned Haskell's "flip" in a comment), which reorder arguments, but I sometimes find that confusing.

Note that most curried functions are written so that the argument-most-likely-to-be-partially-applied comes first.

F# has named parameters for methods (not let-bound function values), but the names apply to 'tupled' parameters. Named curried parameters do not make much sense; if I have a two-argument curried function 'f', I would expect that given

let g = f
let h x y = f x y

then 'g' or 'h' would be substitutable for 'f', but 'named' parameters make this not necessarily true. That is to say, 'named parameters' can interact poorly with other aspects of the language design, and I personally don't know of a good design offhand for 'named parameters' that interacts well with 'first class curried function values'.




回答2:


Just for completeness - and since you asked about other functional languages - this is how you would do it in OCaml, arguably the "mother" of F#:

$ ocaml
# let foo ~x ~y = x - y ;;
val foo : x:int -> y:int -> int = <fun>
# foo 5 3;;
- : int = 2
# let bar = foo ~y:3;;
val bar : x:int -> int = <fun>
# bar 5;;
- : int = 2

So in OCaml you can hardcode any named parameter you want, just by using its name (y in the example above).

Microsoft chose not to implement this feature, as you found out... In my humble opinion, it's not about "poor interaction with other aspects of the language design"... it is more likely because of the additional effort this would require (in the language implementation) and the delay it would cause in bringing the language to the world - when in fact only few people would (a) be aware of the "stepdown" from OCaml, (b) use named function arguments anyway.

I am in the minority, and do use them - but it is indeed something easily emulated in F# with a local function binding:

let foo x y = x - y
let bar x = foo x 3
bar ...



回答3:


OCaml, the language that F# was based on, has labeled (and optional) arguments that can be specified in any order, and you can partially apply a function based on those arguments' names. I don't believe F# has this feature.

You might try creating something like Haskell's flip function. Creating variants that jump the argument further in the argument list shouldn't be too hard.

let flip f a b = f b a
let flip2 f a b c = f b c a
let flip3 f a b c d = f b c d a



回答4:


In Python, you can use functools.partial, or a lambda. Python has named arguments. functools.partial can be used to specify the first positional arguments as well as any named argument.

from functools import partial

def foo(a, b, bar=None):
    ...

f = partial(foo, bar='wzzz') # f(1, 2) ~ foo(1, 2, bar='wzzz')
f2 = partial(foo, 3)         # f2(5) ~ foo(3, 5)

f3 = lambda a: foo(a, 7)     # f3(9) ~ foo(9, 7)



回答5:


It's possible to do this without declaring anything, but I agree with Brian that a lambda or a custom function is probably a better solution.

I find that I most frequently want this for partial application of division or subtraction.

> let halve = (/) >> (|>) 2.0;;
> let halfPi = halve System.Math.PI;;

val halve : (float -> float)
val halfPi : float = 1.570796327

To generalize, we can declare a function applySecond:

> let applySecond f arg2 = f >> (|>) arg2;;
val applySecond : f:('a -> 'b -> 'c) -> arg2:'b -> ('a -> 'c)

To follow the logic, it might help to define the function thus:

> let applySecond f arg2 =
-     let ff = (|>) arg2
-     f >> ff;;
val applySecond : f:('a -> 'b -> 'c) -> arg2:'b -> ('a -> 'c)

Now f is a function from 'a to 'b -> 'c. This is composed with ff, a function from 'b -> 'c to 'c that results from the partial application of arg2 to the forward pipeline operator. This function applies the specific 'b value passed for arg2 to its argument. So when we compose f with ff, we get a function from 'a to 'c that uses the given value for the 'b argument, which is just what we wanted.

Compare the first example above to the following:

> let halve f = f / 2.0;;
> let halfPi = halve System.Math.PI;;

val halve : f:float -> float
val halfPi : float = 1.570796327

Also compare these:

let filterTwoDigitInts = List.filter >> (|>) [10 .. 99]
let oddTwoDigitInts = filterTwoDigitInts ((&&&) 1 >> (=) 1)
let evenTwoDigitInts = filterTwoDigitInts ((&&&) 1 >> (=) 0)

let filterTwoDigitInts f = List.filter f [10 .. 99]
let oddTwoDigitInts = filterTwoDigitInts (fun i -> i &&& 1 = 1)
let evenTwoDigitInts = filterTwoDigitInts (fun i -> i &&& 1 = 0)

Alternatively, compare:

let someFloats = [0.0 .. 10.0]
let theFloatsDividedByFour1 = someFloats |> List.map ((/) >> (|>) 4.0)
let theFloatsDividedByFour2 = someFloats |> List.map (fun f -> f / 4.0)

The lambda versions seem to be easier to read.



来源:https://stackoverflow.com/questions/1911096/how-do-you-curry-the-2nd-or-3rd-4th-parameter-in-f-or-any-functional-la

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!