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 havetype blah = Blah of something
define a functionpeel_blah
implicitly, andtype foo = Foo of something
definepeel_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 = Blah
This way, you don't have a sum type but a product type (you write
(Blah, whatever)
), and yourpeel
is justsnd
. You still have a different (incompatible) type for eachblah
,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...
精彩评论