Re: [Haskell] Types of when and unless in Control.Monad

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
24 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Henning Thielemann
moving to [hidden email] ...

Andreas Abel schrieb:

> In Control.Monad, when has type
>
>   when :: Monad m => Bool -> m () -> m ()
>
> I think this type should be generalized to
>
>   when :: Monad m => Bool -> m a -> m ()
>
> to avoid silly "return ()" statements like in
>
>   when cond $ do
>     monadicComputationWhoseResultIWantToDiscard
>     return ()

You may use the new 'void' function for that purpose.

Generally I prefer to not ignore monadic results by default. I also do
not like that behavior in the C programming language. GHC now warns
about unused non-() results in a do-block. But (>>) and mapM_ still
ignore non-unit results of monadic action, and certainly have to remain
this way because of Haskell-98 compatibility.

If a monadic action has a result, then this has a reason. If I have to
apply a 'void' then I think about whether it is correct to ignore the
result. E.g. if the result is an ExitCode then it may be convenient to
ignore it (like in C), but it is certainly not correct to do so.

It was often stated that for parsers ignoring of the parsed results by
default is a good idea. However since GHC warns about implicitly ignored
monadic results in do-notation, I found that many warnings in parsers
are due to not using the right parser combinators. E.g. I often found
something like

  do char '('
     x <- expression
     char ')'
     return x

and this should certainly be rewritten to
  between (char '(') (char ')') expression

That is, those warnings about implicitly ignored results helped me to
improve my code.

> P.S.:  A more systematic solution would be to change the Haskell
> language by either introducing a Top type which is the supertype of
> everything and use it instead of ().

There is the type Any, but I suspect it cannot be written in source
code. But I think a type variable like 'a' is the right thing to use.


_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Dan Burton
> P.S.:  A more systematic solution would be to change the Haskell
> language by either introducing a Top type which is the supertype of
> everything and use it instead of ().

Haskell doesn't have subtyping, so this wouldn't really make sense. Perhaps a typeclass Top would make sense (where the compiler automatically creates an instance of it for all types), but then `Top a => a` would be equivalent to just `a`, so it wouldn't really serve any purpose.

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Henning Thielemann
In reply to this post by Henning Thielemann

On Sat, 21 Apr 2012, Andreas Abel wrote:

> While your comments about not ignoring results of functions may be right, in
> the particular case of when and unless the situation is different.  Since
> both are basically an if-then without an else, the expression *cannot* yield
> a result, just execute the monadic effect. Thus, confusion is unlikely.

I don't understand this argument. The current type of 'when' is

> when :: (Monad m) => Bool -> m () -> m ()

thus it makes sure, that the enclosed action has no result that could be
ignored by accident. If the function would be

> when_ :: (Monad m) => Bool -> m a -> m ()

then you could write

> when_ b (system "command")

and thus miss the ExitCode of 'system' by accident.

> I have defined myself when_ and unless_ but I think it is silly, the library
> functions could be more general.

Since you think that the current 'when's type is silly, I would like to
know what are your examples where you want to ignore the result of the
enclosed action?

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Henning Thielemann
In reply to this post by Henning Thielemann

moving to libraries, again ...


On Sun, 22 Apr 2012, Julian Gilbey wrote:

> On Sat, Apr 21, 2012 at 08:28:27PM -0500, Strake wrote:
>> On 21/04/2012, Andreas Abel <[hidden email]> wrote:
>> > to avoid silly "return ()" statements like in
>> >
>> >    when cond $ do
>> >      monadicComputationWhoseResultIWantToDiscard
>> >      return ()
>>
>> (when cond ∘ void) monadicComputationWhoseResultIWantToDiscard
>> or
>> when cond $ () <$ monadicComputationWhoseResultIWantToDiscard
>
> How is that simpler than
>
>  when cond monadicComputationWhoseResultIWantToDiscard
>
> which it would be with the suggested new type?

The original poster wanted to make it simpler, others want to let it be
safer.


