Re: Adding an ignore function to Control.Monad

From: Henning Thielemann
Isaac Dupree schrieb:
On the other hand, maybe it's also an argument to change all the functions like forkIO from :: IO () -> IO ThreadID to :: IO a -> IO ThreadID
I mean, surely they don't rely on the value of a () return-type, other than to pass on to other places that artificially require the ()-type?
No, I think it is already bad enough, that (>>) has type (m a -> m b -> m b) instead of (m () -> m b -> m b). It is like automatically ignoring return values in C. It is too easy to ignore a result that is important.
I agree with Henning on forkIO and the like, but I think the current type of (>>) is right. The name of the function is "anonymous bind" (at least that's what I learned), and its whole purpose of existence is to ignore the value of the first computation while maintaining the context. It would be nice if there were greater differentiation between (>>) and (>>=), though. Ideally, (>>) would be changed to something longer than (>>=) to prevent accidental mis-typing (i.e. on the keyboard). Not that I expect much support for this proposal! John

John Lato wrote:
From: Henning Thielemann
No, I think it is already bad enough, that (>>) has type (m a -> m b -> m b) instead of (m () -> m b -> m b). It is like automatically ignoring return values in C. It is too easy to ignore a result that is important.
I agree with Henning on forkIO and the like, but I think the current type of (>>) is right. The name of the function is "anonymous bind" (at least that's what I learned), and its whole purpose of existence is to ignore the value of the first computation while maintaining the context.
It would be nice if there were greater differentiation between (>>) and (>>=), though. Ideally, (>>) would be changed to something longer than (>>=) to prevent accidental mis-typing (i.e. on the keyboard). Not that I expect much support for this proposal!
The main issue (IMO) is that do notation uses (>>) in the desugaring, which in turn means that it supports statements that just throw away not () results. Ganesh =============================================================================== Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html ===============================================================================

Sittampalam, Ganesh schrieb:
John Lato wrote:
From: Henning Thielemann
No, I think it is already bad enough, that (>>) has type (m a -> m b -> m b) instead of (m () -> m b -> m b). It is like automatically ignoring return values in C. It is too easy to ignore a result that is important. I agree with Henning on forkIO and the like, but I think the current type of (>>) is right. The name of the function is "anonymous bind" (at least that's what I learned), and its whole purpose of existence is to ignore the value of the first computation while maintaining the context.
It would be nice if there were greater differentiation between (>>) and (>>=), though. Ideally, (>>) would be changed to something longer than (>>=) to prevent accidental mis-typing (i.e. on the keyboard). Not that I expect much support for this proposal!
The main issue (IMO) is that do notation uses (>>) in the desugaring, which in turn means that it supports statements that just throw away not () results.
Yes that's my concern. Otherwise, when using (>>) explicitly I agree with John.

On Thu, Jun 11, 2009 at 9:37 AM, Sittampalam,
Ganesh
John Lato wrote:
From: Henning Thielemann
No, I think it is already bad enough, that (>>) has type (m a -> m b -> m b) instead of (m () -> m b -> m b). It is like automatically ignoring return values in C. It is too easy to ignore a result that is important.
I agree with Henning on forkIO and the like, but I think the current type of (>>) is right. The name of the function is "anonymous bind" (at least that's what I learned), and its whole purpose of existence is to ignore the value of the first computation while maintaining the context.
It would be nice if there were greater differentiation between (>>) and (>>=), though. Ideally, (>>) would be changed to something longer than (>>=) to prevent accidental mis-typing (i.e. on the keyboard). Not that I expect much support for this proposal!
The main issue (IMO) is that do notation uses (>>) in the desugaring, which in turn means that it supports statements that just throw away not () results.
How is that a problem? The whole *point* of (>>) is that it discards
unneeded return values.
--
Dave Menendez

David Menendez wrote:
On Thu, Jun 11, 2009 at 9:37 AM, Sittampalam, Ganesh
wrote:
The main issue (IMO) is that do notation uses (>>) in the desugaring, which in turn means that it supports statements that just throw away not () results.
How is that a problem? The whole *point* of (>>) is that it discards unneeded return values.
I think we should avoid having syntax that implicitly does this. So do getLine return 3 should be banned, and users should be forced to write something like do getLine >> ignore return 3 or do _ <- getLine return 3 Ganesh =============================================================================== Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html ===============================================================================

On Thu, 11 Jun 2009, Sittampalam, Ganesh wrote:
David Menendez wrote:
How is that a problem? The whole *point* of (>>) is that it discards unneeded return values.
I think we should avoid having syntax that implicitly does this. So
do getLine return 3
should be banned, and users should be forced to write something like
do getLine >> ignore return 3
or do ignore getLine return 3
or
do _ <- getLine return 3

On Thu, Jun 11, 2009 at 1:53 PM, Sittampalam,
Ganesh
David Menendez wrote:
On Thu, Jun 11, 2009 at 9:37 AM, Sittampalam, Ganesh
wrote: The main issue (IMO) is that do notation uses (>>) in the desugaring, which in turn means that it supports statements that just throw away not () results.
How is that a problem? The whole *point* of (>>) is that it discards unneeded return values.
I think we should avoid having syntax that implicitly does this. So
do getLine return 3
should be banned, and users should be forced to write something like
do getLine >> ignore return 3
or
do _ <- getLine return 3
Ganesh
Again, why do you think that? I much prefer the current syntax.
--
Dave Menendez

On Thu, 11 Jun 2009, David Menendez wrote:
On Thu, Jun 11, 2009 at 1:53 PM, Sittampalam, Ganesh
wrote: do _ <- getLine return 3
Ganesh
Again, why do you think that? I much prefer the current syntax.
http://haskell.org/haskellwiki/Restrict_type_of_monadic_binding http://haskell.org/haskellwiki/Do_notation_considered_harmful#Safety

On Thu, Jun 11, 2009 at 2:57 PM, Henning
Thielemann
On Thu, 11 Jun 2009, David Menendez wrote:
On Thu, Jun 11, 2009 at 1:53 PM, Sittampalam, Ganesh
wrote: do _ <- getLine return 3
Ganesh
Again, why do you think that? I much prefer the current syntax.
http://haskell.org/haskellwiki/Restrict_type_of_monadic_binding http://haskell.org/haskellwiki/Do_notation_considered_harmful#Safety
Aside from assertions that this is a bad thing because you say so,
what evidence do we have that this is a problem?
Furthermore, I don't think it's appropriate for you to use the Haskell
wiki for unmarked opinion pieces like that. It gives new people the
false impression that this is the consensus view of the Haskell
community.
--
Dave Menendez

On Thu, 11 Jun 2009, David Menendez wrote:
On Thu, Jun 11, 2009 at 2:57 PM, Henning Thielemann
wrote: On Thu, 11 Jun 2009, David Menendez wrote:
Again, why do you think that? I much prefer the current syntax.
http://haskell.org/haskellwiki/Restrict_type_of_monadic_binding http://haskell.org/haskellwiki/Do_notation_considered_harmful#Safety
Aside from assertions that this is a bad thing because you say so, what evidence do we have that this is a problem?
The example on the Wiki is ExitCode. It is commonly ignored, but it must be respected, since its value tells whether it makes sense to continue with other actions. Not a good example? I can also ask the question, what are commonly results that you like to ignore by default where ignoring is the right thing? It's quite easy to add 'ignore' where necessary and it safes us from ignoring a result by accident. Avoiding errors is the goal of using types, isn't it?

On Thu, Jun 11, 2009 at 4:22 PM, Henning
Thielemann
On Thu, 11 Jun 2009, David Menendez wrote:
On Thu, Jun 11, 2009 at 2:57 PM, Henning Thielemann
wrote: On Thu, 11 Jun 2009, David Menendez wrote:
Again, why do you think that? I much prefer the current syntax.
http://haskell.org/haskellwiki/Restrict_type_of_monadic_binding http://haskell.org/haskellwiki/Do_notation_considered_harmful#Safety
Aside from assertions that this is a bad thing because you say so, what evidence do we have that this is a problem?
The example on the Wiki is ExitCode. It is commonly ignored, but it must be respected, since its value tells whether it makes sense to continue with other actions. Not a good example?
I'd ask why system is indicating failure through an exit code instead of throwing an exception.
I can also ask the question, what are commonly results that you like to ignore by default where ignoring is the right thing?
You're the one arguing for the change, not me. The most common examples of deliberately ignoring return results are in parsers, where you might see things like, do pChar '(' e <- pExpr pChar ')' return e And before you ask why pChar returns a result, it's because you might do something like pChar '(' `mplus` pChar '[' and want to know which one got returned.
It's quite easy to add 'ignore' where necessary and it safes us from ignoring a result by accident. Avoiding errors is the goal of using types, isn't it?
If that's all there is to it, then why aren't you using Agda or Coq?
--
Dave Menendez

On Thu, 11 Jun 2009, David Menendez wrote:
I'd ask why system is indicating failure through an exit code instead of throwing an exception.
Yes, throwing exception would also be nice. However current Haskell IO system hides the possibility of exceptions and does not tell, which exceptions may occur. To this end, various monads were developed (transformers,mtl:Control.Monad.Error, explicit-exception:Control.Monad.Exception.Synchronous, control-monad-exception). Of course, under the hood they use return values.
It's quite easy to add 'ignore' where necessary and it safes us from ignoring a result by accident. Avoiding errors is the goal of using types, isn't it?
If that's all there is to it, then why aren't you using Agda or Coq?
I assumed, Haskell shall also be developed towards dependent types, which are intended for more safety, aren't they? Nonetheless, I find Agda an interesting project.

On Thu, Jun 11, 2009 at 04:14:22PM -0400, David Menendez wrote:
Aside from assertions that this is a bad thing because you say so, what evidence do we have that this is a problem?
The wiki mentions the problem with forgetting the results of an operation. Another problem is using functions that needlessly produce results because you're interested in the side effects only. For example, do mapM something1 list something2 (Hmmm, would something like {-# RULES "mapM/mapM_" mapM = mapM_ #-} work nicely?) -- Felipe.

Henning, Felipe,
In the end, neither solution (changing the type of (>>) to require explicit
'ignores' vs. coping with the occasionally discarded important result) is
perfect, so the bikeshed is highly unlikely to be repainted, especially
since due to it being initially painted the other way, the preponderance of
existing monadic code would have to rewritten to make any shift in the
typing of (>>).
Even if we had a time machine and wanted to go back and re-spec Monad again
without the burden of prior art, I'm not entirely sure I would be in favor
of enforcing an explicit discard through typing. Somewhat tongue in cheek,
it seems that a 'must keep or consciously discard' parameter is more of the
domain of a substructural type system than a library.
That said, I do support the creation of an explicit ignore method, if only
because the '() <$' idiom is less obvious in its intent at first glance and
that the applications of ignore go beyond (>>), because of the types of
mplus and (<|>).
-Edward Kmett
On Thu, Jun 11, 2009 at 4:26 PM, Felipe Lessa
On Thu, Jun 11, 2009 at 04:14:22PM -0400, David Menendez wrote:
Aside from assertions that this is a bad thing because you say so, what evidence do we have that this is a problem?
The wiki mentions the problem with forgetting the results of an operation. Another problem is using functions that needlessly produce results because you're interested in the side effects only. For example,
do mapM something1 list something2
(Hmmm, would something like
{-# RULES "mapM/mapM_" mapM = mapM_ #-}
work nicely?)
-- Felipe. _______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

On Thu, Jun 11, 2009 at 4:26 PM, Felipe Lessa
On Thu, Jun 11, 2009 at 04:14:22PM -0400, David Menendez wrote:
Aside from assertions that this is a bad thing because you say so, what evidence do we have that this is a problem?
The wiki mentions the problem with forgetting the results of an operation.
This is exactly what I'm talking about. That page is written almost entirely by Henning Thielemann, but because it's on the official Haskell wiki, it seems authoritative.
Another problem is using functions that needlessly produce results because you're interested in the side effects only. For example,
do mapM something1 list something2
I do agree that someone is less likely to accidently write do ignore (mapM something1 list) something2 But this is generally less dangerous than, say, using foldl instead of foldl'.
(Hmmm, would something like
{-# RULES "mapM/mapM_" mapM = mapM_ #-}
work nicely?)
Unfortunately, I don't think that rule would ever be applied, because
the types of mapM and mapM_ don't match.
You could have a rule that transformed ignore (mapM x y) to mapM_ x y, though.
--
Dave Menendez

David Menendez wrote:
On Thu, Jun 11, 2009 at 4:26 PM, Felipe Lessa
wrote: On Thu, Jun 11, 2009 at 04:14:22PM -0400, David Menendez wrote:
Aside from assertions that this is a bad thing because you say so, what evidence do we have that this is a problem? The wiki mentions the problem with forgetting the results of an operation.
This is exactly what I'm talking about. That page is written almost entirely by Henning Thielemann, but because it's on the official Haskell wiki, it seems authoritative.
It's a wiki, I think it should document problems even if not everyone agrees about them; I think it's our tradition to scatter pages with conflicting viewpoints around our wiki (and even cross-link them to each other). Please don't think things on the haskell-wiki are authoritative; I think they're there so that there is the potential to collaborate on them. That page even has links to some other people expressing the same frustrations. Feel free to update it sympathetically if we've discussed something here that would strengthen it as a page (and/or, is there a "do notation considered helpful" page?). Maybe something about "ignore" would strengthen it?; I'm not sure; or references to stronger languages like Agda where "forgetting" is a more crucial concept. -Isaac

On 11/06/2009 22:56, Isaac Dupree wrote:
David Menendez wrote:
On Thu, Jun 11, 2009 at 4:26 PM, Felipe Lessa
wrote: On Thu, Jun 11, 2009 at 04:14:22PM -0400, David Menendez wrote:
Aside from assertions that this is a bad thing because you say so, what evidence do we have that this is a problem? The wiki mentions the problem with forgetting the results of an operation.
This is exactly what I'm talking about. That page is written almost entirely by Henning Thielemann, but because it's on the official Haskell wiki, it seems authoritative.
It's a wiki, I think it should document problems even if not everyone agrees about them; I think it's our tradition to scatter pages with conflicting viewpoints around our wiki (and even cross-link them to each other). Please don't think things on the haskell-wiki are authoritative; I think they're there so that there is the potential to collaborate on them. That page even has links to some other people expressing the same frustrations. Feel free to update it sympathetically if we've discussed something here that would strengthen it as a page (and/or, is there a "do notation considered helpful" page?). Maybe something about "ignore" would strengthen it?; I'm not sure; or references to stronger languages like Agda where "forgetting" is a more crucial concept.
I think it would help if there was a standard box we could put at the top of these sort of pages, something like This page addresses an aspect of Haskell style, and the viewpoints expressed here may not be universally accepted within the community. To discuss the contents of this page, use the libraries@haskell.org mailing list. Cheers, Simon

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 On Fri, Jun 12, 2009 at 7:49 AM, Simon Marlow wrote:
I think it would help if there was a standard box we could put at the top of these sort of pages, something like
This page addresses an aspect of Haskell style, and the viewpoints expressed here may not be universally accepted within the community. To discuss the contents of this page, use the libraries@haskell.org mailing list.
Cheers, Simon
Your wish is my command: {{essay}} http://haskell.org/haskellwiki/Template:Essay http://haskell.org/haskellwiki/?title=Restrict_type_of_monadic_binding&curid=3366&diff=28583&oldid=16749 http://haskell.org/haskellwiki/?title=Do_notation_considered_harmful&curid=3362&diff=28584&oldid=26327 - -- gwern -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEAREKAAYFAkoyXlEACgkQvpDo5Pfl1oIkVwCfY9QJOGLiUiA75HyaenGquqJp szUAnjB5Hs1PfrLGKbO7EDa+FcFDO1ue =suLb -----END PGP SIGNATURE-----

On Thu, Jun 11, 2009 at 08:57:31PM +0200, Henning Thielemann wrote:
On Thu, 11 Jun 2009, David Menendez wrote:
On Thu, Jun 11, 2009 at 1:53 PM, Sittampalam, Ganesh
wrote: do _ <- getLine return 3
Ganesh
Again, why do you think that? I much prefer the current syntax.
http://haskell.org/haskellwiki/Restrict_type_of_monadic_binding http://haskell.org/haskellwiki/Do_notation_considered_harmful#Safety
And here I always get annoyed by types that specify '()' explicitly when a universally quantified type will do. (`when` and `unless` are particularly offensive in this regard). Forcing idioms to be more verbose than necessary obscures intent. Do we have any data that accidentally ignoring return values is a problem in practice? I don't mean as it confused someone once and then they learned their lesson. Because changing the type of (>>) would imply extra effort always for everyone, whereas remembering that return values might be important isn't exactly a recurring problem. It may save someone a bug once when using System.system, but cause enough other code to be obscured in meaning to hide a dozen other bugs. The fact you currently don't assign a value to something means you probably would have just mistakenly used 'ignore' in any case. Also, treating '()' specially as 'don't care' as opposed to any other unit type doesn't sit right with me either. Sure it is an obvious choice for a don't care value, but the fact you can use others is nicely consistent with the idea that other than having some syntatic sugar, built in types are not special in haskell. John -- John Meacham - ⑆repetae.net⑆john⑈ - http://notanumber.net/

John Meacham wrote:
And here I always get annoyed by types that specify '()' explicitly when a universally quantified type will do. (`when` and `unless` are particularly offensive in this regard). Forcing idioms to be more verbose than necessary obscures intent.
Do we have any data that accidentally ignoring return values is a problem in practice?
On the flip side, could you give some examples where ignoring return values is useful and natural? Cheers, Ganesh =============================================================================== Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html ===============================================================================

On Thu, Jun 11, 2009 at 11:21:54PM +0100, Sittampalam, Ganesh wrote:
John Meacham wrote:
And here I always get annoyed by types that specify '()' explicitly when a universally quantified type will do. (`when` and `unless` are particularly offensive in this regard). Forcing idioms to be more verbose than necessary obscures intent.
Do we have any data that accidentally ignoring return values is a problem in practice?
On the flip side, could you give some examples where ignoring return values is useful and natural?
certainly, the very useful parser example mentioned. (char 'x' >> char 'y') - we want to parse x, then y (char 'x' `mplus` char 'y') - we want to parse one of x or y, returning which one we got so we can change behavior based on it. When using the 'Writer' monad or similar it comes up a whole lot. sometimes you are interested in the result, sometimes the written value, sometimes both. there are a lot more monads out there than 'IO', or even generalized state monads. The initial thread took a lesson from the C language and tried to apply it to all monads in general, which is certainly flawed. Monads are way more interesting than C program flow, and even C explicitly gave up on '(void)' casts for function returns on purpose. John -- John Meacham - ⑆repetae.net⑆john⑈ - http://notanumber.net/

On Thu, 11 Jun 2009, John Meacham wrote:
On Thu, Jun 11, 2009 at 11:21:54PM +0100, Sittampalam, Ganesh wrote:
On the flip side, could you give some examples where ignoring return values is useful and natural?
certainly, the very useful parser example mentioned.
(char 'x' >> char 'y') - we want to parse x, then y
Are you really interested to only ignore the result of the first 'char' parser and maintain the result of the second one?
(char 'x' `mplus` char 'y') - we want to parse one of x or y, returning which one we got so we can change behavior based on it.
Indeed using the same 'char' parser in both cases is convenient. It seems that parser monads are different from IO monad, in that it is more common to ignore results. (Or parser monads are developed with the simplicity of ignoring results in mind.) With char :: Char -> Parser Char ignore :: Functor m => m a -> m Ignore (or m ()) ignore = fmap (const Ignore) we would have to write ignore (char 'x') >> char 'y' char 'x' `mplus` char 'y' With char :: Char -> Parser Ignore (or Parser ()) echo :: Functor m => (a -> m Ignore) -> (a -> m a) echo f x = fmap (const x) $ f x we would write char 'x' >> char 'y' echo char 'x' `mplus` echo char 'y' If the parser library anticipates frequent use of ignore it could define a type class. class IgnorableChar a where wrapChar :: Char -> a class IgnorableChar Ignore where wrapChar _ = Ignore class IgnorableChar Char where wrapChar = id char :: IgnorableChar char => Char -> Parser char Cumbersome to define, but as simple to use as today's 'char' with today's '>>'. Just as a compromise for parser libraries. Ok, all these suggestions make things more complicated. Convenience vs. safety - It's certainly a matter of taste which one is more important for you.
When using the 'Writer' monad or similar it comes up a whole lot. sometimes you are interested in the result, sometimes the written value, sometimes both.
I think one should use Monoid class directly whereever possible. I have seen frequently that people use Writer monad instead of Monoid only because of the do notation.

Hello Ganesh, Friday, June 12, 2009, 2:21:54 AM, you wrote:
On the flip side, could you give some examples where ignoring return values is useful and natural?
a lots of! let dll_register mode = when (oldContextMenu /= contextMenu) $ do runProgram$ "regsvr32 "++mode++" /s /c \""++(shext > "ArcShellExt.dll\"") runProgram$ "regsvr32 "++mode++" /s /c \""++(shext > "ArcShellExt-64.dll\"") return () here, runProgram returns program stdout. and, in runProgram implementation: forkIO (hGetContents stderr >>= evaluate.length >> return ()) or this: forkIO_ action = forkIO action >> return () again, used inside when -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

John Meacham wrote:
And here I always get annoyed by types that specify '()' explicitly when a universally quantified type will do. (`when` and `unless` are particularly offensive in this regard). Forcing idioms to be more verbose than necessary obscures intent.
Indeed. In my personal libraries for scripting Haskell I define: infixr 8 `returning` {-# INLINE returning #-} returning :: (Monad m) => (a -> m b) -> (a -> m a) f `returning` x = f x >> return x precisely to get around this annoyance. The one I find particularly offensive is MonadState.put. In general, I find the use of () for what C-like languages would call void is a sign of excessive imperativism. If C had any measure of compositionality then it wouldn't be returning void either. Indeed, you can often see this corrected in the style of OOP where putatively void method returns the current object instead, so that they support method chaining, aka applicative style. There are certainly functions which should return () ---e.g. with the OOP analogy again, methods to close a file handle, destroy an object, etc--- but functions which should accept only () seem quite rare.
Do we have any data that accidentally ignoring return values is a problem in practice?
There is the memory leak (or stack overflow) problems of using mapM or sequence when the result is unused. I'm not sure if these fall under your "confused someone once" category, but they can be pernicious. This is potentially solvable by the compiler rewriting to mapM_ and sequence_ when it detects the result is unused; though whether that's the best solution is unclear. -- Live well, ~wren

On Thu, 11 Jun 2009, John Meacham wrote:
Also, treating '()' specially as 'don't care' as opposed to any other unit type doesn't sit right with me either. Sure it is an obvious choice for a don't care value, but the fact you can use others is nicely consistent with the idea that other than having some syntatic sugar, built in types are not special in haskell.
Agreed, it would be certainly better to have a special type like: data Ignore = Ignore

Hello Henning, Friday, August 14, 2009, 8:55:58 PM, you wrote:
Also, treating '()' specially as 'don't care' as opposed to any other unit type doesn't sit right with me either. Sure it is an obvious choice for a don't care value, but the fact you can use others is nicely consistent with the idea that other than having some syntatic sugar, built in types are not special in haskell.
Agreed, it would be certainly better to have a special type like:
data Ignore = Ignore
and what () means, as you think? -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com
participants (12)
-
Bulat Ziganshin
-
David Menendez
-
Edward Kmett
-
Felipe Lessa
-
Gwern Branwen
-
Henning Thielemann
-
Isaac Dupree
-
John Lato
-
John Meacham
-
Simon Marlow
-
Sittampalam, Ganesh
-
wren ng thornton