在f#和OCaml中,我写了很多像
这样的代码type C = Blah of Whatever
let d = Blah (createWhatever ()) // so d is type C
...
let x = match d with | Blah b -> b
我想要的是这个
...
let x = peel d
对于任何构造函数/鉴别符,peel都可以工作。
当然,我不是唯一一个为此烦恼的人。
编辑:回答得好,但我没有代表投票。这种情况呢?
member self.Length = match self with | L lab -> lab.Length
这是不可能安全地做到的:如果peel
是一个函数,它的类型是什么?它不能被输入,因此不能成为语言中的"好人"。
你可以:
-
使用反射(在f#中)或类型分解函数(在OCaml中是
Obj
模块),但您将获得不精确类型的不安全的东西,因此它相当丑陋和"使用风险自负" -
使用元编程为您生成每种类型的不同版本的
peel
。例如,使用type-conv OCaml工具,您可以让type blah = Blah of something
隐式定义peel_blah
函数,type foo = Foo of something
定义peel_foo
函数。
我认为更好的解决办法是……首先不需要这样的peel
。我看到两种可能:
-
你可以使用聪明的模式来代替函数:通过使用
let (Blah whatever) = f x
或fun (Blah whatever) -> ...
,你不再需要解包函数了。 -
或者你可以不写
type blah = Blah of what
,而写type blah = (blah_tag * whatever) and blah_tag = Blah
这样,你就没有sum类型,而是product类型(你写
(Blah, whatever)
),而你的peel
只是snd
。你仍然有一个不同的(不兼容的)类型为每个blah
,foo
等,但统一的访问接口。
如前所述,let
便于进行模式匹配。如果您想访问表达式中间的值,而不允许使用模式,我建议在类型中添加一个成员:
type C = Blah of int
with member c.Value = match c with Blah x -> x
let x = Blah 5
let y = Blah 2
let sum = x.Value + y.Value
我会这样写:
type C = Blah of Whatever
let d = Blah (createWhatever ()) // so d is type C
...
let (Blah x) = d
对于第二种情况,我喜欢Laurent的member x.Value = match x with Blah v -> v
。
适用于DUs…需要调整以使用类构造函数:
open Microsoft.FSharp.Reflection
let peel d =
if obj.ReferenceEquals(d, null) then nullArg "d"
let ty = d.GetType()
if FSharpType.IsUnion(ty) then
match FSharpValue.GetUnionFields(d, ty) with
| _, [| value |] -> unbox value
| _ -> failwith "more than one field"
else failwith "not a union type"
顺便说一下:我通常不会做这样的事情,但是既然你问了…