_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Ivan Lazar Miljenovic
In reply to this post by Henning Thielemann
On 22 April 2012 21:39, Christian Höner zu Siederdissen
<[hidden email]> wrote:

> * Julian Gilbey <[hidden email]> [22.04.2012 09:22]:
>> On Sat, Apr 21, 2012 at 08:28:27PM -0500, Strake wrote:
>> > On 21/04/2012, Andreas Abel <[hidden email]> wrote:
>> > > to avoid silly "return ()" statements like in
>> > >
>> > >    when cond $ do
>> > >      monadicComputationWhoseResultIWantToDiscard
>> > >      return ()
>> >
>> > (when cond ∘ void) monadicComputationWhoseResultIWantToDiscard
>> > or
>> > when cond $ () <$ monadicComputationWhoseResultIWantToDiscard
>>
>> How is that simpler than
>>
>>   when cond monadicComputationWhoseResultIWantToDiscard
>>
>> which it would be with the suggested new type?
>>
>>    Julian
>
> Wouldn't "when_" and "unless_" or similar be better? I'd probably like
> to have the compiler annoy me, since it is not clear that I want to
> discard the result. If I really want to discard, I should have to make
> it clear as there is probably a good reason for the inner function to
> return a result in the first place?

Agreed; I'm not sure if I agree with having such functionality
(Henning makes some good points), but if people deem it desirable then
I think it would be better to have them with new names for the reasons
you state.

--
Ivan Lazar Miljenovic
[hidden email]
http://IvanMiljenovic.wordpress.com

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Henning Thielemann

On Sun, 22 Apr 2012, Ivan Lazar Miljenovic wrote:

> On 22 April 2012 21:39, Christian Höner zu Siederdissen <[hidden email]> wrote:
>
>> Wouldn't "when_" and "unless_" or similar be better? I'd probably like
>> to have the compiler annoy me, since it is not clear that I want to
>> discard the result. If I really want to discard, I should have to make
>> it clear as there is probably a good reason for the inner function to
>> return a result in the first place?
>
> Agreed; I'm not sure if I agree with having such functionality
> (Henning makes some good points), but if people deem it desirable then
> I think it would be better to have them with new names for the reasons
> you state.
The underscores would also be consistent with mapM_, forM_ and so on.

But please still count me as -1.


_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Andreas Abel
In reply to this post by Ivan Lazar Miljenovic
On 04/22/2012 01:49 PM, Ivan Lazar Miljenovic wrote:

> On 22 April 2012 21:39, Christian Höner zu Siederdissen
> <[hidden email]>  wrote:
>> * Julian Gilbey<[hidden email]>  [22.04.2012 09:22]:
>>> On Sat, Apr 21, 2012 at 08:28:27PM -0500, Strake wrote:
>>>> On 21/04/2012, Andreas Abel<[hidden email]>  wrote:
>>>>> to avoid silly "return ()" statements like in
>>>>>
>>>>>     when cond $ do
>>>>>       monadicComputationWhoseResultIWantToDiscard
>>>>>       return ()
>>>>
>>>> (when cond ∘ void) monadicComputationWhoseResultIWantToDiscard
>>>> or
>>>> when cond $ ()<$ monadicComputationWhoseResultIWantToDiscard
>>>
>>> How is that simpler than
>>>
>>>    when cond monadicComputationWhoseResultIWantToDiscard
>>>
>>> which it would be with the suggested new type?
>>>
>>>     Julian
>>
>> Wouldn't "when_" and "unless_" or similar be better? I'd probably like
>> to have the compiler annoy me, since it is not clear that I want to
>> discard the result. If I really want to discard, I should have to make
>> it clear as there is probably a good reason for the inner function to
>> return a result in the first place?
>
> Agreed; I'm not sure if I agree with having such functionality
> (Henning makes some good points), but if people deem it desirable then
> I think it would be better to have them with new names for the reasons
> you state.

