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 proofy 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 giveny 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 .