2012-03-01 24 views
16

Últimamente he estado escribiendo código FFI que devuelve una estructura de datos en la mónada IO. Por ejemplo:Lo que se prefiere entre liftM, lilftA, etc.

peek p = Vec3 <$> (#peek aiVector3D, x) p 
       <*> (#peek aiVector3D, y) p 
       <*> (#peek aiVector3D, z) p 

Ahora puede pensar en cuatro formas agradables para escribir ese código, todo estrechamente relacionados:

peek p = Vec3 <$> io1 <*> io2 <*> io3 
peek p = liftA3 Vec3 io1 io2 io3 
peek p = return Vec3 `ap` io1 `ap` io2 `ap` io3 
peek p = liftM3 Vec3 io1 io2 io3 

en cuenta que lo que pido sobre el código monádico que no requiere nada más allá lo que Applicative proporciona. ¿Cuál es la forma preferida de escribir este código? ¿Debo usar Applicative para enfatizar lo que hace el código, o debo usar Monad porque podría (?) Tener optimizaciones sobre Applicative?

La pregunta es algo complicado por el hecho de que sólo hay [liftA..liftA3] y [liftM..liftM5] pero tengo varios registros con más de tres o cinco miembros, por lo que si decido ir con lift{A,M} que perder un poco de coherencia, ya que tendría que utilizar un método diferente para los registros más grandes.

+2

La consistencia habla por 'ap' o' <*> '. Lo que eliges es en gran medida una cuestión de gusto. Mi preferencia (y tengo la impresión de que no es solo mía) es '<*>'. –

Respuesta

25

La primera cosa a recordar es que este es un poco más complicado de lo que debería ser - cualquier instancia Monad debe tener un asociado Applicative ejemplo de tal manera que los liftM y liftA funciones coinciden. Como tal, esto es dos directrices:

  • Si va a escribir una función genérica para cualquier Monad, el uso liftM & co. para evitar la incompatibilidad con otras funciones que tienen solo una restricción Monad.

  • Si está trabajando con una instancia específica Monad que usted conoce tiene un acompañante Applicative ejemplo, utilizar Applicative operadores constantemente para cualquier definición o subexpresión en el que no es necesario Monad operaciones, pero no mezclarlos sin rumbo.

¿Debo usar Applicative hacer hincapié en lo que hace el código, o debería utilizar Monad, ya que podría (?) Tienen optimizaciones sobre Applicative?

En general, si hay una diferencia, será al revés. Applicative solo admite una "estructura" estática del cálculo, mientras que Monad permite el flujo de control incorporado. Considere las listas, por ejemplo, con Applicative, todo lo que puede hacer es generar todas las combinaciones posibles y transformar cada una: el número de elementos de resultado está determinado por completo por la cantidad de elementos en cada entrada. Con Monad, puede generar diferentes números de elementos en cada paso en función de los elementos de entrada, lo que le permite filtrar o expandir arbitrariamente.

A más potente ejemplo es es el Applicative y Monad casos sobre la base de comprimir infinito streams-- Applicative simplemente les puede comprimir juntos en la manera obvia, mientras que Monad tiene que volver a calcular las porciones de materia que a continuación tira a la basura.

Por lo tanto, la edición final es de liftA2 f x y vs.f <$> x <*> y, o los Monad equivalentes. Mi consejo aquí sería seguir las siguientes pautas:

  • Si escribe todos los argumentos de todos modos, use el formulario infijo, porque es más fácil de leer para expresiones grandes.
  • Si solo está levantando una función existente, use foo = liftA2 bar en lugar de foo x y = bar <$> x <*> y; es más corta y expresa más claramente lo que está haciendo.

Y, por último, en lo que respecta a la coherencia, no hay ninguna razón por la que no pueda definir su propio liftA4 y así sucesivamente, si los necesita.

+1

En realidad, creo que 'liftM *' podría ser un poco más rápido porque usan menos enlaces que los operadores 'Applicative' cuando el típico' pure = id; (<*>) = se usa una instancia ap'. Pero la diferencia, si la hay, debería ser insignificante. – ehird

+0

No sabía si habría una respuesta correcta, pero creo que esto es todo. –

+0

@ehird, como contrapunto a eso, a veces es posible implementar instancias aplicativas que son más inteligentes sobre la optimización que las instancias de mónada. – luqui

Cuestiones relacionadas