Mmh, this discussion has cooled down, but I just found your answers
which had been stashed away by my mail agent, and I feel I have to reply...

Concerning the suggestion that when_ would be in sync with forM_ and
whenM_ I'd say: not really.  forM_ and whenM_ discard the result of the
monadic computation, while when and when_ do not even have such a
result.  They always just perform some monadic effect and return nothing.

Repeating myself, 'when' can never have a result, since it is an if-then
without an else.  Thus, it is a proper command; and if you want to have
a conditional monadic computation which does return a result, you can
simply not use 'when' or 'unless', logic forces you to use 'if' or 'ifM'.

I do not understand the worries that one could accidentially use 'when'
with a monadic computation whose result one actually cares for.  If that
was the intention of the library designers, they should have given many
other function a more specific type, most prominently

   >> :: m () -> m b -> b

That would have ensured that you cannot discard the result of the first
computation by accident.  But would you want to work with this?  My
answer is no.

Other types that would be changed to implement this kind of safety
policy are:

   mapM_ :: (a -> m ()) -> [a] -> m ()
   forM_ :: [a] -> (a -> m ()) -> m ()
   sequence_ :: [m ()] -> m ()
   forever :: m () -> m ()

and many more, like zipWithM_, foldM_, replicateM_.

Sorry, but I think all these function have been given their maximal
general type

   ==> to be able to ignore a result of a monadic computation

   ==> without further noise.

In my opinion, the types of when and unless are not general enough, an
that is, imho, just an accident of history.  Because it is the type that
inferred for the shortest definition, which is

   when cond m = if cond then m else return ()

Please reevaluate my proposal to change to

   when   :: Bool -> m a -> m ()
   unless :: Bool -> m a -> m ()

in the light of the above arguments.

Cheers,
Andreas

--
Andreas Abel  <><      Du bist der geliebte Mensch.

Theoretical Computer Science, University of Munich
Oettingenstr. 67, D-80538 Munich, GERMANY

[hidden email]
http://www2.tcs.ifi.lmu.de/~abel/

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Henning Thielemann

On Mon, 4 Jun 2012, Andreas Abel wrote:

> Concerning the suggestion that when_ would be in sync with forM_ and whenM_
> I'd say: not really.  forM_ and whenM_ discard the result of the monadic
> computation, while when and when_ do not even have such a result.  They
> always just perform some monadic effect and return nothing.

What is whenM_ ? Do you mean mapM_ ?


>  >> :: m () -> m b -> b
>
>  mapM_ :: (a -> m ()) -> [a] -> m ()
>  forM_ :: [a] -> (a -> m ()) -> m ()
>  sequence_ :: [m ()] -> m ()
>  forever :: m () -> m ()
>
> and many more, like zipWithM_, foldM_, replicateM_.

I would prefer these strict types, too.

Alternatively I have wondered in the past whether it might be a good idea
to generalize them to:

>  mapM_ :: Monoid b => (a -> m b) -> [a] -> m b
>  forM_ :: Monoid b => [a] -> (a -> m b) -> m b
>  sequence_ :: Monoid b => [m b] -> m b
>  forever :: Monoid b => m b -> m b

This would still propagate monadic result type () if the final monadic
action has result type ().

http://www.haskell.org/pipermail/haskell-cafe/2009-January/054243.html

> Sorry, but I think all these function have been given their maximal general
> type
>
>  ==> to be able to ignore a result of a monadic computation
>
>  ==> without further noise.

Since the addition of 'void' the noise has become acceptable for me.

I would follow a kind of "separation of concerns". Ignoring results is one
step and performing forM_, when etc. is the second step.


> In my opinion, the types of when and unless are not general enough, an that
> is, imho, just an accident of history.  Because it is the type that inferred
> for the shortest definition, which is
>
>  when cond m = if cond then m else return ()
>
> Please reevaluate my proposal to change to
>
>  when   :: Bool -> m a -> m ()
>  unless :: Bool -> m a -> m ()
>
> in the light of the above arguments.


