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#?
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:
you cannot define methods on its subtypes: Var
, Fun
, and App
.
the methods you can define on Term
are immutable.
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.
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.
Discriminated unions? You can add member methods to them. Alternatively you can use active patterns on an existing class.