Scala“案例类”的等效项在 F# 中

发布于 2024-11-16 00:44:54 字数 207 浏览 1 评论 0原文

我正在寻找 F# 中 Scala 中可用的“案例类”的等效项。

当您想要使用方法和字段创建自定义类并且仍然能够将它们与模式匹配一​​起使用时,案例类非常有用,如此 Scala 网站的文章

有谁知道 F# 中是否存在同样的情况?

I am looking for the equivalent in F# of "case classes" that are available in Scala.

Cases classes are very useful when you want to create custom classes with methods and fields and still be able to use them with pattern matching, as described in this article of the Scala website.

Does anyone know if the same exists in F#?

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

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

发布评论

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

评论(2

无法言说的痛 2024-11-23 00:44:54

正如 Brian 提到的,模式匹配有两种方法:1. 区分联合和 2. 现有类型上的活动模式。

让我们从这个 Scala 示例开始:

abstract class Term
case class Var(name: String) extends Term
case class Fun(arg: String, body: Term) extends Term
case class App(f: Term, v: Term) extends Term

这个 OO 设计可以转换为 F# 中的可区分联合 (DU):

type Term = 
    Var of string 
    | Fun of string * Term 
    | App of Term * Term

基于此 DU,您可以匹配 Term 值来查找它是什么子类型:

let eval (t: Term) = 
    match t with
    | Var (name) -> ...
    | Fun (para, body) -> ...
    | App (t1, t2) -> ...

请注意,您可以在此 Term 类型上定义方法和属性:

type Term = 
    Var of string 
    | Fun of string * Term 
    | App of Term * Term
    with 
    member x.Type() = 
        match x with
        | Var _ -> 0
        | Fun _ -> 1
        | App _ -> 2

现在差异如下:

  1. 您无法在其子类型上定义方法:VarFun,和应用程序

  2. 您可以在 Term 上定义的方法是不可变的。

  3. 一旦定义了 DU,就无法对其进行扩展。考虑一下您现在需要向 Term 添加一个 For 子类型。然后您必须更改大量 Term 模式匹配的代码。

  4. 而在面向对象设计中,这不是一个问题。因为新的子类型可以携带它自己的实现。

在 F# 中,当您想要在子类型上构建简洁的类型匹配时,应首先考虑 DU。但它也有明显的限制。我认为活动模式匹配更等于Scala中的case类(我只读了一点Scala):

// define the classes for different term types
[<AbstractClass>]
type Term() = 
    abstract Value: int with get

type Var(name:string) =
    inherit Term()
    override x.Value = 
        0
    member x.Name with get() = name

type Fun(name:string, body:Term) = 
    inherit Term()
    override x.Value = 
        0
    member x.Name with get() = name
    member x.Body with get() = body


type App(t1:Term, t2:Term) = 
    inherit Term()
    override x.Value = 
        0    
    member x.Term1 with get() = t1
    member x.Term2 with get() = t2

// the pattern function 
let (|TVar|TFun|TApp|) (x:Term) = 
    match x with
    | :? Var -> 
        let y = x :?> Var
        TVar(y.Name)
    | :? Fun -> 
        let y = x :?> Fun
        TFun(y.Name, y.Body)
    | :? App ->
        let y = x :?> App
        TApp(y.Term1, y.Term2)

以及使用活动模式的eval函数:

let eval2 (t:Term) = 
    match t with
    | TVar (name) -> 0
    | TFun (name, body) -> 0
    | TApp (t1, t2) -> 0

Activity patten结合了双方的优点:函数式编程和面向对象。

参考号此处此处了解活动模式。

您可以进一步参考 Don Syme 的有关活动模式的原始论文

As Brian mentions, there are two ways for pattern matching: 1. Discriminated unions and 2. active pattern on an existing type.

Let's start from this Scala example:

abstract class Term
case class Var(name: String) extends Term
case class Fun(arg: String, body: Term) extends Term
case class App(f: Term, v: Term) extends Term

This OO design could be translated to discriminated unions (DU) in F#:

type Term = 
    Var of string 
    | Fun of string * Term 
    | App of Term * Term

Base on this DU, you can matching a Term value to find what subtype it is:

let eval (t: Term) = 
    match t with
    | Var (name) -> ...
    | Fun (para, body) -> ...
    | App (t1, t2) -> ...

Notice that you can have methods and properties defined on this Term type:

type Term = 
    Var of string 
    | Fun of string * Term 
    | App of Term * Term
    with 
    member x.Type() = 
        match x with
        | Var _ -> 0
        | Fun _ -> 1
        | App _ -> 2

Now here comes the differences:

  1. you cannot define methods on its subtypes: Var, Fun, and App.

  2. the methods you can define on Term are immutable.

  3. it is not possible to extend a DU once it is defined. Think about you now need to add a For subtype to Term. Then you have to change a lot of code where a Term is pattern matched.

  4. while in oo design, it is less a problem. because the new subtype could carry its own implementations.

In F#, DU should be first considered when you want to build succinct type matching over subtypes. But it also has obvious restrictions. I think activity pattern matching is more equal to the case class in Scala (I only read a little Scala):

// define the classes for different term types
[<AbstractClass>]
type Term() = 
    abstract Value: int with get

type Var(name:string) =
    inherit Term()
    override x.Value = 
        0
    member x.Name with get() = name

type Fun(name:string, body:Term) = 
    inherit Term()
    override x.Value = 
        0
    member x.Name with get() = name
    member x.Body with get() = body


type App(t1:Term, t2:Term) = 
    inherit Term()
    override x.Value = 
        0    
    member x.Term1 with get() = t1
    member x.Term2 with get() = t2

// the pattern function 
let (|TVar|TFun|TApp|) (x:Term) = 
    match x with
    | :? Var -> 
        let y = x :?> Var
        TVar(y.Name)
    | :? Fun -> 
        let y = x :?> Fun
        TFun(y.Name, y.Body)
    | :? App ->
        let y = x :?> App
        TApp(y.Term1, y.Term2)

and the eval function using active pattern:

let eval2 (t:Term) = 
    match t with
    | TVar (name) -> 0
    | TFun (name, body) -> 0
    | TApp (t1, t2) -> 0

Activity patten combines the good things on both sides: functional programming and object oriented.

ref. here and here for activity patterns.

You can further refer to the original paper on active pattern by Don Syme.

入怼 2024-11-23 00:44:54

受歧视的工会?您可以向它们添加成员方法。或者,您可以在现有类上使用活动模式。

Discriminated unions? You can add member methods to them. Alternatively you can use active patterns on an existing class.

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