Last time I asked the question, what application you do have in mind. Is
your code cluttered with void's or do you just need it occasionally?

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Henning Thielemann

On Mon, 4 Jun 2012, Henning Thielemann wrote:

> On Mon, 4 Jun 2012, Andreas Abel wrote:
>
>> Concerning the suggestion that when_ would be in sync with forM_ and whenM_
>> I'd say: not really.  forM_ and whenM_ discard the result of the monadic
>> computation, while when and when_ do not even have such a result.  They
>> always just perform some monadic effect and return nothing.
>
> What is whenM_ ? Do you mean mapM_ ?

Ah, I think whenM_ was this one

   whenM_ :: m Bool -> m a -> m ()

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Andres Löh-2
In reply to this post by Andreas Abel
> Please reevaluate my proposal to change to
>
>  when   :: Bool -> m a -> m ()
>  unless :: Bool -> m a -> m ()
>
> in the light of the above arguments.

I strongly support this proposal.

Cheers,
  Andres

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Andreas Abel
In reply to this post by Andreas Abel
On 06/04/2012 08:47 PM, Christian Höner zu Siederdissen wrote:

> Hi Andreas,
>
> I think you missed our point. Say:
>
> gearDown :: IO Bool
> landPlane :: IO ()
>
> do
>    g<- gearDown
>    if g
>    then landPlane
>    else error "we're all gonna die, bail out"
>
> But now, I am really lazy (in a bad way)
>
> do
>    when gearDown
>    landPlane

You probably meant to write

   do gearDown
      landPlane

(Since 'when' expects a pure Bool, the code you wrote does not
type-check.)  Well, you can ignore results in a 'do', but this has
nothing to do with 'when'.  There is a ghc flag

   -fwarn-unused-do-bind

that prevents you from ignoring results silently, in this case you would
have to write

   do _ <- gearDown
      landPlane

to make it obvious that you ignore a result of a monadic computation.
Maybe you prefer this style, but I would like the freedom to silently
ignore unneeded results.

> I can not now accidentally land my plane if my gear ist stuck in "up"
> (returned false). If "when" suppresses the return value, the compiler
> does not warn you if you use "when" together with a function that
> returns something important.

It seems you have confused condition- and then-part of 'when'.

> My point is /not/ that I want the result of "when", but that I want to
> know that I am currently suppressing the result of the inner action,
> which could be disastrous.
>
> And simply adding "when_" is the safest (one of the safer?) way of
> dealing with your wish.

The reason to have a mapM_ additionally to mapM is that in some cases,
e.g. as last statement of a do block of type m (), mapM does not type check.

However, generalizing the type of 'when' does not break any programs,
and then a when_ is superfluous.

More in an answer to Henning...

Cheers,
Andreas

