/ / ¿Por qué no puedo unir el patrón en una familia de tipos? - Haskell, tipos

¿Por qué no puedo unir el patrón en una familia de tipos? - Haskell, tipos

Considera el siguiente código:

{-# LANGUAGE TypeFamilies #-}

data Twothings a b = Twothings a b

type family Leftthing a where
Leftthing (Twothings a b) = Leftthing a
Leftthing a = a

leftthing :: a -> Leftthing a
leftthing (Twothings a b) = leftthing a
leftthing b = b

No compila, con el siguiente error:

Couldn"t match expected type ‘a’
with actual type ‘Twothings a0 t0’
‘a’ is a rigid type variable bound by
the type signature for:
leftthing :: forall a. a -> Leftthing a

Se queja de la línea leftthing (Twothings a b) = leftthing a. Si entiendo correctamente, no puede unificar la variable de tipo a en la firma de tipo con el tipo del constructor Twothings. Ok, esto parece tener sentido. Pero entonces, ¿Cómo puedo definir una función con familias de tipos en la firma tipo??

Respuestas

11 para la respuesta № 1

Cuando declaras

leftthing :: a -> Leftthing a

estás diciendo que el llamador de leftthing consigue elegir qué a es.

Cuando escribes

leftthing (Twothings a b) = leftthing a

usted está presuntuoso que han elegido un Twothings escriba, y como ese no es necesariamente el caso, su programa es rechazado.

Puedes haber pensado que eras probando si ellos habían elegido un Twothings tipo, pero no! La información de tipo se borra antes del tiempo de ejecución, por lo que no hay forma de realizar dicha prueba.

poder intente restaurar la información de tiempo de ejecución necesaria. Primero déjame corregir la incoherencia entre tu Leftthing y leftthing.

type family Leftthing a where
Leftthing (Twothings a b) = Leftthing{-you forgot the recursion!-} a
Leftthing a = a

Ahora podemos definir la GADT de testigos para Twothingness.

data IsItTwothings :: * -> * where
YesItIs   :: IsItTwothings a -> IsItTwothings (Twothings a b)
NoItIsn"t :: Leftthing a ~ a => IsItTwothings a
-- ^^^^^^^^^^^^^^^ this constraint will hold for any type
-- which is *definitely not* a Twothings type

Y luego podemos pasar al testigo como argumento:

leftthing :: IsItTwothings a -> a -> Leftthing a
leftthing (YesItIs r) (Twothings a b) = leftthing r a
leftthing NoItIsn"t   b               = b

En efecto, el testigo es la codificación única del número de anidados a la izquierda Twothingses en la raíz de tu tipo. Esa es información suficiente para determinar en tiempo de ejecución la cantidad correcta de desempaquetado que se debe hacer.

> leftthing (YesItIs (YesItIs NoItIsn"t)) (Twothings (Twothings True 11) (Twothings "strange" [42]))
True

En resumen, no puedes encontrar un tipo por patróncoincidencia en un valor. En su lugar, necesita saber qué tipo de coincidencia de patrón hacer (porque el tipo determina el diseño de la memoria y no hay etiquetas de tipo de tiempo de ejecución). No se puede emparejar el patrón directamente con los tipos (porque simplemente no están allí para que coincidan). Puede construir tipos de datos que actúen como evidencia de tiempo de ejecución de la estructura de tipo y, en su lugar, coincidan.

Quizás, un día, su programa funcionará si le da el tipo

leftthing :: pi a. a -> Leftthing a

dónde pi es el cuantificador dependiente, lo que indica que el argumento de tipo oculto no se borra, sino que se pasa y se combina en tiempo de ejecución. Ese día aún no ha llegado, pero creo que sí.