为什么在文件之间移动代码时类型会发生变化
Why do the types change when I move code between files
我有两个文件。第一个文件 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
我还注意到 <+> 的签名根据它所在的文件而改变,但我不知道为什么签名会改变。在 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>
为什么签名会改变?
<+>
组合子的实现使用 +
运算符。 F# 编译器不知道如何生成此泛型(.NET 泛型没有表明类型应为 "anything with + operator" 的泛型约束)。因此,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# 编译器直接处理,因此它们支持更强大的通用约束 - 包括表示 "anything with +".
的约束
我有两个文件。第一个文件 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
我还注意到 <+> 的签名根据它所在的文件而改变,但我不知道为什么签名会改变。在 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>
为什么签名会改变?
<+>
组合子的实现使用 +
运算符。 F# 编译器不知道如何生成此泛型(.NET 泛型没有表明类型应为 "anything with + operator" 的泛型约束)。因此,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# 编译器直接处理,因此它们支持更强大的通用约束 - 包括表示 "anything with +".