3

如果我定义一个简单的函数:

let myConcat a b = 
    a + "+" + b

然后考虑到函数是 F# 中的一等值的说法,我希望能够myConcat像这样使用:

let result = myConcat "a" (fun () -> "b")

它没有产生字符串“a+b”,而是给了我以下错误:

error FS0002: This function takes too many arguments, or is used in a context where a function is not expected

希望我只是弄错了语法,但在我看来,函数不能真正用作 F# 中的值。谁能解释这里发生了什么?

编辑 为了进一步澄清我的要求,我可以拥有等效的 C# 代码:

public string myConcat(string a, string b) { return a + "+" + b; }

但是,如果我想在参数 b 中传递“稍后调用函数”,我必须这样做:

public string myConcat(string a, Action<string> b) { return a + "+" + b(); }

或者我可以这样称呼它:

Func<string> b = () => "b";
var result = myConcat("a", b());

C# 并没有(据我所知)声称函数是一等值。F# 确实提出了这一要求。那么当我不能将 unit -> string 函数视为“惰性评估”字符串值时有什么区别?

4

3 回答 3

6

您正在创建的 lambda 具有类型(单位 -> 字符串)。然后,您尝试拨打电话,结果为

字符串->字符串->(单位->字符串)->字符串

当预期的调用应该有类型

字符串 -> 字符串 -> 字符串 -> 字符串

所以,你试图传入 lambda,而你真正想要传入的是调用 lambda 的结果

let result = myConcat "a" ((fun() -> "b")())
于 2013-09-05T21:34:17.603 回答
4

你可以这样称呼它:let result = myConcat "a" ((fun() -> "b")())

当您创建 myConcat 时,它被解释为:val myConcat : a:string -> b:string -> string

它期望第二个参数作为字符串,并且您正在向它传递一个方法。

要做你想做的事,你必须声明 myConcat ,let myConcat a b = a + "+" + b()然后你可以传入一个函数作为第二个参数。

这将被解释为val myConcat : a:string -> b:(unit -> string) -> string

于 2013-09-05T21:33:06.143 回答
2

主要感谢 Mauricio Scheffer,我有我的问题的答案:

C# 和许多其他语言也提供“函数是一流的值”。但是,作为一种 OO 语言,在 C# 中这通常表示为“函数是一等对象”。短语“函数是一等值”只是指通过参数将函数引用传递给另一个函数的能力,就好像它是一个值一样。没有“神奇”的解释,例如 unit -> string 值等同于 string 值,只是 unit -> string 函数是传递给另一个值的有效值。

由于 F# 强大的类型推断功能,我不需要像使用 C# 那样在 F# 中指定引用函数的单位 -> 字符串类型,但编译器会推断这种类型,所以我不会能够只传入一个简单的字符串,而无需首先将其包装在匿名单元 -> 字符串函数中。

如果您比我更了解这些东西并且我有问题,请进行编辑以纠正任何错误。

于 2013-09-12T20:51:39.583 回答