Is join useful for every monad?

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

Is join useful for every monad?

Dannyu NDos
join is surely useful for Maybe and [].

But IO (IO a)? ReadPrec (ReadPrec a)? ST s (ST s a)? When the heck could I encounter such types?

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

Re: Is join useful for every monad?

Dmitrii Kovanikov
Data types like `IO (IO a)` are useful and their usage is described in the following blog post:


One function from the example uses `join` directly:

runWizard :: IO (IO a) -> IO a
runWizard = join

On Mon, Oct 14, 2019 at 7:55 AM Dannyu NDos <[hidden email]> wrote:
join is surely useful for Maybe and [].

But IO (IO a)? ReadPrec (ReadPrec a)? ST s (ST s a)? When the heck could I encounter such types?
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

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

Re: Is join useful for every monad?

Georgi Lyubenov
In reply to this post by Dannyu NDos
Often you encounter functions that look weird at a first glance but turn out to be *exactly* what you need to implement something intuitively "more useful".
Apart from Dmitrii's example showing some direct usage, I think this is such one case, as it allows you to implement (>>=) ma f = join (fmap f ma)
Other examples (subjectively of course) of such things are (<*>) which allows you to implement liftA{2,3,..} and mfix, which allows you to have recursive bindings in a do block.

=======
Georgi

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

Re: Is join useful for every monad?

Henning Thielemann

On Mon, 14 Oct 2019, Georgi Lyubenov wrote:

> Other examples (subjectively of course) of such things are (<*>) which
> allows you to implement liftA{2,3,..} and mfix, which allows you to have
> recursive bindings in a do block.

Other example: We have

   liftM2 :: (a -> b -> c) -> m a -> m b -> m c

but what can we do, if we need:

   liftM2' :: (a -> b -> m c) -> m a -> m b -> m c

?


We could just do:

   join $ liftM2 f ma mb
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: Is join useful for every monad?

Dannyu NDos
In reply to this post by Dmitrii Kovanikov
Welp. So IO (IO a) is a thing.

Will I ever encounter ReadPrec (ReadPrec a)?

2019년 10월 14일 (월) 14:42, Dmitrii Kovanikov <[hidden email]>님이 작성:
Data types like `IO (IO a)` are useful and their usage is described in the following blog post:


One function from the example uses `join` directly:

runWizard :: IO (IO a) -> IO a
runWizard = join

On Mon, Oct 14, 2019 at 7:55 AM Dannyu NDos <[hidden email]> wrote:
join is surely useful for Maybe and [].

But IO (IO a)? ReadPrec (ReadPrec a)? ST s (ST s a)? When the heck could I encounter such types?
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

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

Re: Is join useful for every monad?

Georgi Lyubenov
In general, if you want to dynamically generate actions depending on the result of an earlier action you will always encounter join/(>>=).
For example (with ReadPrec/Parser): 
I want to first parse a character, and then parse the same character two more times.
numberAndThenThatManyAs = join (fmap (\c -> satisfy (==c) *> satisfy (==c)) char)

Of note:
* The example is contrived for simplicity's sake, but you do really need a Monad (and hence join) to perform stuff like this in general. A more practical example would be parsing command-line options that depend on previous options.
* Obviously it's way more humane to write this with do-syntax. (or (>>=) or something) - do { c <- char; satisfy (==c); satisfy (==c) }
* I'm not actually sure whether you need a Monad in this situation, maybe you could get away with just selectives

=======
Georgi

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

Re: Is join useful for every monad?

Carter Schonwald
Join actually also comes up in compiler engineering!

Most normalized compiler reps: notably anf and cps, have a sort of flatness condition where you can’t have nested subexpressions (aka in many cases in strict languages this is where evaluation order becomes explicit ) and the join operation corresponds to a step in the flattening process for nested expression syntax when you do compiler transformations in this setting. 

This is in fact exactly why it’s pretty brutal to write the monad for an anf or cps syntax , you’re essentially specifying subexpression evaluation order for all pairs of syntax constructors! 

 And while join is not at the moment in the Monad typeclass because of newtype stuff, writing these monad instances is way saner in terms of the join operators rather than in terms of bind. At least in my biased perspective ;)

