F#将一个有区别的联合转换为字符串

时间:2017-04-05 13:42:17

标签: f# f#-interactive discriminated-union

我试图将受歧视的联盟转换为字符串,但我不明白为什么这段代码无效。

type 'a sampleType =
 | A of 'a
 | B of 'a

let sampleTypeToString x =
 match x with
 | A (value) -> string value
 | B (value) -> string value

这是fsharp交互式输出

sampleTypeToString A(2);;
 Stopped due to error
 System.Exception: Operation could not be completed due to earlier error
 Successive arguments should be separated by spaces or tupled, and arguments involving function or method applications should be parenthesized at 3,19
 This expression was expected to have type
     'obj'    
 but here has type
     'int'     at 3,21

1 个答案:

答案 0 :(得分:7)

这里有两个错误:函数应用程序语法和丢失的通用性。

功能应用程序语法

这是错误" 函数参数应该用空格或元组分隔...... "

在表达式sampleTypeToString A(2)中,您实际上有三个术语,而不是两个:

  1. sampleTypeToString
  2. A
  3. (2)
  4. 不要让A(2)之间缺少空格来欺骗你。不知何故,这些不被视为"一个表达式"。 A(2)是单独的条款。

    因此,整个表达式sampleTypeToString A(2)被解释为应用于两个参数的函数sampleTypeToString - A(2)。当然,这不起作用,因为sampleTypeToString只接受一个参数,而术语A不合适,因为它的类型错误。

    解决问题的最简单方法是将括号放在应首先评估的内容之外:

       sampleTypeToString (A(2))
    

    当然,由于F#中的函数(或构造函数)应用程序实际上并不需要括号,因此可以删除第一组:

       sampleTypeToString (A 2)
    

    或者,您可以使用管道:

       sampleTypeToString <| A(2)
    

    这是有效的,因为管道运算符的优先级低于函数应用程序(这是最高的),因此A(2)首先得到评估,然后只传送到sampleTypeToString

    失去了通用性

    这与错误&#34; 预期obj有关,但此处的类型为int &#34;

    这个有点棘手。了解您如何使用string中的sampleTypeToString功能?该功能在技术上是通用的,但不是常规的。它使用statically resolved type constraints。没有太多细节,这基本上意味着必须在编译时知道参数的具体类型。

    但是你的函数sampleTypeToString采用泛型类型sampleType<'a>的参数,因此当它调用string时,它会传递类型为'a的参数。但是string不能这样做:它需要知道具体类型,不能是通用'a。因此编译器会尽力替换具体类型。因为它几乎不知道'a可能是什么,所以它与最常见的假设obj一致。

    因此,您的函数sampleTypeToString实际上最终会获得sampleType<obj>类型的参数,而不是您所期望的sampleType<'a>

    解决方案?声明你的身份为inline。这将告诉编译器不要将它实际编译为.NET方法,而是将其定义扩展到它被调用的任何地方(有点类似于C中的DEFINE或C ++中的模板)。这样,类型'a将始终在编译时知道,并且将满足挑剔函数string

    let inline sampleTypeToString x =
     match x with
     | A (value) -> string value
     | B (value) -> string value
    
    sampleTypeToString (A 2)
    sampleTypeToString (B "abc")
    sampleTypeToString (A true)
    

    或者,您可以设置string的参数,将其转换为obj

    let sampleTypeToString x =
     match x with
     | A (value) -> string (box value)
     | B (value) -> string (box value)
    

    但是你稍微改变了string的语义,因为它有special processing for certain types,以文化不变的方式转换为字符串。如果你打开参数,它基本上总是回到obj.ToString()。另外,您为盒装值额外分配了堆。

    更重要的是,您可以取消string并自己致电.ToString()

    let sampleTypeToString x =
     match x with
     | A (value) -> value.ToString()
     | B (value) -> value.ToString()
    

    请注意,这与 完全与调用string相同。