Equivalent of Scala “case class” in F#

前端 未结 2 1878
猫巷女王i
猫巷女王i 2021-02-04 01:57

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

2条回答
  •  小鲜肉
    小鲜肉 (楼主)
    2021-02-04 02:21

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

提交回复
热议问题