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
Objmodule), 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
peelat each type for you. For example, using the type-conv OCaml tool, you may havetype blah = Blah of somethingdefine a functionpeel_blahimplicitly, andtype foo = Foo of somethingdefinepeel_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, orfun (Blah whatever) -> ..., you don't need an unpacking function anymore.Or you may, instead of writing
type blah = Blah of what, writetype blah = (blah_tag * whatever) and blah_tag = BlahThis way, you don't have a sum type but a product type (you write
(Blah, whatever)), and yourpeelis justsnd. You still have a different (incompatible) type for eachblah,fooetc, 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...
加载中,请稍侯......
精彩评论