On Mon, Oct 14, 2019 at 4:37 AM Georgi Lyubenov <[hidden email]> wrote:
In general, if you want to dynamically generate actions depending on the result of an earlier action you will always encounter join/(>>=).
For example (with ReadPrec/Parser): 
I want to first parse a character, and then parse the same character two more times.
numberAndThenThatManyAs = join (fmap (\c -> satisfy (==c) *> satisfy (==c)) char)

Of note:
* The example is contrived for simplicity's sake, but you do really need a Monad (and hence join) to perform stuff like this in general. A more practical example would be parsing command-line options that depend on previous options.
* Obviously it's way more humane to write this with do-syntax. (or (>>=) or something) - do { c <- char; satisfy (==c); satisfy (==c) }
* I'm not actually sure whether you need a Monad in this situation, maybe you could get away with just selectives

=======

Georgi
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

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

Re: Is join useful for every monad?

Edward Kmett-2
I use join a fair bit in IO!

Consider something where you to dig in an IORef, and compute what to do next.

join $ atomicModifyIORef someRef $ \case
  Foo y -> (Bar, doSomethingWith y)
  x -> (x, return ())

I can't run IO actions inside the atomicModifyIORef but I can give one back as the "extra" result from atomicModifyIORef, and do something I precomputed.

With the join there this collapses into one line, and I can often avoid a pair of redundant case statements.

Other patterns are for common patterns that _almost_ look like applicative usage, like

do
  x <- foo 
  y <- bar
  baz x y

which can be expressed via

join $ baz <$> foo <*> bar

without naming all the intermediaries, whether this is good or not depends on how much you like giving transient names to things.

-Edward





On Mon, Oct 14, 2019 at 12:46 PM Carter Schonwald <[hidden email]> wrote:
Join actually also comes up in compiler engineering!

Most normalized compiler reps: notably anf and cps, have a sort of flatness condition where you can’t have nested subexpressions (aka in many cases in strict languages this is where evaluation order becomes explicit ) and the join operation corresponds to a step in the flattening process for nested expression syntax when you do compiler transformations in this setting. 

This is in fact exactly why it’s pretty brutal to write the monad for an anf or cps syntax , you’re essentially specifying subexpression evaluation order for all pairs of syntax constructors! 

 And while join is not at the moment in the Monad typeclass because of newtype stuff, writing these monad instances is way saner in terms of the join operators rather than in terms of bind. At least in my biased perspective ;)

On Mon, Oct 14, 2019 at 4:37 AM Georgi Lyubenov <[hidden email]> wrote:
In general, if you want to dynamically generate actions depending on the result of an earlier action you will always encounter join/(>>=).
For example (with ReadPrec/Parser): 
I want to first parse a character, and then parse the same character two more times.
numberAndThenThatManyAs = join (fmap (\c -> satisfy (==c) *> satisfy (==c)) char)

Of note:
* The example is contrived for simplicity's sake, but you do really need a Monad (and hence join) to perform stuff like this in general. A more practical example would be parsing command-line options that depend on previous options.
* Obviously it's way more humane to write this with do-syntax. (or (>>=) or something) - do { c <- char; satisfy (==c); satisfy (==c) }
* I'm not actually sure whether you need a Monad in this situation, maybe you could get away with just selectives

=======

Georgi
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

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

Re: Is join useful for every monad?

Vanessa McHale-2
In reply to this post by Dannyu NDos

You can use the (-> c) instance in fun ways

both :: (a -> b) -> (a, a) -> (b, b)
both = join (***)

dup :: a -> (a, a)
dup = join (,)

between `join` (char '"')

On 10/13/19 11:55 PM, Dannyu NDos wrote:
join is surely useful for Maybe and [].

But IO (IO a)? ReadPrec (ReadPrec a)? ST s (ST s a)? When the heck could I encounter such types?

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

signature.asc (673 bytes) Download Attachment