Monads, do and strictness

The "do" notation translates do {x <- a;f} into a>>=(\x -> f) However when we're working in the IO monad the semantics we want requires that the lambda expression be strict in its argument. So is this a special case for IO? If I wanted this behavior in other monads is there a way to specify that? Victor Sent from my iPhone

On 21 Jan 2012, at 21:29, Victor S. Miller wrote:
The "do" notation translates
do {x <- a;f} into
a>>=(\x -> f)
However when we're working in the IO monad the semantics we want requires that the lambda expression be strict in its argument. So is this a special case for IO? If I wanted this behavior in other monads is there a way to specify that?
No, why? The (>>=) combinator (for the IO monad) is strict on it's first argument, that's all. We don't impose any special requirements on the lambda expression.

* Victor S. Miller
The "do" notation translates
do {x <- a;f} into
a>>=(\x -> f)
However when we're working in the IO monad the semantics we want requires that the lambda expression be strict in its argument.
I'm not aware of any semantics that would require that. According to a monad law, return x >>= f should be equivalent to (f x). In particular, return x >>= const (return ()) is equivalent to (const (return ()) x) or simply (return ()). So, const is non-strict in its second argument even when used in (>>=). -- Roman I. Cheplyaka :: http://ro-che.info/

As noted, IO is not strict in the value x, only in the operation that
generates x. However, should you desire strictness in a generic way, it
would be trivial to model a transformer monad to provide it.
E.g.
data StrictT m a = StrictT (m a)
runStrictT :: StrictT m a -> m a
runStrictT (StrictT op) = op
class (Monad m) => Monad (StrictT m a) where
return x = StrictT (return x)
(StrictT op) >>= f = op >>= \ a -> a `seq` StrictT (f a)
On Sat, Jan 21, 2012 at 9:29 AM, Victor S. Miller
The "do" notation translates
do {x <- a;f} into
a>>=(\x -> f)
However when we're working in the IO monad the semantics we want requires that the lambda expression be strict in its argument. So is this a special case for IO? If I wanted this behavior in other monads is there a way to specify that?
Victor
Sent from my iPhone _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

* David Barbour
As noted, IO is not strict in the value x, only in the operation that generates x. However, should you desire strictness in a generic way, it would be trivial to model a transformer monad to provide it.
Again, that wouldn't be a monad transformer, strictly speaking, because "monads" it produces violate the left identity law. -- Roman I. Cheplyaka :: http://ro-che.info/

On Sat, Jan 21, 2012 at 10:08 AM, Roman Cheplyaka
* David Barbour
[2012-01-21 10:01:00-0800] As noted, IO is not strict in the value x, only in the operation that generates x. However, should you desire strictness in a generic way, it would be trivial to model a transformer monad to provide it.
Again, that wouldn't be a monad transformer, strictly speaking, because "monads" it produces violate the left identity law.
It meets the left identity law in the same sense as the Eval monad from Control.Strategies. http://hackage.haskell.org/packages/archive/parallel/3.1.0.1/doc/html/src/Co... That is, so long as values at each step can be evaluated to WHNF, it remains true that `return x >>= f` = f x. I did mess up the def of >>=. I think it should be: (StrictT op) >>= f = StrictT (op >>= \ x -> x `seq` runStrictT (f x)) But I'm not interested enough to actually pull out an interpreter and test... Regards, Dave

On Sat, Jan 21, 2012 at 1:45 PM, David Barbour
On Sat, Jan 21, 2012 at 10:08 AM, Roman Cheplyaka
wrote: * David Barbour
[2012-01-21 10:01:00-0800] As noted, IO is not strict in the value x, only in the operation that generates x. However, should you desire strictness in a generic way, it would be trivial to model a transformer monad to provide it.
Again, that wouldn't be a monad transformer, strictly speaking, because "monads" it produces violate the left identity law.
It meets the left identity law in the same sense as the Eval monad from Control.Strategies. http://hackage.haskell.org/packages/archive/parallel/3.1.0.1/doc/html/src/Co...
The Eval monad has the property: return undefined >>= const e = e.
From what I can tell, your proposed monads do not.
--
Dave Menendez

