开发者

Generic extraction from a constructor

In F# and 开发者_StackOverflow中文版OCaml I wind up writing a lot of code like

type C = Blah of Whatever  
let d = Blah (createWhatever ())  // so d is type C  
...  
let x = match d with | Blah b -> b

What I'd like is this

...  
let x = peel d

Where peel would work for any constructor/discriminator.

Surely I'm not the only one annoyed by this.

edit: Good answers, but I don't have the rep to vote on them. How about this situation?

member self.Length = match self with | L lab -> lab.Length


It is not possible to do that safely : if peel was a function, what would be its type ? It cannot be typed and therefore cannot be a "good guy" in the language.

You may :

  • use reflection (in F#) or type-breaking functions (in OCaml it's the Obj module), but you will get something unsafe with an imprecise type, so it's rather ugly and "use at your own risk"

  • use metaprogramming to generate different versions of peel at each type for you. For example, using the type-conv OCaml tool, you may have type blah = Blah of something define a function peel_blah implicitly, and type foo = Foo of something define peel_foo.

The better solution imho is... not to need such a peel in the first place. I see two possibilities:

  • You may use clever patterns instead of a function : by using let (Blah whatever) = f x, or fun (Blah whatever) -> ..., you don't need an unpacking function anymore.

  • Or you may, instead of writing type blah = Blah of what, write

    type blah = (blah_tag * whatever) and blah_tag = Blah

    This way, you don't have a sum type but a product type (you write (Blah, whatever)), and your peel is just snd. You still have a different (incompatible) type for each blah, foo etc, but a uniform access interface.


As mentioned, the let is convenient to do a pattern matching. If you want to access the value in the middle of an expression, where patterns are not allowed, I suggest adding a member to the types:

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


I would write this instead:

type C = Blah of Whatever  
let d = Blah (createWhatever ())  // so d is type C  
...
let (Blah x) = d

For your second situation, I like Laurent's member x.Value = match x with Blah v -> v.


Works for DUs...will need tweaking to work with class constructors:

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"

By the way: I wouldn't typically do something like this, but since you asked...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