f#签名匹配解释

时间:2014-12-29 17:23:12

标签: f#

我在很多情况下都遇到了F#的困难。我相信我没有掌握一些基本概念。我希望有人可以追踪我的推理并弄清楚我可能遗失的事情(可能是很多)。

说我正在使用Xunit。我想要做的是,提供两个列表,成对应用Assert.Equal方法。例如:

Open Xunit
let test1 = [1;2;3]
let test2 = [1;2;4]
List.map2 Assert.Equal test1 test2

编译器抱怨函数Equal不带一个参数。据我所知,不应该map2提供2个参数吗?

作为一个完整性检查,我在f#immediate中使用以下代码:

let doequal = fun x y -> printf "result: %b\n" (x = y)
let test1 = [1;2;3]
let test2 = [1;2;4]
List.map2 doequal test1 test2;;

这似乎完全相同。 doequal是一个lambda,它使用两个通用参数并返回 unit List.map2将每个参数成对地交给lambda,我得到了我预期的输出:

result: true
result: true
result: false

那是什么给出的?来源展示Xunit.Equal已签名public static void Equal<T>(T expected, T actual)。为什么我的参数不会映射到方法签名上?

编辑一个 我认为两个变量x和y与一个元组(x,y)可以互换地构造和解构。所以我尝试了两个选项,得到了不同的结果。似乎第二个可能比第一个更进一步。

List.map2 Assert.Equal(test1, test2) 编译器现在抱怨“连续参数”应该是分隔空格或者是tupled&#39;

List.map2(Assert.Equal(test1, test2)) 编译器现在抱怨“无法确定一个独特的重载方法......可能需要一个类型注释&#39;

4 个答案:

答案 0 :(得分:7)

我认为问题的一部分来自混合方法(OO风格)和功能(FP风格)。

  • FP样式函数有多个以空格分隔的参数。
  • OO样式方法的逗号和参数用逗号分隔。
  • 其他.NET库中的方法总是使用“tuple”语法调用(实际上与元组略有不同),并且元组被认为是一个参数。

F#编译器尝试处理这两种方法,但偶尔需要一些帮助。

一种方法是使用FP函数“包装”OO方法。

// wrap method call with function
let assertEqual x y = Assert.Equal(x,y)

// all FP-style functions
List.map2 assertEqual test1 test2

如果不创建辅助函数,在使用lambda调用“inline”方法时,通常需要将多个函数参数转换为一个元组:

List.map2 (fun x y -> Assert.Equal(x,y)) test1 test2

当您在一行中混合方法和函数时,通常会得到“连续参数应该分开”错误。

printfn "%s" "hello".ToUpper()  
// Error: Successive arguments should be separated 
// by spaces or tupled

这告诉你编译器遇到了问题,需要一些帮助!

你可以通过方法调用附加额外的问题来解决这个问题:

printfn "%s" ("hello".ToUpper())  // ok

或者有时,使用反向管道:

printfn "%s" <| "hello".ToUpper() // ok

包装方法通常值得无论如何,以便您可以交换参数以使其更适合部分应用:

// wrap method call with function AND swap params
let contains searchFor (s:string) = s.Contains(searchFor)

// all FP-style functions
["a"; "b"; "c"]
|> List.filter (contains "a")

请注意,在最后一行中,我必须使用parens优先于contains "a"而不是List.filter

答案 1 :(得分:5)

public static void Equal<T>(T expected, T actual)

不带两个参数 - 它需要一个参数,这是一个带有两个元素的元组(T expected, T actual)

请改为尝试:

List.map2 Assert.Equal(test1, test2)

答案 2 :(得分:2)

所有类型签名都在那里。

Assert.Equals的签名与'a * 'a -> unit类似。 List.map2需要'a -> 'b -> 'c

他们只是不适合。

List.map2 (fun x y -> Assert.Equal(x,y)) test1 test2 - 有效,因为lambda包裹Equals具有预期的签名。

List.zip test1 test2 |> List.map Assert.Equal - 因为您现在只有一个元组列表,因为List.map需要一个'a -> 'b函数(其中'a现在是一个元组),Assert.Equal 1}}现在是公平的游戏。

两个值和一个元组是隐式可互换的,这是不正确的。至少不是F语言,或者是基础的IL表示。当您从C#调用F#代码时,您可以认为这是一种方式 - 'a -> 'b -> 'c函数确实在句法上称为'a * 'b -> 'c函数 - 但这是更多的例外而不是规则。

答案 3 :(得分:1)

根据其签名Xunit.Assert.Equal()采用单个2值元组参数