> * Andreas Abel<[hidden email]>  [04.06.2012 20:36]:
>> On 04/22/2012 01:49 PM, Ivan Lazar Miljenovic wrote:
>>> On 22 April 2012 21:39, Christian Höner zu Siederdissen
>>> <[hidden email]>   wrote:
>>>> * Julian Gilbey<[hidden email]>   [22.04.2012 09:22]:
>>>>> On Sat, Apr 21, 2012 at 08:28:27PM -0500, Strake wrote:
>>>>>> On 21/04/2012, Andreas Abel<[hidden email]>   wrote:
>>>>>>> to avoid silly "return ()" statements like in
>>>>>>>
>>>>>>>     when cond $ do
>>>>>>>       monadicComputationWhoseResultIWantToDiscard
>>>>>>>       return ()
>>>>>>
>>>>>> (when cond ∘ void) monadicComputationWhoseResultIWantToDiscard
>>>>>> or
>>>>>> when cond $ ()<$ monadicComputationWhoseResultIWantToDiscard
>>>>>
>>>>> How is that simpler than
>>>>>
>>>>>    when cond monadicComputationWhoseResultIWantToDiscard
>>>>>
>>>>> which it would be with the suggested new type?
>>>>>
>>>>>     Julian
>>>>
>>>> Wouldn't "when_" and "unless_" or similar be better? I'd probably like
>>>> to have the compiler annoy me, since it is not clear that I want to
>>>> discard the result. If I really want to discard, I should have to make
>>>> it clear as there is probably a good reason for the inner function to
>>>> return a result in the first place?
>>>
>>> Agreed; I'm not sure if I agree with having such functionality
>>> (Henning makes some good points), but if people deem it desirable then
>>> I think it would be better to have them with new names for the reasons
>>> you state.
>>
>> Mmh, this discussion has cooled down, but I just found your answers
>> which had been stashed away by my mail agent, and I feel I have to
>> reply...
>>
>> Concerning the suggestion that when_ would be in sync with forM_ and
>> whenM_ I'd say: not really.  forM_ and whenM_ discard the result of
>> the monadic computation, while when and when_ do not even have such
>> a result.  They always just perform some monadic effect and return
>> nothing.
>>
>> Repeating myself, 'when' can never have a result, since it is an
>> if-then without an else.  Thus, it is a proper command; and if you
>> want to have a conditional monadic computation which does return a
>> result, you can simply not use 'when' or 'unless', logic forces you
>> to use 'if' or 'ifM'.
>>
>> I do not understand the worries that one could accidentially use
>> 'when' with a monadic computation whose result one actually cares
>> for.  If that was the intention of the library designers, they
>> should have given many other function a more specific type, most
>> prominently
>>
>>    >>  :: m () ->  m b ->  b
>>
>> That would have ensured that you cannot discard the result of the
>> first computation by accident.  But would you want to work with
>> this?  My answer is no.
>>
>> Other types that would be changed to implement this kind of safety
>> policy are:
>>
>>    mapM_ :: (a ->  m ()) ->  [a] ->  m ()
>>    forM_ :: [a] ->  (a ->  m ()) ->  m ()
>>    sequence_ :: [m ()] ->  m ()
>>    forever :: m () ->  m ()
>>
>> and many more, like zipWithM_, foldM_, replicateM_.
>>
>> Sorry, but I think all these function have been given their maximal
>> general type
>>
>>    ==>  to be able to ignore a result of a monadic computation
>>
>>    ==>  without further noise.
>>
>> In my opinion, the types of when and unless are not general enough,
>> an that is, imho, just an accident of history.  Because it is the
>> type that inferred for the shortest definition, which is
>>
>>    when cond m = if cond then m else return ()
>>
>> Please reevaluate my proposal to change to
>>
>>    when   :: Bool ->  m a ->  m ()
>>    unless :: Bool ->  m a ->  m ()
>>
>> in the light of the above arguments.
>>
>> Cheers,
>> Andreas
>>
>> --
>> Andreas Abel<><       Du bist der geliebte Mensch.
>>
>> Theoretical Computer Science, University of Munich
>> Oettingenstr. 67, D-80538 Munich, GERMANY
>>
>> [hidden email]
>> http://www2.tcs.ifi.lmu.de/~abel/


--
Andreas Abel  <><      Du bist der geliebte Mensch.

Theoretical Computer Science, University of Munich
Oettingenstr. 67, D-80538 Munich, GERMANY

[hidden email]
http://www2.tcs.ifi.lmu.de/~abel/

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Andreas Abel
In reply to this post by Henning Thielemann
On 06/04/2012 09:25 PM, Henning Thielemann wrote:
>
> On Mon, 4 Jun 2012, Andreas Abel wrote:
>
>> Concerning the suggestion that when_ would be in sync with forM_ and
>> whenM_ I'd say: not really. forM_ and whenM_ discard the result of the
>> monadic computation, while when and when_ do not even have such a
>> result. They always just perform some monadic effect and return nothing.
>
> What is whenM_ ? Do you mean mapM_ ?

Probably...

