我有两个文件。第一个文件的内容名为RailwayCombinator.fs:
module RailwayCombinator
let (|Uncarbonated|Carbonated|) =
function
| Choice1Of2 s -> Uncarbonated s
| Choice2Of2 f -> Carbonated f
let uncarbonated x = Choice1Of2 x
let carbonated x = Choice2Of2 x
let either successFunc failureFunc twoTrackInput =
match twoTrackInput with
| Uncarbonated s -> successFunc s
| Carbonated f -> failureFunc f
第二个文件的内容叫做Program.fs,它是:
open RailwayCombinator
let carbonate factor label i =
if i % factor = 0 then
carbonated label
else
uncarbonated i
let fizzBuzz =
let carbonateAll =
carbonate 3 "Fizz" <+> carbonate 5 "Buzz"
carbonateAll
我也有一个代码块:
let (<+>) switch1 switch2 x =
match (switch1 x),(switch2 x) with
| Carbonated s1,Carbonated s2 -> carbonated (s1 + s2)
| Uncarbonated f1,Carbonated s2 -> carbonated s2
| Carbonated s1,Uncarbonated f2 -> carbonated s1
| Uncarbonated f1,Uncarbonated f2 -> uncarbonated f1
如果我把代码块放在名为Program的文件中,它编译就好了。如果我把它放在RailwayCombinator中我在这一行得到一个错误。
carbonate 3 "Fizz" <+> carbonate 5 "Buzz"
错误是:
This expression was expected to have type
int
but here has type
string
我还注意到&lt; +&gt;的签名了。根据它所处的文件而改变,但我不知道签名改变的原因。在RailwayCombinator中的签名是:
val ( <+> ) :
switch1:('a -> Choice<'b,int>) ->
switch2:('a -> Choice<'c,int>) -> x:'a -> Choice<'b,int>
当它在程序中时,签名变为
val ( <+> ) :
switch1:('a -> Choice<'b,string>) ->
switch2:('a -> Choice<'c,string>) -> x:'a -> Choice<'b,string>
那么为什么签名会改变?
答案 0 :(得分:4)
<+>
组合子的实施使用+
运算符。 F#编译器不知道如何使这个泛型(.NET泛型没有通用约束,说类型应该是“带+运算符的任何东西”)。因此,F#编译器选择<+>
的第一个类型,具体取决于使用运算符的定义下面的第一段代码。
您可以通过内联定义来解决这个问题:
let inline (<+>) switch1 switch2 x =
match (switch1 x),(switch2 x) with
| Carbonated s1,Carbonated s2 -> carbonated (s1 + s2)
| Uncarbonated f1,Carbonated s2 -> carbonated s2
| Carbonated s1,Uncarbonated f2 -> carbonated s1
| Uncarbonated f1,Uncarbonated f2 -> uncarbonated f1
inline
由F#编译器直接处理,因此它们支持更强大的通用约束 - 包括一个带有“带+的任何东西”的约束。