When compiling the following code:
enum Tree[+A]:
case Leaf(value: A)
case Branch(left: Tree[A], right: Tree[A])
extension (t: Tree[Int]) def firstPositive: Int = t match
case Leaf(i) => i
case Branch(l, r) =>
val lpos = l.firstPositive
if lpos > 0 then lpos else r.firstPositive
I get these errors:
-- [E008] Not Found Error: -----------------------------------------------------
137 | if lpos > 0 then lpos else r.firstPositive
| ^^^^^^
|value > is not a member of Tree[Int] => Int, but could be made available as an extension method.
|
|One of the following imports might make progress towards fixing the problem:
|
| import math.Ordered.orderingToOrdered
| import math.Ordering.Implicits.infixOrderingOps
|
-- [E007] Type Mismatch Error: -------------------------------------------------
137 | if lpos > 0 then lpos else r.firstPositive
| ^^^^
| Found: (lpos : Tree[Int] => Int)
| Required: Int
|
| longer explanation available when compiling with `-explain`
-- [E007] Type Mismatch Error: -------------------------------------------------
137 | if lpos > 0 then lpos else r.firstPositive
| ^^^^^^^^^^^^^^^
| Found: Tree[Int] => Int
| Required: Int
|
| longer explanation available when compiling with `-explain`
3 errors found
It looks like l.firstPositive
has type Tree[Int] => Int
and not Int
. One workaround is to write l.firstPositive(l)
, but then there's no point in using extensions. What am I doing wrong?
Btw, this code snippet is copied from Functional Programming in Scala Second Edition (page 52), which is a very recent book.
That doesn't seem to be a bug, one of the things that I think might be confusing in Scala3 is the indentation rules, that might be misleading in some cases. I haven't read the book yet, but I have a feeling that either you haven't followed the indentations correctly, or it's some mistake in the book. In your case, you're defining an extension method over a specific implementation of Tree
, namely Tree[Int]
, which should be outside the scope of the Tree
enum itself. This works perfectly fine:
enum Tree[+A]:
case Leaf(value: A)
case Branch(left: Tree[A], right: Tree[A])
import Tree.*
// the same indentation as Tree definition, meaning it's outside the scope
extension (t: Tree[Int]) def firstPositive: Int =
t match
case Leaf(i) => i
case Branch(l, r) =>
val lpos = l.firstPositive
if lpos > 0 then lpos else r.firstPositive
One thing to note here is that when you define the extension method inside the Tree
, by the definition of a method, it's first argument (the actual instance) is passed to it implicitly:
enum Tree[+T]:
// sum types here
extension (t: Tree[Int]) def whatever: Int = ???
Would be expanded to something like this (not precisely) when you put the extension inside the scope of Tree
:
def whetever(this: Tree[_])(t: Tree[Int]): Int = ???
That might be the reason why lpos
is being detected as a function from Tree[Int]
to Int
. In fact you can write something like this:
enum Tree[+A]:
case Leaf(value: A)
case Branch(left: Tree[A], right: Tree[A])
extension (t: Tree[Int]) def firstPositive: Int =
t match
case Leaf(i) => i
case Branch(l, r) =>
// first l is "this", latter one is "t"
val lpos = l.firstPositive(l)
if lpos > 0 then lpos else r.firstPositive(r)
If my assumptions are correct, I can't see why defining such thing, which is both a method and an extension method at the same time is not a specific compile-time error.