从构造函数中通用提取

发布于 11-01 10:44 字数 438 浏览 5 评论 0原文

在 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

,其中剥离适用于任何构造函数/判别器。
当然我不是唯一对此感到恼火的人。
编辑: 很好的答案,但我没有代表对它们进行投票。 这种情况怎么样?

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

In F# and 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

眼前雾蒙蒙2024-11-08 10:44:28

不可能安全地做到这一点:如果 peel 是一个函数,它的类型是什么?它无法输入,因此不能成为该语言中的“好人”。

您可以:

  • 使用反射(在 F# 中)或类型破坏函数(在 OCaml 中是 Obj 模块),但是您会得到不精确类型的不安全内容,因此它相当难看并且“使用时请自行承担风险”

  • 使用元编程为每种类型生成不同版本的peel。例如,使用 type-conv OCaml 工具,您可能有 < code>type blah = Blah of Something 隐式定义函数 peel_blah,并且 type foo = Foo of Something 定义 peel_foo。< /p>

恕我直言,更好的解决方案是......首先不需要这样的剥离。我看到两种可能性:

  • 您可以使用巧妙的模式而不是函数:通过使用 let (Blahwhat) = f xfun (Blahwhat) -> ...,您不再需要解包函数。

  • 或者你可以不写type blah = Blah of what,而是写

    输入 blah = (blah_tag * 随便) 和 blah_tag = Blah

    这样,您就没有总和类型,而是产品类型(您编写(Blah,whatever)),并且您的peel只是snd。对于每个 blahfoo 等,您仍然有不同的(不兼容)类型,但有一个统一的访问接口。

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.

不喜欢何必死缠烂打2024-11-08 10:44:28

如前所述,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

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
紫南2024-11-08 10:44:28

我会这样写:

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

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.

伴我老2024-11-08 10:44:28

适用于 DU...需要调整才能与类构造函数一起使用:

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"

顺便说一句:我通常不会做这样的事情,但既然你问了...

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...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文