I have a stateful type with the >>
and >>=
operators, which is nearly a monad. The intended use is to generate code for another language, and having the do-notation available will be very appropriate.
However, there is no well-defined return function, as values are only supposed to be produced along with side-effects. So, if I fake a return function, the return function should return an error only, and it breaks the monad laws. ( I.e. it is never valid to use a return function. )
What I observe is that the do-notation only sugars two operators, the >>
and >>=
operators.
Is there a way to retain something like the do notation for only those 2 operators, or something as clean as it, but without making a monad?
NOTE: Below the line are details that are not necessary for the question in the title. It's the context for why I asked the question, replies to which I'm interested in. Others who browse this topic would probably prefer to ignore it.
CORRECTION: I'm not generating code into an imperative language, although it shouldn't matter. I'm generating code into Javascript.
CLARIFICATION (in response to bdonlan): My type is (m a), which carries a state (which is the code to be generated given various parameters, similar to the state monad) and will return a value (the variable names in the generated code, with a haskell type attached). Thus, there shouldn't be a way to return values, which are associated with variable names, without generating code that defines the variable names. * This is solely due to my design, which might be inferior to others which I have not thought of.
In response to the responses: There seems to be two kinds of responses to this. First, is whether it is indeed possible, and perhaps the best way to go about using the do notation. The second is on whether it is better to use a monad, and define a return (or whether it even makes sense to not do so - perhaps at a later point in time, I would find that a return is required).
Example of what I would be doing:
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)
monads are used to address the more general problem of computations (involving state, input/output, backtracking, ...) returning values: they do not solve any input/output-problems directly but rather provide an elegant and flexible abstraction of many solutions to related problems.
So in simple words, a monad is a rule to pass from any type X to another type T(X) , and a rule to pass from two functions f:X->T(Y) and g:Y->T(Z) (that you would like to compose but can't) to a new function h:X->T(Z) . Which, however, is not the composition in strict mathematical sense.
A monad is essentially just a functor T with two extra methods, join , of type T (T a) -> T a , and unit (sometimes called return , fork , or pure ) of type a -> T a .
A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.
Well, the sort of structure you're working with does already exist; a relevant type class can be found on Hackage, in fact. I don't recommend trying to force it into an instance of Monad
, though, for the reasons that bdonlan gives. The existence of return
is pretty fundamental and you're likely to run into trouble everywhere trying to use standard functions for Monad
s.
....however! That said, if you really want do
notation, consider this quote from the GHC user guide:
"Do" notation is translated using whatever functions (>>=), (>>), and fail, are in scope (not the Prelude versions).
...in other words, you're right that using do
notation makes sense, because nowhere does it actually use return
when desugared. That section is about the RebindableSyntax
extension, which does exactly what it claims. If you don't mind giving up do
notation for Monad
, you can use that extension, define your own functions, and use do
notation all you like.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With