当我在文件之间移动代码时,为什么类型会发生变化

时间:2015-04-28 12:46:46

标签: f# inline

我有两个文件。第一个文件的内容名为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>

那么为什么签名会改变?

1 个答案:

答案 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#编译器直接处理,因此它们支持更强大的通用约束 - 包括一个带有“带+的任何东西”的约束。