>> >> :: m () -> m b -> b
>>
>> mapM_ :: (a -> m ()) -> [a] -> m ()
>> forM_ :: [a] -> (a -> m ()) -> m ()
>> sequence_ :: [m ()] -> m ()
>> forever :: m () -> m ()
>>
>> and many more, like zipWithM_, foldM_, replicateM_.
>
> I would prefer these strict types, too.

Ok, well, then you probably would not want to use mapM_ and the like,
but instead

   mapM (void . f) l

which instantiates the type b to (), giving above typings.

> Alternatively I have wondered in the past whether it might be a good
> idea to generalize them to:
>
>> mapM_ :: Monoid b => (a -> m b) -> [a] -> m b
>> forM_ :: Monoid b => [a] -> (a -> m b) -> m b
>> sequence_ :: Monoid b => [m b] -> m b
>> forever :: Monoid b => m b -> m b
>
> This would still propagate monadic result type () if the final monadic
> action has result type ().
>
> http://www.haskell.org/pipermail/haskell-cafe/2009-January/054243.html

But that would not be backwards compatible.

>> Sorry, but I think all these function have been given their maximal
>> general type
>>
>> ==> to be able to ignore a result of a monadic computation
>>
>> ==> without further noise.
>
> Since the addition of 'void' the noise has become acceptable for me.
>
> I would follow a kind of "separation of concerns". Ignoring results is
> one step and performing forM_, when etc. is the second step.

Ok.  But I am really surprised that an operation should not get the
maximally sensible type.

It seems that I like to think of () as a terminal type (in the sense of
category theory), but this intuition is not shared by everyone.  [I am
aware that () is NOT the terminal type in Haskell.]

>> In my opinion, the types of when and unless are not general enough, an
>> that is, imho, just an accident of history. Because it is the type
>> that inferred for the shortest definition, which is
>>
>> when cond m = if cond then m else return ()
>>
>> Please reevaluate my proposal to change to
>>
>> when :: Bool -> m a -> m ()
>> unless :: Bool -> m a -> m ()
>>
>> in the light of the above arguments.
>
>
> Last time I asked the question, what application you do have in mind. Is
> your code cluttered with void's or do you just need it occasionally?

The application is that I have a function that provides a resource that
may be present or not.  If it is not present, an exception is thrown
(that is the monadic effect).  If I am just interested in checking the
presence of the resource, I can call this function in a do-block.  But I
am not allowed to call it in a 'when', without 'void'ing it.  That is
counterintuitive.

To reconcile the 'strict' vs. 'liberal' programmers, it seems that
library functions need to have different types depending on whether

   -fwarn-unused-do-bind

is set or not...

Cheers,
Andreas

--
Andreas Abel  <><      Du bist der geliebte Mensch.

Theoretical Computer Science, University of Munich
Oettingenstr. 67, D-80538 Munich, GERMANY

[hidden email]
http://www2.tcs.ifi.lmu.de/~abel/

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Henning Thielemann
In reply to this post by Andreas Abel

On Tue, 5 Jun 2012, Andreas Abel wrote:

> The reason to have a mapM_ additionally to mapM is that in some cases, e.g.
> as last statement of a do block of type m (), mapM does not type check.

The functions mapM and mapM_ are really distinct. That's why they are
generalized in different modules:
   mapM_ is generalized in Foldable
   mapM is generalized in Traversable

Using mapM instead of mapM_ can lead to a memory leak and I think the
confusion between mapM and mapM_ was the initial reason to add the
unused-binding warning to GHC. And it actually helped me spotting a lot of
calls to mapM that have to be calls to mapM_.

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Andres Löh
> Using mapM instead of mapM_ can lead to a memory leak and I think the
> confusion between mapM and mapM_ was the initial reason to add the
> unused-binding warning to GHC. And it actually helped me spotting a lot of
> calls to mapM that have to be calls to mapM_.

The different space behaviour is a very good reason to make the
distinction for mapM and mapM_. However, it's not a good reason to
choose arbitrarily restrictive types for perfectly generic functions
in other cases IMHO. At least I don't see that there's a performance
reason not to generalize when or unless, as their implementation would
stay the same.

