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

Strange behavior of && operator (maybe also exists with other logic operation) #37

Open
jilen opened this issue Nov 2, 2015 · 9 comments

Comments

@jilen
Copy link
Contributor

jilen commented Nov 2, 2015

Code

monadic[Option] { val i = Some(1).each < 0; i && {println("foo"); Some(2).each > 0} }

Output

foo
res5: Option[Boolean] = Some(false)
@Atry
Copy link
Collaborator

Atry commented Nov 2, 2015

Given a method that accepts a call-by-name parameter, it means the method decides when and how many times the parameter evaluates.

Boolean.&& is one of that kind of methods. Technically, there is no way to pass a monadic expression to methods like that. You could not pass an Option[Boolean] to a => Boolean typed method because type mismatch.

Instead, the only thing monadic macro can do is evaluating the monadic expression before passing it to a method. The following code is generated by monadic for you example code.

{
  val monad$macro$1: scalaz.Traverse[Option] with scalaz.MonadPlus[Option] with scalaz.Each[Option] with scalaz.Index[Option] with scalaz.Length[Option] with scalaz.Cozip[Option] with scalaz.Zip[Option] with scalaz.Unzip[Option] with scalaz.Align[Option] with scalaz.IsEmpty[Option] with scalaz.Cobind[Option] with scalaz.Optional[Option]{def point[A](a: => A): Some[A]; def cojoin[A](a: Option[A]): Option[Some[A]]} = scalaz.std.option.optionInstance;
  monad$macro$1.bind(com.thoughtworks.each.Monadic.EachOps[Some, Int](scala.Some.apply[Int](1)).underlying)(((element$macro$2: Int) => {
    val i: Boolean = element$macro$2.<(0);
    monad$macro$1.map({
      scala.this.Predef.println("foo");
      monad$macro$1.map(com.thoughtworks.each.Monadic.EachOps[Some, Int](scala.Some.apply[Int](2)).underlying)(((element$macro$3: Int) => element$macro$3.>(0)))
    })(((element$macro$4: Boolean) => i.&&(element$macro$4)))
  }))
}

In brief, there is no way to pass a monadic expression to a method that accept call-by-name parameters and still keep the same behavior as the behavior when passing a direct expression. No way, even if you manually create the monadic expression without using Each library.

@Atry
Copy link
Collaborator

Atry commented Nov 2, 2015

In order to achieve the correspond behavior with a monadic expression, you must replace call-by-name parameter => T to a explicit function () => M[T], for example

// Monadic version of Boolean.&&
def monadicBooleanAnd[M[_] : Monad](leftHandSide: Boolean, rightHandSide: () => M[Boolean]): M[Boolean] = {
  if (leftHandSide) {
    rightHandSide()
  } else {
    Monad[M].point(false)
  }
}

monadic[Option] { val i = Some(1).each < 0; monadicBooleanAnd(i, { () => monadic[Option] { println("foo"); Some(2).each > 0 } }).each }

@jilen
Copy link
Contributor Author

jilen commented Dec 10, 2015

We should point this out in the README file.
For a trade system like this will lead to terrible behavior

val decreased = account.decrease(xxx).each
val recordAdded = decreased && transfer.addRecord(xxx).each

@Atry
Copy link
Collaborator

Atry commented Dec 10, 2015

Could you create a Pull Request to the README.md?

@Atry
Copy link
Collaborator

Atry commented Jan 8, 2016

How do you think if we translate Boolean.&& to a if expressions to enable behavior like a call-by-name parameter?

@jilen
Copy link
Contributor Author

jilen commented Jan 8, 2016

That seems reasonable. And it will be better if we can raise an warning for other call-by-name parameter.

@Atry
Copy link
Collaborator

Atry commented Mar 20, 2017

I think it's better to disable .each magic in a call-by-value parameters.

The following code should not compile any more

monadic[Option] { val i = Some(1).each < 0; i && {println("foo"); Some(2).each > 0} }

@jilen
Copy link
Contributor Author

jilen commented Mar 20, 2017

@Atry Can't agree anymore

@MarisaKirisame
Copy link

👍

Atry pushed a commit to Atry/each that referenced this issue Feb 3, 2023
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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

No branches or pull requests

3 participants