Skip to content

bugfix #2732

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

Merged
merged 2 commits into from
Mar 14, 2023
Merged

bugfix #2732

merged 2 commits into from
Mar 14, 2023

Conversation

rebekah
Copy link
Contributor

@rebekah rebekah commented Mar 10, 2023

The examples didn't make sense to me... so I played with them a bit realized they were broken and talked with @BalmungSan on discord. He suggested this resolution, which seems concise and appropriate. Here is the code in working(and tested) examples: https://github.com/rebekah/scala-3-play/blob/main/src/main/scala/ExploreContextBounds.scala

@julienrf
Copy link
Contributor

I think it would be better to provide the definition of max. I believe the current code does work if max is defined as follows:

def max[A](using Ord[A]): (A, A) => A =
  (a1, a2) => if a1 > a2 then a1 else a2

@BalmungSan
Copy link
Contributor

@julienrf yeah totally, when I looked at the page I was really lost since I didn't find max and the code didn't make sense to me at all.
I assume it was defined before but I didn't want to check multiple pages to find out.

@rebekah
Copy link
Contributor Author

rebekah commented Mar 13, 2023

def max[A](using Ord[A]): (A, A) => A =
  (a1, a2) => if a1 > a2 then a1 else a2

@julienrf This max method doesn't appear to use the Ord instance in any way. Am I missing something? Furthermore, when I try to use this code I get the error: value > is not a member of A, but could be made available as an extension method

@rebekah
Copy link
Contributor Author

rebekah commented Mar 13, 2023

def max[A](using Ord[A]): (A, A) => A =
  (a1, a2) => if a1 > a2 then a1 else a2

@julienrf This max method doesn't appear to use the Ord instance in any way. Am I missing something? Furthermore, when I try to use this code I get the error: value > is not a member of A, but could be made available as an extension method

@rebekah rebekah closed this Mar 13, 2023
@rebekah rebekah reopened this Mar 13, 2023
@rebekah
Copy link
Contributor Author

rebekah commented Mar 13, 2023

@julienrf is there a code base with working code for each of these examples?

@BalmungSan
Copy link
Contributor

@rebekah so the funny thing is that both parts of your question are connected :)
Such max method should be indeed using Ord, specifically by the use of > which should be added as an extension method to any value of type A given the presence of the Ord[A]

But, it is not doing that because that code is also missing an import like:

import scala.math.Ordering.Implicits._

So, the lesson of the day is that code examples in docs should be complete and compiled, otherwise they may confuse newcomers reading them; which is exactly the opposite of what a doc should do :)

@rebekah
Copy link
Contributor Author

rebekah commented Mar 13, 2023

@BalmungSan are you saying there would be an implicit class for the ord instance that defined the > method? because if so sure... okay that makes sense. And yes if that is the case it should be clearly communicated... but as it is there are so many levels of assumptions of understanding being made. It would be almost impossible for someone to understand the simple concept of context bounds unless they fully understood type classes and implicit conversion, and could make the assumption that they were being used. I suppose it would be semi-okay if there was a code base to go along with the docs so that if someone wanted to they could refer to the entire context of the method referenced in the docs. But in my opinion, it should just be much simpler, each example should basically stand on it's own.

@julienrf
Copy link
Contributor

I 100% agree with you @rebekah. That page about context bounds explains the syntax that is handled by the compiler, and in some sense we don’t need to know what the definition of max or Ord is to understand the syntactic transformation performed by the compiler, but I agree that it would be way better to provide a self-contained, executable, example. I propose the following, then.

Scala 3:

trait Ord[A]:
  /** @return true if a1 is greater than a2, false otherwise */
  def greaterThan(a1: A, a2: A): Boolean

/** @return the maximum value between a1 and a2 */
def max[A](a1: A, a2: A)(using ord: Ord[A]): A =
  if ord.greaterThan(a1, a2) then a1 else a2

/** @return the maximum element in a collection */
def maxElement[A](as: List[A])(using ord: Ord[A]): A =
  as.reduceLeft(max(_, _)(using ord))

Note that since the ord parameter is a context parameter, we don’t need to pass it explicitly to the max method:

def maxElement[A](as: List[A])(using Ord[A]): A =
  as.reduceLeft(max(_, _))

Note that since we don’t explicitly use that context parameter, we don’t even need to give it name (that sentence does not apply to Scala 2).

In that case, we can shorten the syntax even further by using a context bound:

def maxElement[A: Ord](as: List[A]): A =
  as.reduceLeft(max(_, _))

And, optionally, even further:

def maxElement[A: Ord](as: List[A]): A =
  as.reduceLeft(max)

In this last form, we replace the function literal max(_, _) with the eta-expansion of the method max. (This is not really related to the usage of context bounds).

Scala 2:

trait Ord[A] {
  def greaterThan(a1: A, a2: A): Boolean
}

def max[A](a1: A, a2: A)(implicit ord: Ord): A =
  if (ord.greaterThan(a1, a2)) a1 else a2

def maxElement[A](as: List[A])(implicit ord: Ord[A]): A =
  as.reduceLeft(max(_, _)(ord))

Or:

def maxElement[A: Ord](as: List[A]): A =
  as.reduceLeft(max(_, _))

Or even:

def maxElement[A: Ord](as: List[A]): A =
  as.reduceLeft(max[A])

We should also add a note that in practice there is already a trait Ordering[A] in the standard library, which provides a methods max, and there is already a method max on List.

@rebekah
Copy link
Contributor Author

rebekah commented Mar 14, 2023

@julienrf Wow, yes that would be fantastic!

@julienrf
Copy link
Contributor

Alright, I went ahead and pushed my proposition, here is the result:

Screenshot 2023-03-14 at 17-17-14 Context Bounds

Screenshot 2023-03-14 at 17-17-25 Context Bounds

@rebekah
Copy link
Contributor Author

rebekah commented Mar 14, 2023

thank you

@julienrf
Copy link
Contributor

Thank you for starting this discussion!

@julienrf julienrf merged commit 2c756b1 into scala:main Mar 14, 2023
@rebekah
Copy link
Contributor Author

rebekah commented Mar 14, 2023

@julienrf you are welcome!

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.

3 participants