f# erlang pattern-matching ocaml

Patrón de coincidencia, F#vs Erlang



pattern-matching ocaml (5)

En Erlang, se le recomienda no hacer coincidir patrones que en realidad no maneja. Por ejemplo:

case (anint rem 10) of 1 -> {ok, 10} 9 -> {ok, 25} end;

es un estilo que se fomenta, con otros resultados posibles que resultan en un resultado de badmatch . Esto es consistente con la filosofía "déjalo colapsar" en Erlang.

Por otro lado, F # emitiría una "coincidencia de patrón incompleta" en el código F # equivalente, como aquí .

La pregunta: ¿por qué F # eliminaría la advertencia, efectivamente al aumentar cada patrón que coincida con una declaración equivalente a

|_ -> failwith "badmatch"

y usar la filosofía "let it crash"?

Editar : Dos respuestas interesantes hasta ahora: o para evitar errores que son posibles cuando no se manejan todos los casos de un tipo de datos algebraico; o debido a la plataforma .Net. Una forma de averiguar cuál es verificar OCaml. Entonces, ¿cuál es el comportamiento predeterminado en OCaml?

Editar : para eliminar malentendidos por personas de .Net que no tienen antecedentes en Erlang. El objetivo de la filosofía de Erlang no es producir código incorrecto que siempre falle. Dejar que se cuelgue significa dejar que algún otro proceso corrija el error . En lugar de escribir la función para que pueda manejar todos los casos posibles, permita que la persona que llama (por ejemplo) maneje los casos incorrectos que se lanzan automáticamente. Para aquellos con antecedentes Java, es como la diferencia entre tener un lenguaje con excepciones comprobadas que debe declarar todo lo que posiblemente devolverá con cada excepción posible, y tener un lenguaje en el cual las funciones pueden generar excepciones que no están explícitamente declaradas.


OCaml por defecto te advierte sobre coincidencias incompletas. Puede desactivarlo agregando "p" a la bandera "-w". La idea con estas advertencias es que la mayoría de las veces (al menos en mi experiencia) son una indicación de error del programador. Especialmente cuando todos sus patrones son realmente complejos como Node (Node (Leaf 4) x) (Node y (Node Empty _)) , es fácil perder un caso. Cuando el programador está seguro de que no puede salir mal, agregando explícitamente un | _ -> assert false | _ -> assert false case es una forma inequívoca de indicar eso.

GHC por defecto desactiva estas advertencias; pero puedes habilitarlo con -fwarn-incomplete-patterns


¿Por qué F # no eliminaría la advertencia?

Es interesante que preguntes esto. Inyectar silenciosamente fuentes de error en tiempo de ejecución es absolutamente contrario a la filosofía detrás de F # y sus familiares. Se considera una abominación grotesca. Esta familia de idiomas tiene que ver con la comprobación estática, en la medida en que el sistema de tipo fue diseñado fundamentalmente para facilitar exactamente este tipo de comprobaciones estáticas.

Esta marcada diferencia en filosofía es precisamente la razón por la cual F # y Python son tan raramente comparados y contrastados. "Nunca los dos se encontrarán", como dicen.

Entonces, ¿cuál es el comportamiento predeterminado en OCaml?

Igual que F #: se comprueba la exhaustividad y la redundancia de coincidencias de patrones en tiempo de compilación y se emite una advertencia si se encuentra que una coincidencia es sospechosa. El estilo idiomático también es el mismo: se espera que escribas tu código de modo que estas advertencias no aparezcan.

Este comportamiento no tiene nada que ver con .NET y, de hecho, esta funcionalidad (de OCaml) solo se implementó correctamente en F # bastante recientemente.

Por ejemplo, si usa un patrón en un enlace de let para extraer el primer elemento de una lista porque sabe que la lista siempre tendrá al menos un elemento:

let x::_ = myList

En esta familia de idiomas, eso casi siempre es indicativo de un defecto de diseño. La solución correcta es representar su lista no vacía usando un tipo que imposibilite representar la lista vacía. La comprobación estática de tipos prueba que su lista no puede estar vacía y, por lo tanto, garantiza que esta fuente de errores en tiempo de ejecución ha sido completamente eliminada de su código.

Por ejemplo, puede representar una lista no vacía como una tupla que contiene el encabezado y la lista final. Su coincidencia de patrón se convierte en:

let x, _ = myList

Esto es exhaustivo, por lo que el compilador está contento y no emite una advertencia. Este código no puede salir mal en el tiempo de ejecución .

Me volví un defensor de esta técnica en 2004, cuando refactoricé sobre 1kLOC del código OCaml comercial que había sido una fuente importante de errores en tiempo de ejecución en una aplicación (a pesar de que eran explícitos en forma de casos de coincidencia que excepciones planteadas). Mi refactorización eliminó todas las fuentes de errores de tiempo de ejecución de la mayoría del código. La fiabilidad de toda la aplicación mejoró enormemente. Además, habíamos desperdiciado semanas buscando errores a través de la depuración, pero mi refactorización se completó en 2 días. Entonces esta técnica realmente paga dividendos en el mundo real.


En la mayoría de los casos, particularmente con los tipos de datos algebraicos, olvidarse de un caso es probable que sea un accidente y no una decisión intencional de ignorar un caso. En los lenguajes funcionales fuertemente tipados, creo que la mayoría de las funciones serán totales y, por lo tanto, deberían manejar cada caso. Incluso para funciones parciales, a menudo es ideal lanzar una excepción específica en lugar de utilizar una falla genérica de coincidencia de patrones (por ejemplo, List.head arroja una ArgumentException cuando se le da una lista vacía).

Por lo tanto, creo que generalmente tiene sentido que el compilador advierta al desarrollador. Si no le gusta este comportamiento, puede agregar un patrón catch-all que arroje una excepción, o desactivar o ignorar la advertencia.


F # (y otros lenguajes con coincidencia de patrones, como Haskell y O''Caml) agrega implícitamente un caso que arroja una excepción.

En mi opinión, la razón más valiosa para tener coincidencias de patrones completos y prestar atención a la advertencia es que facilita la refactorización extendiendo su tipo de datos, ya que el compilador le advertirá sobre el código que aún no ha actualizado con el nuevo caso.

Por otro lado, a veces realmente hay casos que deberían omitirse, y luego es molesto tener que poner un caso general con lo que a menudo es un mensaje de error pobre. Entonces es una compensación.

En respuesta a su edición, esta también es una advertencia por defecto en O''Caml (y en Haskell con -Wall).


Erlang no puede tener una concordancia exhaustiva de patrones debido a los tipos dinámicos a menos que tengas un "catch-all" en cada uno, lo cual es una tontería. Ocaml, por otro lado, puede. Ocaml también intenta impulsar todos los problemas que puedan detectarse en tiempo de compilación a tiempo de compilación.