scala f# functional-programming

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:

  1. no puede definir métodos en sus subtipos: Var , Fun y App .

  2. Los métodos que puedes definir en Term son inmutables.

  3. no es posible extender un DU una vez que se define. Piense en usted ahora necesita agregar un subtipo For a Term . Entonces tienes que cambiar una gran cantidad de código donde un Term es un patrón coincidente.

  4. 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.