Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Short circuit ops for booleans (over QQ) #4191

Closed
wants to merge 3 commits into from

Conversation

anton-k
Copy link
Contributor

@anton-k anton-k commented Nov 5, 2021

Fixes #4114

The problem is that plutus booleans don't use short circuit on evaluation.
There is ugly fix to rewrite all expressions with if-then-else.
But code becomes rather clumsy. We can fix that with meta TH.

Proposed fix defines two qq-functions and_if and or_if:

[and_if| expr1, expr2, expr3]

for arbitrary number of arguments is expanded to

if expr1
  then if expr2
    then if expr3
      then True
      else False
    else False
  else False 

and

[or_if| expr1, expr2, expr3]

is expanded to

if expr1
  then True
  else if expr2
    then True
    else if expr3
      then True
      else False

@michaelpj
Copy link
Contributor

I think I'd rather have the simpler typed-TH version, no need for quasiquotation parser failure or anything like that.

andLazy :: [ TExpQ Bool ] -> TExpQ Bool
andLazy [] = [|| True ||]
andLazy (e:es) = [|| if $$e then $$(andTH es) else False ||]

orLazy :: [ TExpQ Bool ] -> TExpQ Bool
orLazy [] = [|| False ||]
orLazy (e:es) = [|| if $$e then True else $$(orTH es) ||]

example :: Bool
example = $$(andTH [ [|| True ||] [|| False ||])

Location looks fine. Would be nice to have a test witnessing the lazy behaviour.

@anton-k
Copy link
Contributor Author

anton-k commented Nov 5, 2021

@michaelpj Ok, I've added tests, for simple cases and to prove the lazy evaluation.
To me it's more to type and clutter every case with [| ... |] [| ... |] for simple TH solution,
than to write once [op|.. |] for QQ one.

@anton-k
Copy link
Contributor Author

anton-k commented Nov 5, 2021

There is a bit of problem with multiline versions.
If there is type-checker error in one of the parts of the expressions like in:

[and_if|
  True,
  False,
  () == False
|]

It will report line of the error as beginning of the and_if.

@effectfully
Copy link
Contributor

effectfully commented Feb 15, 2023

@michaelpj can we simply define

(&&) :: Bool -> Bool -> Bool
(&&) = \x y -> if x then y else False
{-# INLINE (&&) #-}

This is pretty much guaranteed to get inlined and we'll get the right short-circuiting semantics as long as GHC doesn't attempt to optimize the resulting if expression while reducing the lambdas statically. Or am I missing something?

Although maybe

(&&) :: Bool -> Bool -> Bool
x && y = if x then y else False
{-# INLINE (&&) #-}

would be a bit more reliable?

@michaelpj
Copy link
Contributor

We could do that. My main concern was that then we have surprising behaviour in a different way: we have a function call where the arguments are not evaluated strictly! That's also surprising. But maybe it's less surprising, I don't really know.

(Both your variant should be the same in PIR)

@bezirg
Copy link
Contributor

bezirg commented Feb 16, 2023

We could do that. My main concern was that then we have surprising behaviour in a different way: we have a function call where the arguments are not evaluated strictly! That's also surprising. But maybe it's less surprising, I don't really know.

(Both your variant should be the same in PIR)

That sounds kind of bad, like following a lesser of two evils approach

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

overzZzealous: Non-lazy Semantics of || and && in Plutus Core
5 participants