继承 – 使用管道或组合的类型推断失败,正常函数调用成功

我现在很少用F#进行这种斗争,但是再次类型继承与F#相比并不常见,所以也许我只是幸运.或者我错过了显而易见的事实.通常当编译器抱怨不知道某种类型时,我会反转管道或合成操作数的顺序,而且我已经完成了.

基本上,给定一个函数调用,作为g(f x),它也可以作为x |> f |> g或(f> g)x.但今天它不……

这是一个凌乱的概念证明我的意思:

module Exc =
    open System

    type MyExc(t) = inherit Exception(t)

    let createExc t = new MyExc(t)
    type Ex = Ex of exn
    type Res = Success of string | Fail of Ex with
        static member createRes1 t = Ex(createExc(t)) |> Fail   // compiled
        static member createRes2 t =  t |> createExc |> Ex |> Fail  // FS0001
        static member createRes3 = createExc >> Ex >> Fail   // FS0001

通常,这是有效的(至少根据我的经验). “失败”抛出的线条:

error FS0001: Type mismatch. Expecting a MyExc -> ‘a but given a exn -> Ex. The type ‘MyExc’ does not match the type ‘exn’

没什么大不了的,不是很难解决,但我碰巧要编写很多代码,其中组合是更简单/更清晰的方法,我不想编写一堆我必须放在任何地方的实用函数.

我看了灵活的类型,因为我猜这是一个逆变问题,但我不知道如何在这里应用它.保持这种惯用的任何想法?

注意,如果我重新排列,即Ex<< createExc>>失败或使用管道反向运算符我最终在不同的部分上出现相同的错误.

在这种情况下,F#编译器的行为有点不规则.在您的示例中,您希望将MyExc类型的值传递给期望exn的构造函数.将对象作为其基类的值处理是一种有效的coersion,但F#编译器仅在非常有限的位置插入这些coersions.

特别是,它在向函数传递参数时插入coersion,但在创建列表或从函数返回结果时不会插入它们(例如).

在您的示例中,在将值传递给有区别的union构造函数时,您需要一个coersion.似乎只有在直接创建union情况时才会发生这种情况,但在将union情况视为函数时不会发生这种情况:

// foo is a function that takes `obj` and Foo is a DU case that takes `obj`
let foo (o:obj) = o
type Foo = Foo of obj

foo(System.Random()) // Coersion inserted automatically
Foo(System.Random()) // Coersion inserted automatically

System.Random() |> foo // Coersion inserted automatically
System.Random() |> Foo // ..but not here!

因此,F#编译器自动应用coersions的有限位置包括各种调用函数的方法,但只能直接创建DU情况.

这是一个有点滑稽的行为 – 我认为将DU案例作为普通函数处理是有意义的,包括在使用|>时自动插入coersions,但我不确定是否有任何技术原因使得硬.

相关文章
相关标签/搜索