¿Cómo probar la igualdad de tipos inductivamente sin clases?

8

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?

kirelagin
fuente
1
Este sería un caso de uso para los tipos dependientes, pero Haskell no los tiene. Necesitamos recurrir a singletons (para que podamos combinar patrones y recurse), posiblemente a través de clases de tipo No creo que pueda escribir un término de tipo no inferior (a++(b++c)) :~: ((a++b)++c)sin más argumentos únicos o restricciones de clase de tipo .
chi

Respuestas:

6

No, no puede probar esto sin una restricción de tipo de clase, porque no es cierto. En particular, aquí hay un contraejemplo:

Any ++ ([] ++ []) -- reduces to Any ++ []
(Any ++ []) ++ [] -- does not reduce

Para descartar la existencia (estúpida) de Any, debe usar una clase de tipo que no tenga una Anyinstancia; sin otra opción.

Daniel Wagner
fuente
Ah, claro, sigo olvidándome de Any:(. Esperaba que las anotaciones amables en mi definición de ++garantizaran que es verdad, pero Anyclaramente rompe esto.
kirelagin
1
¿No es Anybásicamente solo undefinedpero 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?
Joseph Sible: reinstala a Monica el
44
@ JosephSible-ReinstateMonica Ciertamente, podemos fingir todo lo que quieras. Y luego, la forma en que convierte su pretensión de nivel de tipo al nivel de cálculo es usar unsafeCoerce. Pero la pregunta solicita explícitamente evitar unsafeCoerce, por lo que no podemos fingir después de todo.
Daniel Wagner