Ответ 1
Как упоминает Брайан, существует два способа сопоставления шаблонов: 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 может быть переведена на дискриминированные объединения (DU) в F #:
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
Теперь возникают различия:
-
вы не можете определить методы по своим подтипам:
Var
,Fun
иApp
. -
методы, которые вы можете определить на
Term
, неизменяемы. -
невозможно расширить DU после его определения. Подумайте о том, что теперь вам нужно добавить подтекст
For
вTerm
. Затем вам нужно изменить много кода, гдеTerm
соответствует шаблону. -
в то время как в дизайне оо, это меньше проблема. потому что новый подтип может нести свои собственные реализации.
В F #, DU следует сначала рассмотреть, когда вы хотите создать сжатый тип соответствия по подтипам. Но он также имеет очевидные ограничения. Я думаю, что сопоставление шаблонов действий больше соответствует классу case в Scala (я немного читаю 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
Активность patten сочетает в себе хорошие вещи с обеих сторон: функциональное программирование и объектно-ориентированное.
исх. здесь и здесь для шаблонов действий.
Вы можете также ссылаться на оригинальную бумагу по активному шаблону дона Симэ.