Abe*_*bel 7 inheritance f# contravariance piping function-composition
我现在很少用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
Run Code Online (Sandbox Code Playgroud)
通常,这是有效的(至少根据我的经验)."失败"抛出的线条:
错误FS0001:类型不匹配.期待一个MyExc - >'a但是给了一个exn - > Ex."MyExc"类型与"exn"类型不匹配
没什么大不了的,不是很难解决,但我碰巧要编写很多代码,其中组合是更简单/更清晰的方法,我不想编写一堆我必须放在任何地方的实用函数.
我看了灵活的类型,因为我猜这是一个逆变问题,但我不知道如何在这里应用它.保持这种惯用的任何想法?
注意,如果我重新排列,即作为Ex << createExc >> Fail或使用管道后向运算符,我最终会在不同的部分上出现相同的错误.
在这种情况下,F#编译器的行为有点不规则.在您的示例中,您希望将type的值传递给MyExc期望的构造函数exn.将对象作为其基类的值处理是有效的coersion,但F#编译器仅在非常有限的位置插入此类coersion.
特别是,它在向函数传递参数时插入coersion,但在创建列表或从函数返回结果时不插入它们(例如).
在您的示例中,在将值传递给有区别的联合构造函数时,您需要一个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!
Run Code Online (Sandbox Code Playgroud)
因此,F#编译器自动应用coersions的有限位置包括调用函数的各种方式,但只能直接创建DU情况.
这是一个有点滑稽的行为 - 我认为将DU案件作为普通函数处理是有意义的,包括在你使用时自动插入coersions |>,但我不确定是否有任何技术原因使得这很难.