Equivalente de Scala "clase de caso" en F#
functional-programming (2)
¿Sindicatos discriminados? Puede agregar métodos de miembro a ellos. Alternativamente, puedes usar patrones activos en una clase existente.
Estoy buscando el equivalente en F # de "clases de casos" que están disponibles en Scala.
Las clases de casos son muy útiles cuando desea crear clases personalizadas con métodos y campos y aún así poder usarlas con la coincidencia de patrones, como se describe en este article del sitio web de Scala.
¿Alguien sabe si existe lo mismo en F #?
Como lo menciona Brian, hay dos formas de hacer coincidir patrones: 1. Uniones discriminadas y 2. patrón activo en un tipo existente.
Empecemos por este ejemplo de 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
Este diseño OO se podría traducir a uniones discriminadas (DU) en F #:
type Term =
Var of string
| Fun of string * Term
| App of Term * Term
Sobre la base de este DU, puede hacer coincidir un valor de Term
para encontrar qué subtipo es:
let eval (t: Term) =
match t with
| Var (name) -> ...
| Fun (para, body) -> ...
| App (t1, t2) -> ...
Tenga en cuenta que puede tener métodos y propiedades definidos en este tipo de 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
Ahora aquí vienen las diferencias:
no puede definir métodos en sus subtipos:
Var
,Fun
yApp
.Los métodos que puedes definir en
Term
son inmutables.no es posible extender un DU una vez que se define. Piense en usted ahora necesita agregar un subtipo
For
aTerm
. Entonces tienes que cambiar una gran cantidad de código donde unTerm
es un patrón coincidente.Mientras que en el diseño oo, es menos un problema. Porque el nuevo subtipo podría llevar sus propias implementaciones.
En F #, DU debe considerarse primero cuando se desea crear una concordancia de tipo sucinta sobre los subtipos. Pero también tiene restricciones obvias. Creo que la coincidencia de patrones de actividad es más igual a la clase de casos en Scala (solo leo un poco de 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)
y la función eval
utilizando un patrón activo:
let eval2 (t:Term) =
match t with
| TVar (name) -> 0
| TFun (name, body) -> 0
| TApp (t1, t2) -> 0
Activity patten combina las cosas buenas en ambos lados: programación funcional y orientada a objetos.
árbitro. here y here para patrones de actividad.
Puede consultar el documento original sobre el patrón activo de Don Syme.