【问题标题】:F# Convert 'a discriminated union to stringF#将'一个可区分的联合转换为字符串
【发布时间】:2017-04-05 22:24:02
【问题描述】:

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

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

【问题讨论】:

  • 你需要box这个:sampleTypeToString (A(box(6)))
  • 这是使这个特定示例工作的快捷方式,但您必须同意这不是预期的。
  • 不,这就是为什么它只是一个评论:) 但我想知道这是否可以在没有内联的情况下完成。
  • 是的,可以。我的回答有两个选项。

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


【解决方案1】:

这里有两个错误:函数应用语法和失去通用性。

函数应用语法

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

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

  1. sampleTypeToString
  2. A
  3. (2)

不要让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

失去通用性

这与错误“预期为obj,但这里有类型int”有关。

这个有点棘手。看看你是如何在sampleTypeToString 中使用string 函数的?该功能在技术上是通用的,但不是常规方式。它使用statically resolved type constraints。无需过多详细说明,这基本上意味着参数的具体类型必须在编译时知道。

但是你的函数sampleTypeToString 接受一个泛型类型sampleType&lt;'a&gt; 的参数,因此当它调用string 时,它传递了'a 类型的参数。但是string 不能这样工作:它需要知道具体的类型,不能是通用的'a。所以编译器会尽力替换一个具体的类型。因为它实际上对 'a 可能是什么一无所知,所以它符合最普遍的假设 obj

因此,您的函数 sampleTypeToString 实际上最终会采用 sampleType&lt;obj&gt; 类型的参数,而不是您可能期望的 sampleType&lt;'a&gt;

解决方案?声明你的功能是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 不同完全

【讨论】:

  • 这并不能完全解决问题,结合@s952163解决工作
  • 是的,我正在更新它。写得太多了,一口气写不完。但请注意@s952163 的解决方案实际上并没有给您想要的东西。将数字装箱会将其从int 变为obj,这样您就失去了拥有sampleType 通用的全部意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-25
  • 1970-01-01
  • 2017-11-29
  • 2018-04-30
  • 2011-11-19
  • 1970-01-01
  • 2020-01-19
相关资源
最近更新 更多