How to make boolean logic with IO Monad more expressive?

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

How to make boolean logic with IO Monad more expressive?

Magicloud Magiclouds
Hi,

I think `a || b && c` is more clear than
```
if a
  then True
  else if b
    then if c
      then True
      else False
    else False
```

And some languages, `pureComputing || (ioOperation && pure2)` involves
IO only when pureComputing is False.

So in Haskell, is it possible to get both benefits?
```
status <- ioOperation -- IO-ed anyway
return $ pureComputing || (status && pure2)
```
```
if pureComputing
  then return True
  else do
    status <- ioOperation
    if status
      then return pure2
      else return False
-- Looks ugly
```
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: How to make boolean logic with IO Monad more expressive?

Emil Axelsson-3
Not with the standard `||` and `&&` operations. You'd have to define new
versions with a `Monad` constraint.

The `monad-loops` package is pretty nice for short-circuiting stuff.
See, for example, `andM` and `orM`. But it doesn't provide any binary
operators.

https://hackage.haskell.org/package/monad-loops

/ Emil

Den 2019-05-27 kl. 07:07, skrev Magicloud Magiclouds:

> Hi,
>
> I think `a || b && c` is more clear than
> ```
> if a
>    then True
>    else if b
>      then if c
>        then True
>        else False
>      else False
> ```
>
> And some languages, `pureComputing || (ioOperation && pure2)` involves
> IO only when pureComputing is False.
>
> So in Haskell, is it possible to get both benefits?
> ```
> status <- ioOperation -- IO-ed anyway
> return $ pureComputing || (status && pure2)
> ```
> ```
> if pureComputing
>    then return True
>    else do
>      status <- ioOperation
>      if status
>        then return pure2
>        else return False
> -- Looks ugly
> ```
> _______________________________________________
> Haskell-Cafe mailing list
> To (un)subscribe, modify options or view archives go to:
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> Only members subscribed via the mailman list are allowed to post.
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: How to make boolean logic with IO Monad more expressive?

Magicloud Magiclouds
Good call. I see `orM` is actually a wrapper of the "ugly" nested-if.
So operators can be another wrapper away. Thanks.

On Mon, May 27, 2019 at 3:35 PM Emil Axelsson <[hidden email]> wrote:

>
> Not with the standard `||` and `&&` operations. You'd have to define new
> versions with a `Monad` constraint.
>
> The `monad-loops` package is pretty nice for short-circuiting stuff.
> See, for example, `andM` and `orM`. But it doesn't provide any binary
> operators.
>
> https://hackage.haskell.org/package/monad-loops
>
> / Emil
>
> Den 2019-05-27 kl. 07:07, skrev Magicloud Magiclouds:
> > Hi,
> >
> > I think `a || b && c` is more clear than
> > ```
> > if a
> >    then True
> >    else if b
> >      then if c
> >        then True
> >        else False
> >      else False
> > ```
> >
> > And some languages, `pureComputing || (ioOperation && pure2)` involves
> > IO only when pureComputing is False.
> >
> > So in Haskell, is it possible to get both benefits?
> > ```
> > status <- ioOperation -- IO-ed anyway
> > return $ pureComputing || (status && pure2)
> > ```
> > ```
> > if pureComputing
> >    then return True
> >    else do
> >      status <- ioOperation
> >      if status
> >        then return pure2
> >        else return False
> > -- Looks ugly
> > ```
> > _______________________________________________
> > Haskell-Cafe mailing list
> > To (un)subscribe, modify options or view archives go to:
> > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> > Only members subscribed via the mailman list are allowed to post.
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: How to make boolean logic with IO Monad more expressive?

Viktor Dukhovni
In reply to this post by Magicloud Magiclouds


> On May 27, 2019, at 1:07 AM, Magicloud Magiclouds <[hidden email]> wrote:
>
> ```
> status <- ioOperation -- IO-ed anyway
> return $ pureComputing || (status && pure2)
> ```

