How to write a variadic function in F# emulating a similar Haskell solution?

生来就可爱ヽ(ⅴ<●) 提交于 2019-11-30 07:30:29

问题


How can I (if at all) emulate variadic functions (not methods) so that I could write

sum 1 2 3
sum 1 2 3 4 5
sum 1 2 3 4 5 6 7
// etc.

The code above is just meant as an example - obviously if I would have to sum up a list then

[ 1; 2 ; 3] |> List.sum 

is a much better way.

However I am looking for a structurally similar solution like this Haskell solution

What is also important is that the normal syntax for function calls and parameter values remains the same. So

sum 1 2 3

vs

sum(1, 2, 3)

which effectively means that

let sum ([<ParamArray>] arr) = ...

is not wanted in this specific case.

The motivation for all of this: I am exploring the outer fringes of F#'s type system and syntax. And I am fully aware of that I might have crossed the boundary of what is possible already.

PS: my concrete ideas (which I have not described here) can also be solved completely differently - so I know and so I have done already. Therefore my question is not: how can this be solved differently but how can this be solved structurally like Haskell.

PPS: Double Karma-Points if you can make the whole solution recursive.


回答1:


You said function, not method. So ParamArray is not an option.

The Haskell code you linked is based on the inferred result type.

Here's a way to resolve based on the inferred result type in F#:

type T = T with
    static member inline ($) (T, r:'t->'t        ) = fun a b     -> a + b
    static member inline ($) (T, r:'t->'t->'t    ) = fun a b c   -> a + b + c
    static member inline ($) (T, r:'t->'t->'t->'t) = fun a b c d -> a + b + c + d

let inline sum (x:'a) :'r = (T $ Unchecked.defaultof<'r>) x

let x:int = sum 2 3 
let y:int = sum 2 3 4
let z:int = sum 2 3 4 5
let d:decimal = sum 2M 3M 4M

let mult3Numbers a b c = a * b * c
let res2 = mult3Numbers 3 (sum 3 4  ) 10
let res3 = mult3Numbers 3 (sum 3 4 5) 10

UPDATE

The above code doesn't work anymore as from F# 4.1 (see the comments) but here's a better example with a recursive polyvariadic function taking n (unlimited) arguments:

type T = T with
    static member        ($) (T, _:int    ) = (+)
    static member        ($) (T, _:decimal) = (+)

let inline sum (i:'a) (x:'a) :'r = (T $ Unchecked.defaultof<'r>) i x

type T with
    static member inline ($) (T, _:'t-> 'rest) = fun (a:'t) -> (+) a >> sum


let x:int = sum 2 3 
let y:int = sum 2 3 4
let z:int = sum 2 3 4 5
let d:decimal = sum 2M 3M 4M

let mult3Numbers a b c = a * b * c
let res2 = mult3Numbers 3 (sum 3 4) (sum 2 2 3 3)
let res3 = mult3Numbers 3 (sum 3 4 5 11 13 20) 10

You can also have a look at this polyvariadic fold.




回答2:


As mentioned in the comments, you can use the ParamArray attribute in F# and this will let you call the function with multiple parameters - although you'll have to use the .NET notation and write sum(1,2,3,4,5,6).

That said, I probably wouldn't do this in practice. If you're writing a function that takes an input consisting of an unknown number of values, then using a list is likely a better design:

List.sum [1; 2; 3 ]
List.sum [1; 2; 3; 4; 5 ]
List.sum [1; 2; 3; 4; 5; 6; 7 ]

This is only a few more characters and it better models the problem that you're solving - at least, based on the toy example you posted here.

It is hard to give a good answer without knowing what is the problem that you are actually solving. But in general, I think taking a list is a good F#-friendly default. Using ParamArray is useful in some cases and for C# interop.



来源:https://stackoverflow.com/questions/28243963/how-to-write-a-variadic-function-in-f-emulating-a-similar-haskell-solution

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