2010-08-03 14 views
14

En Erlang, se le recomienda no hacer coincidir patrones que en realidad no maneja. Por ejemplo:Coincidencia de patrones, F # vs Erlang

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

es un estilo que se anima, con otros resultados posibles resultantes 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 incompleto" en el código F # equivalente, como here.

La pregunta: ¿por qué no F # eliminar la advertencia, efectivamente aumentando cada patrón de coincidencia con una declaración equivalente a

|_ -> failwith "badmatch" 

y el uso de la filosofía "deja que choque"?

Editar: Dos respuestas interesantes hasta ahora: ya sea 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 los malentendidos de 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. Let it crash means let some other process fix the 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.

+0

Creo que la filosofía es que su función debe manejar todo el dominio que se espera de ella, y el compilador se lo recuerda. Si la función no maneja más casos que dos, entonces debe restringir los argumentos por tipo, de lo contrario cuenta para todos ellos. Esa es mi comprensión; Estoy aprendiendo sobre esta filosofía yo mismo. – grettke

+0

@grettke: tienes razón. Esta filosofía es exactamente lo opuesto a la filosofía de Erlang. Y mi pregunta es por qué se prefiere esta filosofía (o cuándo). Las respuestas a continuación son bastante esclarecedoras. –

Respuesta

23

F # (y otros idiomas 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, porque el compilador le advertirá sobre el código que aún no tiene 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).

+0

Gracias. Puedo ver la refactorización es una buena razón. Y gracias por la información de OCaml/Haskell. –

13

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 de coincidencia de patrón genérica (por ejemplo, List.head arroja un 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.

11

por qué no habría F # quitar la advertencia

interesante que le pregunte 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 la 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 una coincidencia 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 se utiliza un patrón en una let unión para extraer el primer elemento de una lista, ya sabes la lista siempre tendrá al menos un elemento:

let x::_ = myList 

En esta familia de lenguas, eso es casi siempre 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 patrones se convierte entonces en:

let x, _ = myList 

Esta es exhaustiva por lo que el compilador es feliz y no emite una advertencia. Este código no puede fallar en el tiempo de ejecución.

Me volví un defensor de esta técnica en 2004, cuando refactoricé sobre 1kLOC de código OCaml comercial que había sido una fuente importante de errores de tiempo de ejecución en una aplicación (aunque eran explícitos en la forma de capturas). todos los casos coincidentes que generaron excepciones). 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.

+2

Nitpick: si el comportamiento del código cambió, no estabas refactorizando. – Douglas

+2

@Jon: se quedó atascado en el problema de Python, ya veo. Vea mi comentario a JSBands en la respuesta de Justin Niessner sobre por qué la filosofía de Erlang no es tan mala. Erlang tiene aplicaciones de telecomunicaciones mucho más estables que cualquier otra que .Net haya logrado hasta ahora. Intenta comprender el otro lado de la pregunta antes de responder. Dejar que se cuelgue se traduce en Erlang para permitir que otro proceso solucione el error. Lo remito a [la tesis de Joe Armstrong] (http://www.erlang.org/download/armstrong_thesis_2003.pdf) para más detalles. –

+2

Es curioso, "inyectar silenciosamente fuentes de error en tiempo de ejecución" es exactamente por lo que Erlang no te obliga a agregar cosas como '| _ ->() 'solo para deshacerse de ellos. Erlang siempre devolverá 'badmatch' si no coincide con uno de los casos dados. –

3

OCaml de forma predeterminada le 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 una funda. Cuando el programador está seguro de que no puede salir mal, agregar explícitamente un caso | _ -> assert false es una manera inequívoca de indicar eso.

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

3

Erlang no puede tener una concordancia de patrón exhaustiva debido a los tipos dinámicos a menos que tenga 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.

+0

El problema de tipeo dinámico es importante. Gracias. –

Cuestiones relacionadas