F#将已区分的并集转换为字符串 [英] F# Convert 'a discriminated union to string

查看:75
本文介绍了F#将已区分的并集转换为字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将已区分的并集转换为字符串,但是我不明白为什么此代码无法正常工作.

I'm trying to convert a discriminated union to string but I don't understand why this code is not working.

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

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

这是fsharp交互式输出

This is the fsharp interactive output

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

推荐答案

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

There are two errors here: function application syntax and lost genericity.

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

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

In the expression sampleTypeToString A(2), you actually have three terms, not two:

  1. sampleTypeToString
  2. A
  3. (2)
  1. sampleTypeToString
  2. A
  3. (2)

不要让A(2)之间缺少空格会蒙骗您.某种程度上,这些不被视为一个表达". A(2)是单独的术语.

Don't let the lack of a space between A and (2) fool you. These are not considered, somehow, as "one expression". A and (2) are separate terms.

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

Therefore, the whole expression sampleTypeToString A(2) is being interpreted as function sampleTypeToString applied to two arguments - A and (2). This, of course, doesn't work, because sampleTypeToString takes only one argument, and term A doesn't fit, because it's of the wrong type.

最简单的解决方法是在应首先评估的内容上加上括号:

The simplest way to fix it is to just put parentheses around what should be evaluated first:

   sampleTypeToString (A(2))

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

And of course, since function (or constructor) application in F# doesn't actually require parentheses by itself, you can drop the first set:

   sampleTypeToString (A 2)

或者,您可以使用管道:

Alternatively, you can use a pipe:

   sampleTypeToString <| A(2)

之所以行之有效,是因为管道运算符的优先级低于函数应用程序的优先级(是所有应用程序中最高的),因此先对A(2)求值,然后才将其传递给sampleTypeToString.

This works, because the pipe operator has lower precedence than function application (which is the highest of all), so that A(2) gets evaluated first, and only then piped into sampleTypeToString.

这与错误" expected obj,但这里的类型为int "

This has to do with the error "expected obj, but here has type int"

这有点棘手.看看您如何在sampleTypeToString中使用string函数?该功能在技术上是通用的,但不是常规方式.它使用静态解析类型约束.无需赘述,这基本上意味着必须在编译时知道参数的具体类型.

This one is a bit trickier. See how you're using the string function within sampleTypeToString? That function is technically generic, but not in the regular way. It uses statically resolved type constraints. Without going into too much detail, this basically means that the concrete type of the argument has to be known at compile time.

但是函数sampleTypeToString接受通用类型sampleType<'a>的参数,因此,当调用string时,它将传递类型'a的参数.但是string不能那样工作:它需要知道具体的类型,不能是通用的'a.因此,编译器会尽力替代具体类型.因为它实际上不知道'a的含义,所以它采用了最笼统的假设obj.

But your function sampleTypeToString takes a parameter of generic type sampleType<'a>, and thus when it calls string, it passes the argument of type 'a. But string can't work like that: it needs to know the concrete type, can't be generic 'a. So the compiler tries its best to substitute a concrete type. Because it knows literally nothing of what 'a could be, it goes with the most general assumption obj.

结果,您的函数sampleTypeToString实际上最终接受了类型为sampleType<obj>的参数,而不是您期望的sampleType<'a>类型.

As a result, your function sampleTypeToString actually ends up taking parameter of type sampleType<obj>, not sampleType<'a> as you might expect.

解决方案?声明您充当inline.这将告诉编译器不要将其实际编译为.NET方法,而应将其定义扩展到任何被调用的地方(有点类似于C中的DEFINE或C ++中的模板).这样,'a类型将始终在编译时就已知,并且可以满足选择函数string.

The solution? Declare you function as inline. This will tell the compiler not to actually compile it as a .NET method, but rather expand its definition wherever it's called (somewhat similar to a DEFINE in C or a template in C++). This way, the type 'a will always be known at compile time, and the picky function string would be satisfied.

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:

Alternatively, you could box the argument of string, turning it into obj:

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

但是然后您会稍微更改string的语义,因为它具有

But then you'd slightly change the semantics of string, for it has special processing for certain types, converting to string in culture-invariant way. If you box the argument, it will basically always fall back to obj.ToString(). Plus, you'd suffer an extra heap allocation for the boxed value.

或者,您也可以取消string并自己致电.ToString():

Even more alternatively, you could do away with string and just call .ToString() yourself:

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

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

Keep in mind that this is not exactly the same as calling string.

这篇关于F#将已区分的并集转换为字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