On Sat, Jan 21, 2012 at 10:51 AM, David Menendez
The Eval monad has the property: return undefined >>= const e = e.
You can't write `const e` in the Eval monad.
From what I can tell, your proposed monads do not.
You can't write `const e` as my proposed monad, either. Regards, Dave

* David Barbour
On Sat, Jan 21, 2012 at 10:51 AM, David Menendez
wrote: The Eval monad has the property: return undefined >>= const e = e.
You can't write `const e` in the Eval monad.
Why not? ghci> runEval $ return undefined >>= const (return ()) () -- Roman I. Cheplyaka :: http://ro-che.info/

On Sat, Jan 21, 2012 at 11:08 AM, Roman Cheplyaka
* David Barbour
[2012-01-21 11:02:40-0800] On Sat, Jan 21, 2012 at 10:51 AM, David Menendez
wrote: The Eval monad has the property: return undefined >>= const e = e.
You can't write `const e` in the Eval monad.
Why not?
ghci> runEval $ return undefined >>= const (return ()) ()
It is, at the very least, utterly pointless to have an Eval monad with `const`. Regards, Dave

Oops, I was misreading. You have `e` here as the next monad.
In any case, I think the monad identity concept messed up. The property:
return x >>= f = f x
Logically only has meaning when `=` applies to values in the domain.
`undefined` is not a value in the domain.
We can define monads - which meet monad laws - even in strict languages.
On Sat, Jan 21, 2012 at 11:02 AM, David Barbour
On Sat, Jan 21, 2012 at 10:51 AM, David Menendez
wrote: The Eval monad has the property: return undefined >>= const e = e.
You can't write `const e` in the Eval monad.
From what I can tell, your proposed monads do not.
You can't write `const e` as my proposed monad, either.
Regards,
Dave

* David Barbour
Logically only has meaning when `=` applies to values in the domain. `undefined` is not a value in the domain.
We can define monads - which meet monad laws - even in strict languages.
In strict languages 'undefined' is not a value in the domain indeed, but it is in non-strict languages, exactly because they are non-strict. I think that's what Robert Harper meant by saying that Haskell doesn't have a type of lists, while ML has one [1]. [1]: http://existentialtype.wordpress.com/2011/04/24/the-real-point-of-laziness/ -- Roman I. Cheplyaka :: http://ro-che.info/

On Sat, Jan 21, 2012 at 8:09 PM, David Barbour
In any case, I think the monad identity concept messed up. The property: return x >>= f = f x
Logically only has meaning when `=` applies to values in the domain. `undefined` is not a value in the domain.
We can define monads - which meet monad laws - even in strict languages.
In strict languages both `return undefined >>= f` and `f undefined` are observably equivalent to `undefined` so the law holds. In a lazy language both sides might be observably different from `undefined` but need to be consistently so. The point of equational laws is that one can replace one side with the other without observing a difference. Your implementation of `StrictT` violates this principle. Sebastian

observably different from `undefined`
If we understand `undefined` as meaning a computation that never ends, then
you cannot ever observe whether one `undefined` is or is not equivalent to
another. In strict languages, this is especially obvious.
In any case, I don't accept a concept of `monads` that changes so
drastically based upon the host language. The laws for monads only apply to
actual values and combinators of the monad algebra - and, since `undefined`
is not a value, it need not apply. Similarly, algebraic laws for integer
arithmetic don't need to account for `undefined`. And our geometry
abstractions and theorems don't need to account for `undefined`.
Attempting to shoehorn `undefined` into your reasoning about domain
algebras and models and monads is simply a mistake.
Regards,
Dave
On Sun, Jan 22, 2012 at 6:49 AM, Sebastian Fischer
On Sat, Jan 21, 2012 at 8:09 PM, David Barbour
wrote: In any case, I think the monad identity concept messed up. The property: return x >>= f = f x
Logically only has meaning when `=` applies to values in the domain. `undefined` is not a value in the domain.
We can define monads - which meet monad laws - even in strict languages.
In strict languages both `return undefined >>= f` and `f undefined` are observably equivalent to `undefined` so the law holds.
In a lazy language both sides might be observably different from `undefined` but need to be consistently so. The point of equational laws is that one can replace one side with the other without observing a difference. Your implementation of `StrictT` violates this principle.
Sebastian

