如何在F#模式匹配中隐式转换为常见的超类型?

Ric*_*ton 7 f# type-inference

问题摘要

在使用f#时,我必须明确地将值强制转换为其类型的父类型,以便使模式匹配表达式正确地进行类型检查.理想情况下,我会喜欢一种更简洁的方式.

假设我有一些类层次结构:

type Foo () =
    abstract member Value : unit -> string

type A (i:int) = 
    inherit Foo ()
        override this.Value () = i.ToString()

type B (s:string) = 
    inherit Foo ()
        override this.Value () = s
Run Code Online (Sandbox Code Playgroud)

理想情况下,在一般的编程语言中,我会编写以下内容:

let bar (i:int) : Foo =
    match i with
      | 1 -> B "one"
      | _ -> A i
Run Code Online (Sandbox Code Playgroud)

然而,这无法正确键入检查,给我错误,"这个表达式应该有类型Foo但这里有类型B".我不明白为什么编译器没有足够的信息来推断匹配表达式的公共超类型,然后检查公共超类型是否为'Foo'.

目前,我被迫为模式匹配中的每个案例提供明确的强制:

let bar2 (i:int) : Foo =
    match i with
      | 1 -> (B "one") :> Foo
      | _ -> (A i) :> Foo
Run Code Online (Sandbox Code Playgroud)

我想避免这个.

进一步说明

  • 直觉表明这是一个更普遍的问题的结果.我会想到,尽管像模式匹配这样常见的东西,或者也表现出相同属性的语句,都会有一个类型检查规则来解释常见的超类型.
  • 在任何人建议之前 - 我很欣赏如果A或B是对象表达式,这将起作用,但我真正的例子是创建C#类的实例,它们是普通类.
  • 有没有办法让我声明函数来隐式转换类型,比如scala有,所以我可以为我正在做这一代的模块应用自动转换?

感谢您对此事的任何帮助.

Bri*_*ian 7

我会用upcast,一个

[<AbstractClass>]
type Foo () = 
    abstract member Value : unit -> string 

type A (i:int) =  
    inherit Foo () 
    override this.Value () = i.ToString() 

type B (s) =  
    inherit Foo () 
    override this.Value () = s 

let bar2 i : Foo = 
    match i with 
    | 1 -> upcast B "one"
    | _ -> upcast A i
Run Code Online (Sandbox Code Playgroud)

你仍然需要将它添加到每个分支,但这通常比转换为类型更好,因为typename通常是20或30个字符long(MyNamespace.ThisThingy),而upcast只是6个字符.

但是,简而言之,语言规则不允许任何其他内容,所有分支的类型必须相同.

  • 类型检查器不使用派生类型执行任何恶作剧的原因是,首先它会使类型检查步骤更复杂,其次它会允许您引入细微的错误.例如,请考虑以下内容:将i与|匹配 1 - > B("一")| 2 - >"stuff"好吧,System.String和B共享公共基类型System.Object,因此该匹配表达式的结果是obj类型.这是99.9%不是你想要的. (2认同)

Tom*_*cek 5

我之前已经看过几次这个问题了,但我刚才意识到有一种有趣的方法来解决这个问题(没有任何重大的负面影响,比如大的运行时间开销).

您可以使用只有成员的非常简单的计算表达式Return.构建器将具有类型参数,并且Return将期望此类型的值.诀窍是,F#在调用成员时插入自动向上转换.这是宣言:

type ExprBuilder<'T>() =
  member x.Return(v:'T) = v

let expr<'T> = ExprBuilder<'T>()
Run Code Online (Sandbox Code Playgroud)

要编写一个返回任何内容的简单模式匹配obj,您现在可以编写:

let foo a = expr<obj> {
  match a with
  | 0 -> return System.Random()
  | _ -> return "Hello" }
Run Code Online (Sandbox Code Playgroud)

您仍然必须明确返回类型(在创建计算表达式时),但我发现语法非常整洁(但它绝对是一个棘手的用法,可能会让那些第一次看到它的人感到困惑).