With two helpers:

  (<&&>) :: IO Bool -> IO Bool -> IO Bool
  (<&&>) ma mb = ma >>= (\a -> if not a then return False else mb)

  (<||>) :: IO Bool -> IO Bool -> IO Bool
  (<||>) ma mb = ma >>= (\a -> if a then return True else mb)

you'd write:

  pure preComputing <||> (ioOperation <&&> pure pure2)

You could even define suitable fixity to make <&&> have higher
precedence than <||> and not need any parentheses.

--
        Viktor.

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: How to make boolean logic with IO Monad more expressive?

Magicloud Magiclouds
Cool. Thanks.

On Mon, May 27, 2019 at 5:10 PM Viktor Dukhovni <[hidden email]> wrote:

>
>
>
> > On May 27, 2019, at 1:07 AM, Magicloud Magiclouds <[hidden email]> wrote:
> >
> > ```
> > status <- ioOperation -- IO-ed anyway
> > return $ pureComputing || (status && pure2)
> > ```
>
> With two helpers:
>
>   (<&&>) :: IO Bool -> IO Bool -> IO Bool
>   (<&&>) ma mb = ma >>= (\a -> if not a then return False else mb)
>
>   (<||>) :: IO Bool -> IO Bool -> IO Bool
>   (<||>) ma mb = ma >>= (\a -> if a then return True else mb)
>
> you'd write:
>
>   pure preComputing <||> (ioOperation <&&> pure pure2)
>
> You could even define suitable fixity to make <&&> have higher
> precedence than <||> and not need any parentheses.
>
> --
>         Viktor.
>
> _______________________________________________
> Haskell-Cafe mailing list
> To (un)subscribe, modify options or view archives go to:
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> Only members subscribed via the mailman list are allowed to post.
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: How to make boolean logic with IO Monad more expressive?

Jack Kelly
In reply to this post by Viktor Dukhovni
Viktor Dukhovni <[hidden email]> writes:

>> On May 27, 2019, at 1:07 AM, Magicloud Magiclouds <[hidden email]> wrote:
>>
>> ```
>> status <- ioOperation -- IO-ed anyway
>> return $ pureComputing || (status && pure2)
>> ```
>
> With two helpers:
>
>   (<&&>) :: IO Bool -> IO Bool -> IO Bool
>   (<&&>) ma mb = ma >>= (\a -> if not a then return False else mb)
>
>   (<||>) :: IO Bool -> IO Bool -> IO Bool
>   (<||>) ma mb = ma >>= (\a -> if a then return True else mb)

These generalise to any Applicative, I think:

import Control.Applicative (liftA2)

(<&&>) = liftA2 (&&)
(<||>) = liftA2 (||)

-- Jack
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: How to make boolean logic with IO Monad more expressive?

Magicloud Magiclouds
With side effect, they are not the same.

Viktor's nested-if does not trigger unnecessary IO action, while liftA2 does.

On Mon, May 27, 2019 at 5:38 PM Jack Kelly <[hidden email]> wrote:

>
> Viktor Dukhovni <[hidden email]> writes:
>
> >> On May 27, 2019, at 1:07 AM, Magicloud Magiclouds <[hidden email]> wrote:
> >>
> >> ```
> >> status <- ioOperation -- IO-ed anyway
> >> return $ pureComputing || (status && pure2)
> >> ```
> >
> > With two helpers:
> >
> >   (<&&>) :: IO Bool -> IO Bool -> IO Bool
> >   (<&&>) ma mb = ma >>= (\a -> if not a then return False else mb)
> >
> >   (<||>) :: IO Bool -> IO Bool -> IO Bool
> >   (<||>) ma mb = ma >>= (\a -> if a then return True else mb)
>
> These generalise to any Applicative, I think:
>
> import Control.Applicative (liftA2)
>
> (<&&>) = liftA2 (&&)
> (<||>) = liftA2 (||)
>
> -- Jack
> _______________________________________________
> Haskell-Cafe mailing list
> To (un)subscribe, modify options or view archives go to:
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> Only members subscribed via the mailman list are allowed to post.
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: How to make boolean logic with IO Monad more expressive?