Отправлено с iPad
22.01.2012, в 20:25, David Barbour
Attempting to shoehorn `undefined` into your reasoning about domain algebras and models and monads is simply a mistake.
No. Using the complete semantics — which includes bottoms aka undefined — is a pretty useful technique, especially in a non-strict language.

2012/1/22 MigMit
Отправлено с iPad
22.01.2012, в 20:25, David Barbour
написал(а): Attempting to shoehorn `undefined` into your reasoning about domain algebras and models and monads is simply a mistake.
No. Using the complete semantics — which includes bottoms aka undefined — is a pretty useful technique, especially in a non-strict language.
It is a mistake. You mix semantic layers - confusing the host language with the embedded language. If you need to model non-termination or exceptions or the like, you should model them explicitly as values in your model. That is, *each* layer of abstraction that needs `undefined` should explicitly have its own representation for such concepts, rather than borrowing implicitly from the host. Regards, Dave

On Sun, Jan 22, 2012 at 5:25 PM, David Barbour
The laws for monads only apply to actual values and combinators of the monad algebra
You seem to argue that, even in a lazy language like Haskell, equational laws should be considered only for values, as if they where stated for a total language. This kind of reasoning is called "fast and loose" in the literature and the conditions under which it is justified are established by Danielsson and others: http://www.cse.chalmers.se/~nad/publications/danielsson-et-al-popl2006.html Sebastian

Thanks for the reference. I base my opinion on my own observations - e.g.
the repeated failures of attempting to model stream processing with
infinite lists, the relative success of modeling exceptions explicitly with
monads compared to use of `fail` or SomeException, etc..
On Mon, Jan 23, 2012 at 6:29 AM, Sebastian Fischer
On Sun, Jan 22, 2012 at 5:25 PM, David Barbour
wrote: The laws for monads only apply to actual values and combinators of the monad algebra
You seem to argue that, even in a lazy language like Haskell, equational laws should be considered only for values, as if they where stated for a total language. This kind of reasoning is called "fast and loose" in the literature and the conditions under which it is justified are established by Danielsson and others:
http://www.cse.chalmers.se/~nad/publications/danielsson-et-al-popl2006.html
Sebastian

Space leaks, time leaks, resource leaks, subtle divergence issues when
filtering lists, etc.
On Mon, Jan 23, 2012 at 11:57 AM, Jake McArthur
On Mon, Jan 23, 2012 at 10:45 AM, David Barbour
wrote: the repeated failures of attempting to model stream processing with infinite lists,
I'm curious about what failures you're talking about.
- Jake

