Is join useful for every monad?

Edward Kmett ekmett at gmail.com
Mon Oct 14 21:07:00 UTC 2019


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 <
carter.schonwald at gmail.com> 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 <godzbanebane at gmail.com>
> 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
>> <http://hackage.haskell.org/package/selective-0.3>
>>
>> =======
>>
>> Georgi
>> _______________________________________________
>> Libraries mailing list
>> Libraries at haskell.org
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>>
> _______________________________________________
> Libraries mailing list
> Libraries at haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/libraries/attachments/20191014/19dafa70/attachment.html>


More information about the Libraries mailing list