2010-10-11 69 views
7

使用F#,当我必须明确地强迫为了获得模式匹配表达式来键入正确的检查值,其类型的父类问题摘要如何隐式转换为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 

理想的情况下,并在正常一些编程语言,我会写下面的等价:

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

但是这不能正确地输入检查,给我错误,“这个表达式预计有Foo类型,但她e有B型“。我不明白为什么编译器没有足够的信息来推断匹配表达式的公共超类型,然后检查公共超类型是'Foo'。

目前我被迫在模式匹配任何情况下提供一个明确的威逼:

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

我想避免这种情况。

其它注意事项

  • 直觉认为,这是一个比较普遍的问题的结果。我会想,虽然模式匹配这样的事情,或者如果语句也表现出相同的属性,会有一个类型检查规则来说明常见的超类型。
  • 之前有人建议 - 我明白,如果A或B是对象表达式这将工作,但我真正的例子是创建C#类的实例,他们是正常的类。
  • 有没有一种方法可以让我声明隐式转换类型的函数,例如scala已经有了,所以我可以将自动转换应用到我在做这一代的模块中?

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

回答

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 

你还必须将它添加到每一个分支,但这往往是最好铸造的类型,因为常常类型名称就像是20或30个字符长(MyNamespace.ThisThingy),而upcast只是6个字符。

但是,简言之,语言规则不允许其他任何事情,所有分支的类型必须相同。

+2

类型检查器没有对派生类型进行任何欺骗的原因是,首先它会使类型检查步骤更加复杂,其次它会允许您引入细微的错误。例如,请考虑以下内容: 与我匹配 | 1 - > B(“one”) | 2 - >“stuff” 好吧,System.String和B共享公共基类型System.Object,所以匹配表达式的结果是obj类型。这是99.9%不是你想要的。 – 2010-10-11 20:00:45

+1

的确如此,尽管在OP所询问的具体实例中,他已经将函数返回类型声明为“:Foo”,所以我认为推理者也许并不完全不合理地将其用作最大值较低的值,绑定什么的。尽管如此,我还没有考虑过所有的影响。 – Brian 2010-10-11 20:04:32

+1

我认为'x:> _'比'upcast x'读得更好,但也许这就是我。不过,这场比赛的分支并不一致。 – kvb 2010-10-11 22:08:15

5

我以前曾经见过这个问题,但我只是意识到有一个相当有趣的方法来解决这个问题(没有任何重大的负面影响,如大的运行时间开销)。

您可以使用一个非常简单的计算表达式,它只有Return成员。构建器将有一个类型参数,并且Return将会期望这种类型的值。诀窍在于,F#确实在调用成员时插入了自动向上播放器。以下是声明:

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

let expr<'T> = ExprBuilder<'T>() 

写一个简单的模式匹配,返回什么为obj,你可以这样写:

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

你还是要明确有关的返回类型(在创建时计算表达式),但是我觉得语法非常整洁(但它绝对是一个非常棘手的用法,可能会让第一次看到它的人感到困惑)。

+0

这是一个有趣的解决方案,但我仍然必须在模式匹配的每一行都有语法。尽管如此 - 这绝对是我见过的最好的解决方案! – 2010-10-11 23:15:36

+2

-1,在我看来,这太“聪明”了。 – Brian 2010-10-11 23:27:39

+0

@Brian:我会把它当作赞美:-),但你说的对,它可能太棘手了(我在最后一句中写到了),尽管它总是取决于项目的类型 - 它是一些C#/ F#管道代码或复杂F#系统的核心部分?在某些情况下,应该允许棘手的解决方案:-)。毕竟,同步工作流也很棘手(从某种意义上说)。 – 2010-10-12 00:26:18