type
sintaxis dentro clase de tipos y declaraciones de instancias es una parte de TypeFamilies
extensión. Las familias tipo pueden considerarse funciones de tipos a tipos. Hay una gran explicación detallada de las familias de tipos y datos en la wiki de Haskell (ver el enlace).
Aplicado a las clases de tipo, las familias de tipo se convierten en tipos asociados. En este sentido, están muy cerca de FunctionalDependencies
, es decir, permiten una resolución de instancia no ambigua. La necesidad de esto está ampliamente explicada in the GHC manual.
Las definiciones de tipo en su ejemplo son muy simples. :::
es otro nombre para 2-tuple (un par de valores), y TyNil
es isomorfo a la unidad tipo ()
.
Intentaré leer la declaración de la clase y la instancia para que quede claro lo que significan.
class Append a b where
type a :++: b
(.++.) :: a -> b -> a :++: b
infixr 5 :++:
Declare multiparamétrico clase de tipos Append a b
con una función de un método (.++.)
tipo asociado a :++: b
y que toma valores de tipos a
y b
y produce un valor de tipo a :++: b
. También establecemos (.++.)
ser asociativo por la derecha con prioridad 5.
instance Append TyNil b where
type TyNil :++: b = b
_ .++. b = b
Declarar una instancia de Append a b
con primer parámetro fijo (TyNil
) y segundo parámetro arbitrario (b
), donde asocia tipo a :++: b
(en este caso es TyNil :++: b
) se declara igual a b
. (No describiré qué método hago, es bastante claro).
instance (Append y b) => Append (x ::: y) b where
type (x ::: y) :++: b = x ::: (y :++: b)
~(x ::: y) .++. b = x ::: (y .++. b)
Declarar una instancia de Append a b
con primer parámetro en la forma x ::: y
para arbitraria x
y y
y arbitraria segundo parámetro b
dado que ya hay una instancia de Append y b
declarado. El tipo asociado a :++: b
(aquí (x ::: y) :++: b
, obviamente) se declara igual a x ::: (y :++: b)
.La definición del método también es clara aquí: toma un par de valores y otro valor y construye otro par donde el primer elemento es el mismo que en el primer argumento y el segundo elemento es el segundo elemento del primer argumento combinado con el segundo argumento con el método .++.
. Se nos permite utilizar .++.
debido Append y b
restricción
Estas son las firmas de tipos de (.++.)
método de declaración y declaraciones de instancias de clases:
(.++.) :: a -> b -> a :++: b
(.++.) :: TyNil -> b -> b
(.++.) :: Append y b => x ::: y -> b -> x ::: (y :++: b)
Tenga en cuenta que en cada caso muy abstracto a :++: b
transforma el tipo más concreto . Es simple b
en el primer caso y más complejo x ::: (y :++: b)
, escrito en términos de :++:
. Se necesita
Tal declaración de tipo asociado para indicar al sistema tipo que hay algún tipo (a :++: b
en este caso) que es determinado unívocamente por a
y b
solo. Es decir, si typechecker sabe que en cierta expresión a
y b
tipos son iguales a, por ejemplo, Int
y Double
, y:
- hay una restricción
Append a b
;
- hay una instancia de clase de tipo
Append Int Double
con el tipo asociado declarada, por ejemplo, como type Int :++: Double = String
,
entonces el typechecker sabrán que si él cumple con el tipo a :++: b
se sabe que en realidad este tipo es String
.
En cuanto a ~
, se llama 'Lazy pattern match'. Está muy claramente explicado here.
No dude en preguntar si algo no está claro todavía.
Se llaman sinónimos de tipo asociado. Estas construcciones son similares a las dependencias funcionales pero usan funciones de tipo en lugar de relaciones. –