如何在F#中编写一个可变函数来模拟类似的Haskell解决方案?

rob*_*kuz 8 f# variadic-functions

我怎样才能(如果有的话)模拟可变参数函数(而不是方法)以便我可以编写

sum 1 2 3
sum 1 2 3 4 5
sum 1 2 3 4 5 6 7
// etc.
Run Code Online (Sandbox Code Playgroud)

上面的代码只是作为一个例子 - 显然如果我必须总结一个列表

[ 1; 2 ; 3] |> List.sum 
Run Code Online (Sandbox Code Playgroud)

是一个更好的方式.

但是,我正在寻找像Haskell解决方案这样的结构相似的解决方案

同样重要的是函数调用和参数值的正常语法保持不变.所以

sum 1 2 3
Run Code Online (Sandbox Code Playgroud)

VS

sum(1, 2, 3)
Run Code Online (Sandbox Code Playgroud)

这实际上意味着

let sum ([<ParamArray>] arr) = ...
Run Code Online (Sandbox Code Playgroud)

在这个特定的情况下不需要.

所有这一切的动机:我正在探索F#的类型系统和语法的外边缘.而且我完全意识到我可能已经越过了可能已经存在的边界.

PS:我的具体想法(我在这里没有描述)也可以完全不同的方式解决 - 所以我知道,所以我已经完成了.因此我的问题不是:如何以不同的方式解决这个问题,但如何在结构上像Haskell一样解决这个问题.

PPS:Double Karma-Points如果你可以使整个解决方案递归.

Gus*_*Gus 20

你说功能,而不是方法.所以ParamArray不是一个选择.

您链接的Haskell代码基于推断的结果类型.

这是基于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
Run Code Online (Sandbox Code Playgroud)

UPDATE

上面的代码不再适用于F#4.1(参见注释),但这是一个更好的例子,带有n(无限制)参数的递归多变量函数:

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
Run Code Online (Sandbox Code Playgroud)

你也可以看看这个polyvariadic折叠.

  • 我认为你的最后一次编辑只杀死了1,000,000只小猫.但令人惊讶的是,它可以做到. (7认同)
  • @TomasPetricek尝试使用10个以上的args时,它也杀了我的编译器;) (2认同)
  • 在VS2015的Debug构建中运行时,此代码抛出一个`TypeInitializationException`.它虽然适用于Release版本.我[在这里向F#团队报告](https://github.com/Microsoft/visualfsharp/issues/865). (2认同)

Tom*_*cek 7

正如评论中所提到的,您可以ParamArray在F#中使用该属性,这将允许您使用多个参数调用该函数 - 尽管您必须使用.NET表示法并编写sum(1,2,3,4,5,6).

也就是说,我可能不会在实践中这样做.如果您正在编写一个包含未知数量值的输入的函数,那么使用列表可能是更好的设计:

List.sum [1; 2; 3 ]
List.sum [1; 2; 3; 4; 5 ]
List.sum [1; 2; 3; 4; 5; 6; 7 ]
Run Code Online (Sandbox Code Playgroud)

这只是几个字符,它更好地模拟你正在解决的问题 - 至少,基于你在这里发布的玩具示例.

在不知道你实际解决的问题是什么的情况下很难给出一个好的答案.但总的来说,我认为列出一个很好的F#友好默认值.使用ParamArray在某些情况下以及C#互操作有用.

  • @robkuz作为"探索F#型系统的界限",这很有道理.虽然我仍然认为在实践中没有人真的应该这样做很重要:-).问题是"如何从F#中的Haskell做XYZ"的问题尤其如此,因为它们经常指向错误的方向(盲目地使用另一种语言中的一种语言的技术从来都不是一个好主意).当然,纯粹出于好奇心的问题是个例外! (4认同)