una tipo registros por orden modulos hacer guardarlo funciones funcion datos como casos arreglos agregar haskell higher-order-functions lifting

tipo - Levantando una función de orden superior en Haskell



registros en haskell (1)

Estoy tratando de construir una función de tipo:

liftSumthing :: ((a -> m b) -> m b) -> (a -> t m b) -> t m b

donde t es un transformador de mónada. Específicamente, estoy interesado en hacer esto:

liftSumthingIO :: MonadIO m => ((a -> IO b) -> IO b) -> (a -> m b) -> m b

Jugué con algunas libs de magia de Haskell y en vano. ¿Cómo lo hago bien, o tal vez hay una solución lista en algún lugar que no encontré?


Esto no se puede hacer genéricamente en todas MonadIO instancias de MonadIO debido a que el tipo IO en una posición negativa. Hay algunas bibliotecas en hackage que hacen esto para instancias específicas ( monad-control monad-peel ), pero ha habido cierto debate sobre si son sólidas semánticamente, especialmente con respecto a cómo manejan las excepciones y cosas extrañas similares.

Edit: Algunas personas parecen interesadas en la distinción de posición positiva / negativa. En realidad, no hay mucho que decir (y probablemente ya lo haya escuchado, pero con un nombre diferente). La terminología proviene del mundo del subtipo.

La intuición detrás de los subtipos es que " a es un subtipo de b (que escribiré a <= b ) cuando se puede usar a en cualquier lugar donde se esperaba a b ". Decidir el subtipo es sencillo en muchos casos; para productos, (a1, a2) <= (b1, b2) siempre que a1 <= b1 y a2 <= b2 , por ejemplo, que es una regla muy sencilla. Pero hay algunos casos difíciles; por ejemplo, ¿cuándo debemos decidir que a1 -> a2 <= b1 -> b2 ?

Bueno, tenemos una función f :: a1 -> a2 y un contexto que espera una función de tipo b1 -> b2 . Entonces, el contexto va a usar el valor de retorno de f como si fuera un b2 , por lo tanto, debemos requerir que a2 <= b2 . Lo complicado es que el contexto suministrará f con un b1 , aunque f lo usará como si fuera un a1 . Por lo tanto, debemos requerir que b1 <= a1 - ¡que mira hacia atrás por lo que podrías adivinar! Decimos que a2 y b2 son "covariantes", u ocurren en una "posición positiva", y a1 y b1 son "contravariantes", o ocurren en una "posición negativa".

(Dejando de lado: ¿por qué "positivo" y "negativo"? Está motivado por la multiplicación. Considere estos dos tipos:

f1 :: ((a1 -> b1) -> c1) -> (d1 -> e1) f2 :: ((a2 -> b2) -> c2) -> (d2 -> e2)

¿Cuándo debería ser el tipo f1 un subtipo del tipo f2 ? Enuncio estos hechos (ejercicio: verifique esto usando la regla anterior):

  • Deberíamos tener e1 <= e2 .
  • Deberíamos tener d2 <= d1 .
  • Deberíamos tener c2 <= c1 .
  • Deberíamos tener b1 <= b2 .
  • Deberíamos tener a2 <= a1 .

e1 está en una posición positiva en d1 -> e1 , que a su vez se encuentra en una posición positiva en el tipo de f1 ; además, e1 encuentra en una posición positiva en el tipo de f1 general (ya que es covariante, según el hecho anterior). Su posición en todo el término es el producto de su posición en cada subterráneo: positivo * positivo = positivo. De manera similar, d1 está en una posición negativa en d1 -> e1 , que está en una posición positiva en todo el tipo. negativo * positivo = negativo, y las variables d son efectivamente contravariantes. b1 está en una posición positiva en el tipo a1 -> b1 , que está en una posición negativa en (a1 -> b1) -> c1 , que está en una posición negativa en todo el tipo. positivo * negativo * negativo = positivo, y es covariante. Tienes la idea.

Ahora, echemos un vistazo a la clase de MonadIO :

class Monad m => MonadIO m where liftIO :: IO a -> m a

Podemos ver esto como una declaración explícita de subtipo: estamos dando una manera de hacer que IO a sea ​​un subtipo de ma para algunos m concretos. De inmediato, sabemos que podemos tomar cualquier valor con los constructores de IO en posiciones positivas y convertirlos en m s. Pero eso es todo: no tenemos forma de convertir a los constructores de IO negativos en m s; necesitamos una clase más interesante para eso.