I'd argue that making functions more restrictive than necessary isn't
an indication of safety, it's just confusing :)

Cheers,
  Andres

--
Andres Löh, Haskell Consultant
Well-Typed LLP, http://www.well-typed.com

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Henning Thielemann
In reply to this post by Andreas Abel

On Tue, 5 Jun 2012, Andreas Abel wrote:

>> I would prefer these strict types, too.
>
> Ok, well, then you probably would not want to use mapM_ and the like, but
> instead
>
>  mapM (void . f) l

This would result in 'm [()]' not in 'm ()'. I could e.g. get information
from the number elements in the [()] list, but () tells me nothing. Thus
they are really different.


>> Alternatively I have wondered in the past whether it might be a good
>> idea to generalize them to:
>>
>>> mapM_ :: Monoid b => (a -> m b) -> [a] -> m b
>>> forM_ :: Monoid b => [a] -> (a -> m b) -> m b
>>> sequence_ :: Monoid b => [m b] -> m b
>>> forever :: Monoid b => m b -> m b
>>
>> This would still propagate monadic result type () if the final monadic
>> action has result type ().
>>
>> http://www.haskell.org/pipermail/haskell-cafe/2009-January/054243.html
>
> But that would not be backwards compatible.

Not completely. You may need additional type annotations.


>> Since the addition of 'void' the noise has become acceptable for me.
>>
>> I would follow a kind of "separation of concerns". Ignoring results is
>> one step and performing forM_, when etc. is the second step.
>
> Ok.  But I am really surprised that an operation should not get the maximally
> sensible type.

There are other functions, like 'asTypeOf' that have intentionally not the
most general type. :-)

My concern is safety and safety means to intentionally forbid things.
That's why I prefer Haskell to C or MatLab. E.g. C by default ignores
results from functions if they are not used. But this way, people may
write (a==b;) instead of (a=b;) without noticing the mistake.


> The application is that I have a function that provides a resource that may
> be present or not.  If it is not present, an exception is thrown (that is the
> monadic effect).  If I am just interested in checking the presence of the
> resource, I can call this function in a do-block.  But I am not allowed to
> call it in a 'when', without 'void'ing it.  That is counterintuitive.

How do you free the resource if you ignore the returned handle?

If checking for an exception this way is a common pattern in your code,
how about writing a function that catches the exception and run some code,
if there is no exception? It might have a signature like

whenAvailable :: IO resource -> IO () -> IO ()

and might be used like this

whenAvailable getResource $ do
    thingsToDoWhenResourceIsAvailable

> To reconcile the 'strict' vs. 'liberal' programmers, it seems that library
> functions need to have different types depending on whether
>
>  -fwarn-unused-do-bind
>
> is set or not...

Something like a type system that does not only support 'correct' and
'wrong', but also 'suspect', would solve the problem. :-)


_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Henning Thielemann
In reply to this post by Andres Löh

On Tue, 5 Jun 2012, Andres Löh wrote:

>> Using mapM instead of mapM_ can lead to a memory leak and I think the
>> confusion between mapM and mapM_ was the initial reason to add the
>> unused-binding warning to GHC. And it actually helped me spotting a lot of
>> calls to mapM that have to be calls to mapM_.
>
> The different space behaviour is a very good reason to make the
> distinction for mapM and mapM_. However, it's not a good reason to
> choose arbitrarily restrictive types for perfectly generic functions
> in other cases IMHO. At least I don't see that there's a performance
> reason not to generalize when or unless, as their implementation would
> stay the same.
The type of 'when' is not arbitrarily restrictive. It makes sure, that no
monadic result is ignored by accident.

Btw. the mapM memory leak can be easily and accidentally resurrected by
writing

   when_ b (mapM f xs)

with

   f :: a -> m ()
   when_ :: Bool -> m a -> m ()


