haskell types unification

haskell - Tipo de diversión gx=ys donde ys=[x]++ filter(curry gx) ys?



types unification (2)

Estoy tratando de entender por qué el tipo de fun gx = ys where ys = [x] ++ filter (curry gx) ys es ((a, a) -> Bool) -> a -> [a] .

Entiendo que:

filter :: (a -> Bool) -> [a] -> [a] y ese curry :: ((a, b) -> c) -> a -> b -> c

Pero no entiendo cómo continuar.


El siguiente enfoque no es necesariamente el más fácil ni el más rápido, pero es relativamente sistemático.

Estrictamente hablando, estás buscando el tipo de

/g -> (/ x -> let ys = (++) [x] (filter (curry g x) ys) in ys)

( let y where son equivalentes, pero a veces es un poco más fácil razonar usando let ), dados los tipos

filter :: (a -> Bool) -> [a] -> [a] curry :: ((a, b) -> c) -> a -> b -> c

No olvides que también estás usando

(++) :: [a] -> [a] -> [a]

Primero veamos la parte más "profunda" del árbol de sintaxis:

curry g x

Tenemos x , de los cuales no hemos visto antes todavía, así que asumiremos que tienen algún tipo:

g :: t1 x :: t2

También tenemos curry . En cada punto donde se producen estas funciones, las variables de tipo ( a , b , c ) se pueden especializar de forma diferente, por lo que es una buena idea reemplazarlas por un nombre nuevo cada vez que utilice estas funciones. En este punto, curry tiene el siguiente tipo:

curry :: ((a1, b1) -> c1) -> a1 -> b1 -> c1

Entonces solo podemos decir curry gx si los siguientes tipos se pueden unificar:

t1 ~ ((a1, b1) -> c1) -- because we apply curry to g t2 ~ a1 -- because we apply (curry g) to x

Entonces también es seguro suponer que

g :: ((a1, b1) -> c1) x :: a1 --- curry g x :: b1 -> c1

Continuemos:

filter (curry g x) ys

Vemos ys por primera vez, así que vamos a mantenerlo en ys :: t3 por ahora. También tenemos que instanciar el filter . Entonces, en este punto, sabemos

filter :: (a2 -> Bool) -> [a2] -> [a2] ys :: t3

Ahora debemos hacer coincidir los tipos de argumentos del filter :

b1 -> c1 ~ a2 -> Bool t3 ~ [a2]

La primera restricción se puede desglosar para

b1 ~ a2 c1 ~ Bool

Ahora sabemos lo siguiente:

g :: ((a1, a2) -> Bool) x :: a1 ys :: [a2] --- filter (curry g x) ys :: [a2]

Continuemos.

(++) [x] (filter (curry g x) ys)

No dedicaré demasiado tiempo a explicar [x] :: [a1] , veamos el tipo de (++) :

(++) :: [a3] -> [a3] -> [a3]

Obtenemos las siguientes limitaciones:

[a1] ~ [a3] -- [x] [a2] ~ [a3] -- filter (curry g x) ys

Dado que estas restricciones se pueden reducir a

a1 ~ a3 a2 ~ a2

llamaremos a todos estos a a1 :

g :: ((a1, a1) -> Bool) x :: a1 ys :: [a1] --- (++) [x] (filter (curry g x) ys) :: [a1]

Por ahora, voy a ignorar que el tipo ys se generaliza y me enfoco en

/x -> let { {- ... -} } in ys

Sabemos qué tipo necesitamos para x , y sabemos el tipo de ys , entonces ahora sabemos

g :: ((a1, a1) -> Bool) ys :: [a1] --- (/x -> let { {- ... -} } in ys) :: a1 -> [a1]

De manera similar, podemos concluir que

(/g -> (/x -> let { {- ... -} } in ys)) :: ((a1, a1) -> Bool) -> a1 -> [a1]

En este punto, solo tiene que cambiar el nombre (en realidad, generalizar, porque desea vincularlo a la fun ) las variables de tipo y tiene su respuesta.


Podemos derivar tipos en Haskell de una manera más o menos mecánica, utilizando el esquema general de

foo x = y -- is the same as foo = /x -> y -- so the types are foo :: a -> b , x :: a , y :: b -- so that foo x :: b

lo que significa que, por ejemplo

f x y z :: d , x :: a , y :: b, z :: c

implica

f x y :: c -> d f x :: b -> c -> d f :: a -> b -> c -> d

Con estos simples trucos, la derivación del tipo será trivial para ti. Adjunto

filter :: (a -> Bool) -> [a] -> [a] curry :: ((a, b) -> c) -> a -> b -> c (++) :: [a] -> [a] -> [a]

simplemente escribimos las cosas cuidadosamente alineadas, procesándolas de arriba hacia abajo , renombrando y sustituyendo constantemente las variables de tipo, y registrando las equivalencias de tipo en el costado:

fun g x = ys where ys = [x] ++ filter (curry g x) ys fun g x :: c , ys :: c fun g :: b -> c , x :: b fun :: a -> b -> c , g :: a

ys = [x] ++ filter (curry g x) ys c ~ c (++) [x] (filter (curry g x) ys) :: c (++) :: [a1] -> [a1] -> [a1] ----------------------------------------------- (++) :: [b] -> [b] -> [b] , a1 ~ b , c ~ [b] filter (curry g x ) ys :: [b] filter :: (a2 -> Bool) -> [a2] -> [a2] , a2 ~ b -------------------------------------- filter :: (b -> Bool) -> [b] -> [b] curry g x :: b -> Bool curry :: ((a3, b) -> c3 ) -> a3 -> b -> c3 , c3 ~ Bool , a3 ~ b ------------------------------------------- curry :: ((b , b) -> Bool) -> b -> b -> Bool , a ~ ((b,b) -> Bool)

entonces tenemos eso a ~ ((b,b) -> Bool) y c ~ [b] , y por lo tanto

fun :: a -> b -> c fun :: ((b,b) -> Bool) -> b -> [b]

que es lo mismo que ((a,a) -> Bool) -> a -> [a] , hasta un cambio de nombre consistente de variables de tipo.