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

Doc*_*cLM 3 f# discriminated-union f#-interactive

我正在尝试将一个有区别的联合转换为字符串,但我不明白为什么这个代码不起作用.

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

let sampleTypeToString x =
 match x with
 | A (value) -> string value
 | B (value) -> string value
Run Code Online (Sandbox Code Playgroud)

这是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
Run Code Online (Sandbox Code Playgroud)

Fyo*_*kin 7

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

功能应用语法

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

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

  1. sampleTypeToString
  2. A
  3. (2)

不要让缺乏空间A(2)欺骗你.不知何故,这些都不被视为"一个表达".A并且(2)是单独的条款.

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

修复它的最简单方法是将括号放在应该首先评估的内容之外:

   sampleTypeToString (A(2))
Run Code Online (Sandbox Code Playgroud)

当然,由于F#中的函数(或构造函数)应用程序本身并不需要括号,因此可以删除第一个集合:

   sampleTypeToString (A 2)
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用管道:

   sampleTypeToString <| A(2)
Run Code Online (Sandbox Code Playgroud)

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

失去了通用性

这与错误" 预期obj,但这里有类型int "有关

这个有点棘手.看看你如何使用这个string功能sampleTypeToString?该功能在技术上是通用的,但不是常规的.它使用静态解析的类型约束.没有太多细节,这基本上意味着必须在编译时知道参数的具体类型.

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

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

解决方案?声明你的功能inline.这将告诉编译器不要将它实际编译为.NET方法,而是在其调用的任何地方扩展其定义(有点类似于DEFINEC中的C或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)
Run Code Online (Sandbox Code Playgroud)

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

let sampleTypeToString x =
 match x with
 | A (value) -> string (box value)
 | B (value) -> string (box value)
Run Code Online (Sandbox Code Playgroud)

但是你稍微改变了string它的语义,因为它对某些类型特殊处理,以文化不变的方式转换为字符串.如果您打开参数,它将基本上总是回落obj.ToString().另外,您将为盒装值额外分配堆.

更可替代的是,你可以取消string并自称.ToString():

let sampleTypeToString x =
 match x with
 | A (value) -> value.ToString()
 | B (value) -> value.ToString()
Run Code Online (Sandbox Code Playgroud)

请记住,这与调用不完全相同string.

  • 是的,我正在更新它.写得太多,无法一次性发布.但请注意,@ s952163的解决方案实际上并没有给你你想要的东西.拳击数字从"int"变为"obj",这样你就失去了`sampleType`泛型的全部意义. (2认同)