_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Andres Löh
> Btw. the mapM memory leak can be easily and accidentally resurrected by
> writing
>
>  when_ b (mapM f xs)
>
> with
>
>  f :: a -> m ()
>  when_ :: Bool -> m a -> m ()

I don't understand the whole mapM analogy (yet). I don't need when_ to
get into trouble with using mapM. Also, whether a space leak with mapM
arises or not typically doesn't depend on whether you use its result
or not. It's the other way round: if you run mapM on a huge list but
aren't interested in the results, then you should use mapM_ instead.
So the problem here is accidentally producing a (large) result rather
than accidentally ignoring it.

Cheers,
  Andres

--
Andres Löh, Haskell Consultant
Well-Typed LLP, http://www.well-typed.com

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Iavor Diatchki
Hello,

-1 for me too, for the reasons already discussed in the thread (i.e., because it makes it easy to accidentally ignore potentially important results).  I think that gains in readability from this generalization are at best questionable.

-Iavor




On Tue, Jun 5, 2012 at 2:07 PM, Andres Löh <[hidden email]> wrote:
> Btw. the mapM memory leak can be easily and accidentally resurrected by
> writing
>
>  when_ b (mapM f xs)
>
> with
>
>  f :: a -> m ()
>  when_ :: Bool -> m a -> m ()

I don't understand the whole mapM analogy (yet). I don't need when_ to
get into trouble with using mapM. Also, whether a space leak with mapM
arises or not typically doesn't depend on whether you use its result
or not. It's the other way round: if you run mapM on a huge list but
aren't interested in the results, then you should use mapM_ instead.
So the problem here is accidentally producing a (large) result rather
than accidentally ignoring it.

Cheers,
 Andres

--
Andres Löh, Haskell Consultant
Well-Typed LLP, http://www.well-typed.com

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries


_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Henning Thielemann
In reply to this post by Andres Löh

On Tue, 5 Jun 2012, Andres Löh wrote:

>> Btw. the mapM memory leak can be easily and accidentally resurrected by
>> writing
>>
>>  when_ b (mapM f xs)
>>
>> with
>>
>>  f :: a -> m ()
>>  when_ :: Bool -> m a -> m ()
>
> I don't understand the whole mapM analogy (yet). I don't need when_ to
> get into trouble with using mapM.
If I write

   when b (mapM f xs)

with

   f :: a -> m ()
   when :: Bool -> m () -> m ()

then GHC will say something like: Cannot match [()] (result of 'mapM')
with () (argument of 'when').


_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: [Haskell] Types of when and unless in Control.Monad

Andreas Abel
On 06.06.12 12:13 AM, Henning Thielemann wrote:

>
> On Tue, 5 Jun 2012, Andres Löh wrote:
>
>>> Btw. the mapM memory leak can be easily and accidentally resurrected by
>>> writing
>>>
>>>  when_ b (mapM f xs)
>>>
>>> with
>>>
>>>  f :: a -> m ()
>>>  when_ :: Bool -> m a -> m ()
>>
>> I don't understand the whole mapM analogy (yet). I don't need when_ to
>> get into trouble with using mapM.
>
> If I write
>
> when b (mapM f xs)
>
> with
>
>   f :: a -> m ()
>   when :: Bool -> m () -> m ()
>
> then GHC will say something like: Cannot match [()] (result of 'mapM')
> with () (argument of 'when').

Well, but nothing in the type system prevents you from writing

   mapM f xs >> cont

or

   do mapM f xs
      cont

so you are not warned of a space leak in the common situations anyway,
but you are responsible yourself.

I don't know why 'when' should be any different than '>>'.

Cheers,
Andreas


--
Andreas Abel  <><      Du bist der geliebte Mensch.

Theoretical Computer Science, University of Munich
Oettingenstr. 67, D-80538 Munich, GERMANY

[hidden email]
http://www2.tcs.ifi.lmu.de/~abel/

_______________________________________________
Libraries mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/libraries
12