Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Equivalent of Scala "case class" in F#

Tags:

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#?

like image 354
SRKX Avatar asked Jun 15 '11 08:06

SRKX


People also ask

What is a Scala case class?

Scala case classes are just regular classes which are immutable by default and decomposable through pattern matching. It uses equal method to compare instance structurally. It does not use new keyword to instantiate object. All the parameters listed in the case class are public and immutable by default.

What is case _ in Scala?

case _ => does not check for the type, so it would match anything (similar to default in Java). case _ : ByteType matches only an instance of ByteType . It is the same like case x : ByteType , just without binding the casted matched object to a name x .

What is difference between class and case class in Scala?

A class can extend another class, whereas a case class can not extend another case class (because it would not be possible to correctly implement their equality).

Can case class have methods?

case classes automatically have equality and nice toString methods based on the constructor arguments. case classes can have methods just like normal classes.


2 Answers

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.

like image 54
Yin Zhu Avatar answered Sep 20 '22 04:09

Yin Zhu


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

like image 45
Brian Avatar answered Sep 23 '22 04:09

Brian