Viktor Dukhovni
In reply to this post by Jack Kelly
On Mon, May 27, 2019 at 07:37:37PM +1000, Jack Kelly wrote:

> Viktor Dukhovni <[hidden email]> writes:
>
> >> On May 27, 2019, at 1:07 AM, Magicloud Magiclouds <[hidden email]> wrote:
> >>
> >> ```
> >> status <- ioOperation -- IO-ed anyway
> >> return $ pureComputing || (status && pure2)
> >> ```
> >
> > With two helpers:
> >
> >   (<&&>) :: IO Bool -> IO Bool -> IO Bool
> >   (<&&>) ma mb = ma >>= (\a -> if not a then return False else mb)
> >
> >   (<||>) :: IO Bool -> IO Bool -> IO Bool
> >   (<||>) ma mb = ma >>= (\a -> if a then return True else mb)
>
> These generalise to any Applicative, I think:
>
> import Control.Applicative (liftA2)
>
> (<&&>) = liftA2 (&&)
> (<||>) = liftA2 (||)

As written, the above Applicative version won't short-circuit (is
strict in both arguments) in the IO Monad.  For example, the below
will still read stdin:

    pure False <&&> (getLine >>= return . read)

The generalized Monadic version will short-circuit.

      (<&&>) :: Monad m => m Bool -> m Bool -> m Bool
      (<&&>) ma mb = ma >>= (\a -> if not a then return False else mb)

      (<||>) :: Monad m => m Bool -> m Bool -> m Bool
      (<||>) ma mb = ma >>= (\a -> if a then return True else mb)

--
        Viktor.
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: How to make boolean logic with IO Monad more expressive?

Jack Kelly
In reply to this post by Magicloud Magiclouds
Magicloud Magiclouds <[hidden email]> writes:

> With side effect, they are not the same.
> Viktor's nested-if does not trigger unnecessary IO action, while liftA2 does.

Ah, of course. Still, there's no need to restrict their type to IO; any
Monad should be fine.

-- Jack

> On Mon, May 27, 2019 at 5:38 PM Jack Kelly <[hidden email]> wrote:
>>
>> Viktor Dukhovni <[hidden email]> writes:
>>
>> >> On May 27, 2019, at 1:07 AM, Magicloud Magiclouds <[hidden email]> wrote:
>> >>
>> >> ```
>> >> status <- ioOperation -- IO-ed anyway
>> >> return $ pureComputing || (status && pure2)
>> >> ```
>> >
>> > With two helpers:
>> >
>> >   (<&&>) :: IO Bool -> IO Bool -> IO Bool
>> >   (<&&>) ma mb = ma >>= (\a -> if not a then return False else mb)
>> >
>> >   (<||>) :: IO Bool -> IO Bool -> IO Bool
>> >   (<||>) ma mb = ma >>= (\a -> if a then return True else mb)
>>
>> These generalise to any Applicative, I think:
>>
>> import Control.Applicative (liftA2)
>>
>> (<&&>) = liftA2 (&&)
>> (<||>) = liftA2 (||)
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: How to make boolean logic with IO Monad more expressive?

Viktor Dukhovni
In reply to this post by Viktor Dukhovni
On Mon, May 27, 2019 at 05:46:11AM -0400, Viktor Dukhovni wrote:

> The generalized Monadic version will short-circuit.
>
>       (<&&>) :: Monad m => m Bool -> m Bool -> m Bool
>       (<&&>) ma mb = ma >>= (\a -> if not a then return False else mb)
>
>       (<||>) :: Monad m => m Bool -> m Bool -> m Bool
>       (<||>) ma mb = ma >>= (\a -> if a then return True else mb)

I should mention that a compatible even better generalized interface
is available via Control.Selective:

(<||>): http://hackage.haskell.org/package/selective-0.2/docs/Control-Selective.html#v:-60--124--124--62-
(<&&>): http://hackage.haskell.org/package/selective-0.2/docs/Control-Selective.html#v:-60--38--38--62-

--
        Viktor.
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.