Estoy tratando de demostrar la asociatividad de las listas de nivel de tipo de tal manera que me permita convertir entre tipos equivalentes sin tener que soportar ninguna restricción.
Suponiendo la definición estándar de concatenación:
type family (++) (xs :: [k]) (ys :: [k]) :: [k] where
'[] ++ ys = ys
(x ': xs) ++ ys = x ': (xs ++ ys)
Supongamos que se me asigna una función:
given :: forall k (a :: [k]) (b :: [k]) (c :: [k]). Proxy ((a ++ b) ++ c)
given = Proxy -- Proxy is just an example
y me gustaría llamar a esta función y luego usar asociatividad:
my :: forall k (a :: [k]) (b :: [k]) (c :: [k]). Proxy (a ++ (b ++ c))
my = given @k @a @b @c -- Couldn't match type ‘(a ++ b) ++ c’ with ‘a ++ (b ++ c)’
Este tipo de igualdad no es realmente trivial, por lo que no es una sorpresa que el compilador no lo entienda, ¡pero puedo demostrarlo! Desafortunadamente, no sé cómo convencer al compilador de que puedo.
Mi primer pensamiento natural es hacer algo como:
proof :: forall k (a :: [k]) (b :: [k]) (c :: [k]). (a ++ (b ++ c)) :~: ((a ++ b) ++ c)
proof = _
y luego cambio mi función a:
my :: forall k (a :: [k]) (b :: [k]) (c :: [k]). Proxy (a ++ (b ++ c))
my = case proof @k @a @b @c of Refl -> given @k @a @b @c
Pero todavía tengo que definir proof
y para esto necesito realizar una inducción sobre sus argumentos de tipo. La única forma de hacer inducción sobre tipos en Haskell que conozco es definir una clase de tipo, pero luego tendré que agregar la restricción correspondiente al tipo de my
, que no quiero hacer, el hecho de que llama given
y coacciona el resultado es un "detalle de implementación".
¿Hay alguna forma de demostrar este tipo de igualdad de tipos en Haskell sin recurrir a postulados inseguros?
(a++(b++c)) :~: ((a++b)++c)
sin más argumentos únicos o restricciones de clase de tipo .Respuestas:
No, no puede probar esto sin una restricción de tipo de clase, porque no es cierto. En particular, aquí hay un contraejemplo:
Para descartar la existencia (estúpida) de
Any
, debe usar una clase de tipo que no tenga unaAny
instancia; sin otra opción.fuente
Any
:(. Esperaba que las anotaciones amables en mi definición de++
garantizaran que es verdad, peroAny
claramente rompe esto.Any
básicamente soloundefined
pero a nivel de tipo? Dado que es moralmente correcto pretender que lo último no existe, ¿por qué no podemos hacer lo mismo por lo primero?unsafeCoerce
. Pero la pregunta solicita explícitamente evitarunsafeCoerce
, por lo que no podemos fingir después de todo.