Variantes o variantes Polimórficas?


Noté que, entre los programadores de OCaml que conozco, algunos de ellos siempreusan variantes polimórficas (variantes que no se declaran, prefijadas con una referencia), mientras que otros nunca usan variantes polimórficas, y prefieren variantes declaradas en tipos.

Excepto por razones de rendimiento (las variantes polimórficas se compilan actualmente de manera menos eficiente que las variantes simples), ¿cómo eligen entre ellas los desarrolladores expertos de OCaml ?

Author: Fabrice Le Fessant, 2012-02-20

3 answers

Mi uso se puede dividir en las siguientes 5 categorías. 1. interfaz 2. modularidad 3. legibilidad 4. brevedad 5. trucos

  1. Si el tipo de variante es solo interno del módulo, utilizo variantes regulares, porque como has dicho se compilan de manera más eficiente.
  2. Si el tipo de variante se exporta en la interfaz y siento que algunos casos podrían aparecer en otros módulos, pero no necesariamente tendría sentido hacerlos dependientes del módulo, utilizo variantes polimórficas porque no están vinculados al sistema de espacio de nombres del módulo. Ejemplos: el tipo de codificación tipo de Xmlm. También tener el tipo de señal como tipo variante significa que puede desarrollar módulos usando la misma idea para el procesamiento XML sin introducir una dependencia en Xmlm.
  3. Si el tipo de variante se exporta en la interfaz, a veces me resulta demasiado detallado usar variantes regulares cuando se dan valores del tipo de variante a las funciones del módulo. Ejemplo: la versión tipo de Uuidm. En lugar de tener que escribir Uuidm.create Uuidm.V4 simplemente puede escribir Uuidm.create `V4, que es tan claro y menos detallado.
  4. A veces una función particular puede devolver diferentes casos. Si estos casos solo son utilizados por esta función declaro el tipo de función en la interfaz sin tener que introducir una definición de tipo. Por ejemplo parse : string -> [`Error of string | `Ok of t]
  5. Las variantes polimórficas y sus subtipos permiten imponer invariantes estáticamente con tipos fantasma. Además de la posibilidad de definirlas incremental puede ser útil, tanto para aplicar invariantes estáticamente como para fines de documentación.

Finalmente a veces uso variantes polimórficas en la implementación de un módulo de acuerdo con 4. pero sin que aparezcan en la interfaz. Desaconsejo este uso a menos que declare las variantes polimórficas y las cierre porque debilita la disciplina de escritura estática.

 36
Author: Daniel Bünzli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-09 07:00:19

La única razón por la que uso variantes polimórficas en la mayoría de las interfaces de módulos es para solucionar los problemas de nomenclatura de las variantes clásicas.

Si lo siguiente pudiera funcionar, las variantes polimórficas ya no serían útiles en la mayoría de los casos:

type t1 = String of string | Int of int | Bool of bool | List of t1 list
type t2 = String of string | Int of int | Other

let simplify x =
  match (x : t1) with
      String s -> String s
    | Int n -> Int n
    | Bool _
    | List _ -> Other

Actualización 2014-02-21: el código anterior ahora es válido en OCaml 4.01. ¡Hurra!

 12
Author: Martin Jambon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-22 00:12:11

No es cierto que las variantes polimórficas sean siempre menos eficientes. Usando el ejemplo de Martin:

type base = [`String of string | `Int of int]
type t1 = [base | `Bool of bool | `List of t1 list]
type t2 = [base | `Other]

let simplify (x:t1):t2 = match x with
| #base as b -> b
| `Bool _ | `List _ -> `Other

Para hacer esto con variantes estándar requiere dos tipos distintos y una recodificación completa, con variantes polimórficas el caso base es físicamente invariante. Esta característica realmente entra en su propia cuando se utiliza la recursión abierta para la reescritura de términos:

type leaf = [`String of string | `Int of int]
type 'b base = [leaf | `List of 'b list]
type t1 = [t1 base | `Bool of bool ]
type t2 = [t2 base | `Other]

let rec simplify (x:t1):t2 = match x with
| #leaf as x -> x
| `List t -> `List (List.map simplify t)
| `Bool _ -> `Other

Y las ventajas son aún mayores cuando las funciones de reescritura también se factorizan con open recursión.

Desafortunadamente, la inferencia de tipos Hindley-Milner de Ocaml no es lo suficientemente fuerte como para hacer este tipo de cosas sin la tipificación explícita, lo que requiere una cuidadosa factorización de los tipos, lo que a su vez dificulta la prototipificación. Además, a veces se requieren coerciones explícitas.

La gran desventaja de esta técnica es que para términos con múltiples parámetros, uno pronto termina con una explosión combinatoria bastante confusa de tipos, y al final es más fácil dar use un tipo de fregadero de cocina con comodines y excepciones (es decir, escritura dinámica).

 11
Author: Yttrill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-02-26 19:05:02