preestablecida monadas leibniz ejemplos armonia haskell monads

haskell - monadas - ¿Notación sin mónadas: posible?



ejemplos de armonia preestablecida (5)

Tengo un tipo con estado con los operadores >> y >>= , que es casi una mónada. El uso previsto es generar código para otro idioma, y ​​tener la notación disponible será muy apropiado.

Sin embargo, no hay una función de retorno bien definida, ya que se supone que los valores solo se producen junto con los efectos secundarios. Por lo tanto, si falsifico una función de retorno, la función de retorno debería devolver solo un error, y viola las leyes de la mónada. (Es decir, nunca es válido utilizar una función de retorno).

Lo que observo es que la notación solo azucara a dos operadores, los >> y >>= operadores.

¿Hay una manera de conservar algo como la notación de hacer solo para esos 2 operadores, o algo tan limpio como eso, pero sin hacer una mónada?

NOTA: Debajo de la línea hay detalles que no son necesarios para la pregunta en el título. Es el contexto por el que hice la pregunta, respuestas en las que estoy interesado. Es probable que otros que examinen este tema prefieran ignorarlo.

CORRECCIÓN: No estoy generando código en un lenguaje imperativo, aunque no debería importar. Estoy generando código en Javascript.

CLARIFICACIÓN (en respuesta a bdonlan): Mi tipo es (ma), que lleva un estado (que es el código que se generará dados varios parámetros, similar a la mónada del estado) y devolverá un valor (los nombres de las variables en el código generado) , con un tipo de haskell adjunto). Por lo tanto, no debería haber una manera de devolver valores, que están asociados con nombres de variables, sin generar código que defina los nombres de variables. * Esto se debe únicamente a mi diseño, que podría ser inferior a otros que no he pensado.

En respuesta a las respuestas: Parece que hay dos tipos de respuestas a esto. Primero, es si es realmente posible, y quizás la mejor manera de utilizar la notación de hacer. El segundo es si es mejor usar una mónada y definir un retorno (o si tiene sentido no hacerlo, tal vez en un momento posterior, me gustaría encontrar un retorno).

Ejemplo de lo que estaría haciendo:

data JsState = JsState { code :: String , vidCount :: Int } -- vidCount is for generating unique variable names newtype JsStmt = JsStmt ( JsState -> JsState ) newtype JsMonad a = JsMonad ( JsState -> ( a , JsState ) ) def :: (JsValue a) => a -> JsMonad a -- Outputs "var " ++ toUniqueVarName vidCount ++ " = " toCode a ++ ";" -- Returns JsMonad with the variable name as the value, -- with a type attached, and the JsState''s vidCount is incremented by 1. alertJsInt :: JsIntE -> JsMonad () -- Outputs something like "alert(" ++ toCode JsIntE ++ ");" do x <- def $ JsIntL 2 y <- def $ JsIntL 4 alertJsInt (x + y)


Bueno, el tipo de estructura con la que estás trabajando ya existe; una clase de tipo relevante se puede encontrar en Hackage , de hecho. No recomiendo intentar forzarlo en una instancia de Monad , sin embargo, por las razones que da bdonlan. La existencia de return es bastante fundamental y es probable que tengas problemas en todas partes al tratar de usar funciones estándar para Monad s.

....¡sin embargo! Dicho esto, si realmente desea do notación, considere esta cita de la guía del usuario de GHC :

La notación "hacer" se traduce utilizando las funciones (>> =), (>>) y fail, están en el alcance (no en las versiones de Prelude).

... en otras palabras, tienes razón en que usar la notación do tiene sentido, porque en ninguna parte usa el return cuando está desugarado. Esa sección trata sobre la extensión RebindableSyntax , que hace exactamente lo que afirma. Si no le importa renunciar a la notación do para Monad , puede usar esa extensión, definir sus propias funciones y usar la notación do su gusto.


La solución idiomática sería utilizar dos Mónadas Escritor y Estado: Escritor para acumular el código "Cadena" y Estado para rastrear la variable del contador.

Puede combinar las dos mónadas en una mónada amalgamada si no desea utilizar una biblioteca de transformadores de mónada:

-- There are better types to use rather than String... type Output = String type St = Int newtype JsMonad a = JSMonad { getJSMonad :: St -> (a, St, Output) }

[Mientras utiliza la combinación estándar de dos mónadas estándar, la definición obvia de retorno y vinculación no infringirá las leyes de la mónada].

Este es un patrón bastante común para crear DSL incrustados que usan la notación do, vea la biblioteca de Andy Gill''s Dot en Hackage por ejemplo. Oleg Kiselyov también tiene una biblioteca XML en este estilo (CSXML, no en Hackage).

Como no te importa la respuesta final, es común escribir una función de "ejecución" que la ignora:

runJS :: JSmonad a -> Output runJS ma = post $ getJSMonad ma 0 where post (_,_,w) = w

Una cosa a tener en cuenta es que el código de salida es solo un "registro": no puede inspeccionarlo mientras lo está construyendo, así que esto limita algunas de las cosas que puede hacer.


Parece que tienes una arrow . En una mónada necesitas poder escribir:

do x <- return $ f y if x then something else notSomething

La condición "si" se evalúa como parte de la evaluación del "hacer", y el resultado es "algo" o "no Algo", pero no ambos a la vez. Sin embargo, para la generación de código, es probable que desee que el "si" se traduzca en su código generado con ambas sucursales evaluadas para producir un código que pueda hacer la elección en su tiempo de ejecución.

El código equivalente para una flecha desaparece del uso de la clase ArrowChoice en la que tiene acceso tanto a la condición como a ambas ramas, que es lo que necesita aquí.


Sería muy difícil usar notación sin return . Considere el patrón común de:

foo = do x <- bar y <- quux return $ x + y

Sin retorno es imposible definir esto. Recuerde que >>= tiene tipo (>>=) :: Monad m => ma -> (a -> mb) - debe devolver un valor dentro de la mónada. Entonces, incluso si escribimos esto sin notación do, tenemos el mismo problema:

foo = bar >>= /x -> quux >>= /y -> (???????) (x + y)

Así que no es una buena idea usar la notación para esto. En particular, no hay nada que impida que la implementación del compilador introduzca return con transformaciones que se ajusten a las leyes de la mónada (que asumen la presencia de return ).

Además, incluso definir >> = será difícil para ti. ¿Cómo lo traducirás a tu lenguaje imperativo? Recuerde que >> = toma una función arbitraria en su argumento correcto; no puede inspeccionar esta función para decir qué podría hacer y, por lo tanto, no puede traducir el cuerpo de esta función a un lenguaje imperativo. Supongo que podría restringir el tipo de argumento a algún tipo de tipo de dato rastreado e intentar averiguar los efectos de la función, pero ahora esto significa que la función no puede inspeccionar su argumento. En resumen, no va a funcionar muy bien.

Un enfoque alternativo que podría considerar es crear una Mónada que represente el proceso de creación de una función imperativa. Podría, por ejemplo, verse así:

emitAddAndPrint varA varB = do varTmp <- allocateTempVariable emitOp (Add varTmp varA varB) -- if you want to be fancy, emitOp (varTmp :=: varA :+: varB) or something emitCall "print" [varTmp]


Si puede hacer que su tipo sea una instancia de Monad , incluso con un return faltante, entonces la notación do funciona. Lo he hecho (por ejemplo, BASIC ), y es una manera excelente de obtener una notación imperativa para un DSL.