La absurd
función en Data.Void
tiene la siguiente firma, donde Void
es el tipo lógicamente deshabitado exportado por ese paquete:
-- | Since 'Void' values logically don't exist, this witnesses the logical
-- reasoning tool of \"ex falso quodlibet\".
absurd :: Void -> a
Sé suficiente lógica para obtener el comentario de la documentación de que esto corresponde, por la correspondencia de proposiciones como tipos, a la fórmula válida ⊥ → a
.
De lo que estoy desconcertado y curioso es: ¿en qué tipo de problemas prácticos de programación es útil esta función? Estoy pensando que tal vez sea útil en algunos casos como una forma segura de manejar exhaustivamente los casos de "no puede suceder", pero no sé lo suficiente sobre los usos prácticos de Curry-Howard para saber si esa idea está en el camino correcto en absoluto.
EDITAR: Ejemplos preferiblemente en Haskell, pero si alguien quiere usar un lenguaje escrito de forma dependiente, no me voy a quejar ...
fuente
absurd
función se ha utilizado en este artículo sobre laCont
mónada: haskellforall.com/2012/12/the-continuation-monad.htmlabsurd
como una dirección del isomorfismo entreVoid
yforall a. a
.Respuestas:
La vida es un poco dura, ya que Haskell no es estricto. El caso de uso general es manejar caminos imposibles. Por ejemplo
Esto resulta ser algo útil. Considere un tipo simple para
Pipes
esta es una versión estricta y simplificada del tipo de tubería estándar de la
Pipes
biblioteca de Gabriel Gonzales . Ahora, podemos codificar una tubería que nunca cede (es decir, un consumidor) comoesto realmente nunca cede. La implicación de esto es que la regla de plegado adecuada para a
Consumer
eso alternativamente, que puede ignorar el caso de rendimiento cuando se trata de consumidores. Esta es la versión general de este patrón de diseño: use tipos de datos polimórficos y
Void
elimine las posibilidades cuando lo necesite.Probablemente el uso más clásico de
Void
es en CPS.es decir, a
Continuation
es una función que nunca regresa.Continuation
es la versión tipo de "no". De esto obtenemos una mónada de CPS (correspondiente a la lógica clásica)dado que Haskell es puro, no podemos sacar nada de este tipo.
fuente
Void
está deshabitado. En Haskell, contiene_|_
. En un lenguaje estricto, un constructor de datos que toma un argumento de tipoVoid
nunca se puede aplicar, por lo que el lado derecho de la coincidencia del patrón es inalcanzable. En Haskell, necesita usar un!
para hacer cumplir eso, y GHC probablemente no notará que la ruta es inalcanzable._|_
? ¿Y entonces sufre la misma limitación?Considere esta representación para términos lambda parametrizados por sus variables libres. (Véanse los artículos de Bellegarde y Hook 1994, Bird y Paterson 1999, Altenkirch y Reus 1999.)
Ciertamente puede hacer que esto
Functor
capture la noción de cambio de nombre yMonad
capture la noción de sustitución.Ahora considere los términos cerrados : estos son los habitantes de
Tm Void
. Debería poder incrustar los términos cerrados en términos con variables libres arbitrarias. ¿Cómo?El problema, por supuesto, es que esta función atravesará el término sin hacer nada precisamente. Pero es un toque más honesto que
unsafeCoerce
. Y es por eso quevacuous
se agregó aData.Void
...O escribe un evaluador. Aquí hay valores con variables libres en formato
b
.Acabo de representar lambdas como cierres. El evaluador está parametrizado por un entorno que asigna variables libres
a
a valores superioresb
.Lo adivinaste. Para evaluar un plazo cerrado en cualquier objetivo
De manera más general,
Void
rara vez se usa por sí solo, pero es útil cuando desea instanciar un parámetro de tipo de una manera que indique algún tipo de imposibilidad (por ejemplo, aquí, usando una variable libre en un término cerrado). A menudo, estos tipos parametrizadas vienen con funciones de orden superior operaciones de elevación de los parámetros a las operaciones en todo el tipo (por ejemplo, aquí,fmap
,>>=
,eval
). Así que pasaabsurd
como la operación de propósito generalVoid
.Para otro ejemplo, imagínese usar
Either e v
para capturar cálculos que con suerte le den unv
pero podrían generar una excepción de tipoe
. Puede utilizar este enfoque para documentar el riesgo de mal comportamiento de manera uniforme. Para cálculos que se comportan perfectamente en esta configuración, tómateloe
yVoid
luego usapara correr con seguridad o
para integrar componentes seguros en un mundo inseguro.
Ah, y un último hurra, manejar un "no puede suceder". Aparece en la construcción de cremallera genérica, en todos los lugares donde el cursor no puede estar.
Decidí no borrar el resto, aunque no es exactamente relevante.
De hecho, tal vez sea relevante. Si se siente aventurero, este artículo inacabado muestra cómo utilizar
Void
para comprimir la representación de términos con variables libresen cualquier sintaxis generada libremente desde a
Differentiable
yTraversable
functorf
. UsamosTerm f Void
para representar regiones sin variables libres y[D f (Term f Void)]
para representar tubos que hacen un túnel a través de regiones sin variables libres, ya sea a una variable libre aislada o a una unión en las rutas a dos o más variables libres. Debo terminar ese artículo en algún momento.Para un tipo sin valores (o al menos, ninguno de los que valga la pena hablar en compañía cortés),
Void
es muy útil. Y asíabsurd
es como lo usas.fuente
forall f. vacuous f = unsafeCoerce f
Sería una regla de reescritura de GHC válida?Functor
instancias falsas pueden ser GADT que en realidad no se parecen en nada a los functores.Functor
No romperían lafmap id = id
regla esos s ? ¿O es eso lo que quiere decir con "falso" aquí?Esto es precisamente correcto.
Se podría decir que
absurd
no es más útil queconst (error "Impossible")
. Sin embargo, tiene restricción de tipo, por lo que su única entrada puede ser algo de tipoVoid
, un tipo de datos que se deja intencionalmente deshabitado. Esto significa que no hay un valor real al que pueda pasarabsurd
. Si alguna vez terminas en una rama de código donde el verificador de tipos cree que tienes acceso a algo de tipoVoid
, entonces, bueno, estás en una situación absurda . Por lo tanto, solo usaabsurd
para marcar básicamente que nunca se debe llegar a esta rama de código."Ex falso quodlibet" significa literalmente "de [una] falsa [proposición], todo sigue". Entonces, cuando descubre que tiene un dato cuyo tipo es
Void
, sabe que tiene pruebas falsas en sus manos. Por lo tanto, puede llenar cualquier hueco que desee (víaabsurd
), porque de una proposición falsa, todo se sigue.Escribí una publicación de blog sobre las ideas detrás de Conduit que tiene un ejemplo de uso
absurd
.http://unknownparallel.wordpress.com/2012/07/30/pipes-to-conduits-part-6-leftovers/#running-a-pipeline
fuente
Generalmente, puede usarlo para evitar coincidencias de patrones aparentemente parciales. Por ejemplo, tomando una aproximación de las declaraciones de tipo de datos de esta respuesta :
Entonces podrías usar
absurd
así, por ejemplo:fuente
Hay diferentes formas de representar el tipo de datos vacío . Uno es un tipo de datos algebraicos vacío. Otra forma es convertirlo en un alias para ∀α.α o
en Haskell: así es como podemos codificarlo en el Sistema F (consulte el Capítulo 11 de Pruebas y tipos ). Estas dos descripciones son, por supuesto isomorfo y el isomorfismo se evidencia con
\x -> x :: (forall a.a) -> Void
yabsurd :: Void -> a
.En algunos casos, preferimos la variante explícita, generalmente si el tipo de datos vacío aparece en un argumento de una función, o en un tipo de datos más complejo, como en Data.Conduit :
En algunos casos, preferimos la variante polimórfica, generalmente el tipo de datos vacío está involucrado en el tipo de retorno de una función.
absurd
surge cuando estamos convirtiendo entre estas dos representaciones.Por ejemplo,
callcc :: ((a -> m b) -> m a) -> m a
usa (implícito)forall b
. También podría ser de tipo((a -> m Void) -> m a) -> m a
, porque una llamada a la continuación en realidad no regresa, transfiere el control a otro punto. Si quisiéramos trabajar con continuaciones, podríamos definir(Podríamos usar,
type Continuation' r a = forall b . a -> Cont r b
pero eso requeriría tipos de rango 2). Y luego,vacuousM
convierte estoCont r Void
enCont r b
.(También tenga en cuenta que puede usar haskellers.com para buscar el uso (dependencias inversas) de un determinado paquete, como para ver quién y cómo usa el paquete void ).
fuente
TypeApplications
se puede utilizar para ser más explícito acerca de los detalles deproof :: (forall a. a) -> Void
:proof fls = fls @Void
.En lenguajes de tipo dependiente como Idris, probablemente sea más útil que en Haskell. Por lo general, en una función total, cuando el patrón coincide con un valor que en realidad no se puede introducir en la función, entonces construye un valor de tipo deshabitado y lo usa
absurd
para finalizar la definición del caso.Por ejemplo, esta función elimina un elemento de una lista con la restricción de tipo de nivel que está presente allí:
Donde el segundo caso dice que hay un determinado elemento en una lista vacía, lo cual es, bueno, absurdo. En general, sin embargo, el compilador no sabe esto y, a menudo, tenemos que ser explícitos. Luego, el compilador puede verificar que la definición de la función no sea parcial y obtenemos mayores garantías en tiempo de compilación.
A través del punto de vista de Curry-Howard, donde hay proposiciones, entonces
absurd
es una especie de QED en una prueba por contradicción.fuente