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.
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 .
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.