(StrictT op) >>= f = StrictT (op >>= \ x -> x `seq` runStrictT (f x))
Are you sure? Here you evaluate the result, and not the computation itself.
Wouldn't it be:
(StrictT op) >>= f = op ` seq` StrictT (op >>= \x -> runStrictT (f x))
??
2012/1/21 David Barbour
On Sat, Jan 21, 2012 at 10:08 AM, Roman Cheplyaka
wrote: * David Barbour
[2012-01-21 10:01:00-0800] As noted, IO is not strict in the value x, only in the operation that generates x. However, should you desire strictness in a generic way, it would be trivial to model a transformer monad to provide it.
Again, that wouldn't be a monad transformer, strictly speaking, because "monads" it produces violate the left identity law.
It meets the left identity law in the same sense as the Eval monad from Control.Strategies.
http://hackage.haskell.org/packages/archive/parallel/3.1.0.1/doc/html/src/Co...
That is, so long as values at each step can be evaluated to WHNF, it remains true that `return x >>= f` = f x.
I did mess up the def of >>=. I think it should be: (StrictT op) >>= f = StrictT (op >>= \ x -> x `seq` runStrictT (f x))
But I'm not interested enough to actually pull out an interpreter and test...
Regards,
Dave
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Evaluating the argument/result was my intention. Evaluating the computation
itself might be useful in some cases, though.
Regards,
Dave
On Sat, Jan 21, 2012 at 3:20 PM, Yves Parès
(StrictT op) >>= f = StrictT (op >>= \ x -> x `seq` runStrictT (f x))
Are you sure? Here you evaluate the result, and not the computation itself. Wouldn't it be:
(StrictT op) >>= f = op ` seq` StrictT (op >>= \x -> runStrictT (f x))
??
2012/1/21 David Barbour
On Sat, Jan 21, 2012 at 10:08 AM, Roman Cheplyaka
wrote: * David Barbour
[2012-01-21 10:01:00-0800] As noted, IO is not strict in the value x, only in the operation that generates x. However, should you desire strictness in a generic way, it would be trivial to model a transformer monad to provide it.
Again, that wouldn't be a monad transformer, strictly speaking, because "monads" it produces violate the left identity law.
It meets the left identity law in the same sense as the Eval monad from Control.Strategies.
http://hackage.haskell.org/packages/archive/parallel/3.1.0.1/doc/html/src/Co...
That is, so long as values at each step can be evaluated to WHNF, it remains true that `return x >>= f` = f x.
I did mess up the def of >>=. I think it should be: (StrictT op) >>= f = StrictT (op >>= \ x -> x `seq` runStrictT (f x))
But I'm not interested enough to actually pull out an interpreter and test...
Regards,
Dave
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On 21/01/2012 17:29, Victor S. Miller wrote:
The "do" notation translates
do {x<- a;f} into
a>>=(\x -> f)
However when we're working in the IO monad the semantics we want requires that the lambda expression be strict in its argument. So is this a special case for IO? If I wanted this behavior in other monads is there a way to specify that? IO is a special case, but strictness isn't the issue.
The value x cannot be evaluated in concrete form (I think the technical term is "head normal form") until the IO action a has been executed. However, evaluating to head normal form isn't really the key issue. The key issue is that the effects of the action must occur at the correct time. This is why the internals of the IO monad are a "black box" (you can't use pattern matching to sneak a look inside a cheat the evaluation order) and, yes, it's why the IO monad is a bit special. But you could still in principle use a non-strict evaluation order. It's a bit like evaluating (a + b) * c - you don't need to specify strict, lazy or whatever to know that you need to evaluate (a + b) before you can evaluate the (? + c), that aspect of evaluation ordering is fixed anyway. In this case, it's just that instead of being able to rewrite the (\x -> f) to (\someExpression -> f), there is no expression that you can insert there - there is e.g. no unary operator to extract out the result of an action and make it available as a normal value outside the IO context. If there were, it could defeat the whole point of the IO monad. Even so, to see that strictness isn't the issue, imagine that (>>=) were rewritten using a unary executeActionAndExtractResult function. You could easily rewrite your lamba to contain this expression in place of x, without actually evaluating that executeActionAndExtractResult. You'd still be doing a form of composition of IO actions. And when you finally did force the evaluation of the complete composed expression, the ordering of side effects would still be preserved - provided you only used that function as an intermediate step in implementing (>>=) at least. BTW - there's a fair chance I'm still not understanding this correctly myself (still newbie), so wait around to see everyone explain why I'm insane before taking this too seriously.

On 21/01/2012 18:08, Steve Horne wrote:
Even so, to see that strictness isn't the issue, imagine that (>>=) were rewritten using a unary executeActionAndExtractResult function. You could easily rewrite your lamba to contain this expression in place of x, without actually evaluating that executeActionAndExtractResult. You'd still be doing a form of composition of IO actions. And when you finally did force the evaluation of the complete composed expression, the ordering of side effects would still be preserved - provided you only used that function as an intermediate step in implementing (>>=) at least.
Doh!!! - that function *does* exist and is spelled "unsafePerformIO". But AFAIK it isn't used for compilation/interpretation of (>>=) operators. If it *were* used, the rewriting would also need an extra "return". So... a >>= b -> f b becomes... return (f (unsafePerformIO a))
participants (9)
-
David Barbour
-
David Menendez
-
Jake McArthur
-
MigMit
-
Roman Cheplyaka
-
Sebastian Fischer
-
Steve Horne
-
Victor S. Miller
-
Yves Parès