Skip to content

Commit f865799

Browse files
committed
Fixed obsolete implicit methods and classes examples.
Signed-off-by: Dean Wampler <[email protected]>
1 parent 6e7f872 commit f865799

File tree

12 files changed

+180
-46
lines changed

12 files changed

+180
-46
lines changed

build.sbt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ lazy val root = project
5050
// "-old-syntax", // Require `(...)` around conditions.
5151
// "-language:Scala2", // Compile Scala 2 code, highlight what needs updating
5252
// "-language:strictEquality", // Require +derives Eql+ for using == or != comparisons
53-
// "-rewrite", // Attempt to fix code automatically. Use with -indent and ...-migration.
5453
// "-scalajs", // Compile in Scala.js mode (requires scalajs-library.jar on the classpath).
55-
// "-source:future", // Choices: future and future-migration. I use this to force future deprecation warnings, etc.
54+
"-source:future-migration", // Choices: future and future-migration. I use this to force future deprecation warnings, etc.
55+
"-rewrite", // Rewrite source, when necessary, for future migration - DeanW: added Sept 2025
5656
"-Xfatal-warnings", // Fail on warnings, not just errors
5757
// "-Xmigration", // Warn about constructs whose behavior may have changed since version.
5858
// "-Ysafe-init", // Warn on field access before initialization

check-scripts.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@ out_ext="out"
66
timestamp=$(date +"%Y-%m-%d_%H-%M-%S")
77
error_log="$out_root/scripts-errors-$timestamp.log"
88
expected_errors_in=(
9-
109
src/script/scala/progscala3/IndentationSyntax.scala
1110
src/script/scala/progscala3/appdesign/Deprecated.scala
11+
src/script/scala/progscala3/basicoop/DollarsPercentagesOpaque.scala
12+
src/script/scala/progscala3/basicoop/GoodBad.scala
1213
src/script/scala/progscala3/basicoop/MatchableOpaque.scala
14+
src/script/scala/progscala3/basicoop/tagging/Tags.scala
15+
src/script/scala/progscala3/basicoop/tagging/Tags2.scala
1316
src/script/scala/progscala3/collections/MultiMap.scala
17+
src/script/scala/progscala3/contexts/ExtensionMethodScoping.scala
1418
src/script/scala/progscala3/contexts/ImplicitEvidence.scala
1519
src/script/scala/progscala3/contexts/ImplicitNotFound.scala
1620
src/script/scala/progscala3/contexts/MatchGivens.scala
@@ -31,6 +35,7 @@ expected_errors_in=(
3135
src/script/scala/progscala3/rounding/InfixMethod.scala
3236
src/script/scala/progscala3/rounding/InfixType.scala
3337
src/script/scala/progscala3/rounding/TypeErasureProblem.scala
38+
src/script/scala/progscala3/typelessdomore/FibonacciTailrec.scala
3439
src/script/scala/progscala3/typelessdomore/Human.scala
3540
src/script/scala/progscala3/typelessdomore/MethodBroadInference.scala
3641
src/script/scala/progscala3/typelessdomore/MethodNestedReturn.scala

src/main/scala/progscala3/basicoop/tagging/Tags2.scala

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ object Tagging2:
2121
@targetName("Tag") type @@[S, T] = Tagged[S, T]
2222

2323
/**
24+
* Pre Scala 3.7.X:
2425
* Instead of extension methods, use implicit conversions to value classes,
2526
* which won't add any runtime overhead, but allow us to use methods like `tag`
2627
* the way we want. This implementation is closer to the original example in
2728
* SIP-35.
28-
*/
2929
implicit class tagOps[S](s: S):
3030
def tag[T]: S @@ T = Tagged.tag(s)
3131
implicit class untagOps[S, T](st: S @@ T):
@@ -34,6 +34,23 @@ object Tagging2:
3434
def tags[T]: F[S @@ T] = Tagged.tags(fs)
3535
implicit class untagsOps[F[_], S, T](fst: F[S @@ T]):
3636
def untags: F[S] = Tagged.untags(fst)
37+
*/
38+
39+
/**
40+
* DeanW (September 2025): as of Scala 3.7.X, implicit classes are no longer
41+
* supported. Alternative are extension methods and declaring regular classes
42+
* and using given conversions to them. Here, we go back to using extension methods!
43+
*/
44+
45+
extension [S](s: S)
46+
def tag[T]: S @@ T = Tagged.tag(s)
47+
extension [S, T](st: S @@ T)
48+
def untag: S = Tagged.untag(st)
49+
extension [F[_], S](fs: F[S])
50+
def tags[T]: F[S @@ T] = Tagged.tags(fs)
51+
extension [F[_], S, T](fst: F[S @@ T])
52+
def untags: F[S] = Tagged.untags(fst)
53+
3754
end Tagging2
3855

3956
@main def TryTagging2(): Unit =
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// tag::definitions[]
2+
// src/main/scala/progscala3/contexts/UsingClauses.scala
3+
// This is identical to src/script/scala/progscala3/contexts/UsingClauses.scala,
4+
// with the method invocations moved to a new @main function at the end.
5+
// I created it just to make it easier to test some work, and decided it
6+
// "doesn't hurt" to keep it around, but only the script version is used in the
7+
// book.
8+
9+
case class SortableSeq[A](seq: Seq[A]):
10+
def sortBy1a[B](transform: A => B)(using o: Ordering[B]): SortableSeq[A] =
11+
SortableSeq(seq.sortBy(transform)(using o))
12+
13+
def sortBy1b[B](transform: A => B)(using Ordering[B]): SortableSeq[A] =
14+
SortableSeq(seq.sortBy(transform)(using summon[Ordering[B]]))
15+
16+
def sortBy2[B : Ordering](transform: A => B): SortableSeq[A] =
17+
SortableSeq(seq.sortBy(transform)(using summon[Ordering[B]]))
18+
// end::definitions[]
19+
20+
// tag::defaultOrdering[]
21+
def defaultOrdering() =
22+
val seq = SortableSeq(Seq(1,3,5,2,4))
23+
val expected = SortableSeq(Seq(5, 4, 3, 2, 1))
24+
assert(seq.sortBy1a(i => -i) == expected)
25+
assert(seq.sortBy1b(i => -i) == expected)
26+
assert(seq.sortBy2(i => -i) == expected)
27+
28+
// end::defaultOrdering[]
29+
30+
// tag::oddEvenImplicitOrdering[]
31+
def oddEvenImplicitOrdering() =
32+
implicit val oddEven: Ordering[Int] = new Ordering[Int]:
33+
def compare(i: Int, j: Int): Int = i%2 compare j%2 match
34+
case 0 => i compare j
35+
case c => c
36+
37+
val seq = SortableSeq(Seq(1,3,5,2,4))
38+
val expected = SortableSeq(Seq(5, 3, 1, 4, 2))
39+
assert(seq.sortBy1a(i => -i) == expected)
40+
assert(seq.sortBy1b(i => -i) == expected)
41+
assert(seq.sortBy2(i => -i) == expected)
42+
43+
assert(seq.sortBy1a(i => -i)(using oddEven) == expected)
44+
assert(seq.sortBy1b(i => -i)(using oddEven) == expected)
45+
assert(seq.sortBy2(i => -i)(using oddEven) == expected)
46+
47+
// end::oddEvenImplicitOrdering[]
48+
49+
// tag::oddEvenGivenOrdering[]
50+
def evenOddGivenOrdering() =
51+
given evenOdd: Ordering[Int]:
52+
def compare(i: Int, j: Int): Int = i%2 compare j%2 match
53+
case 0 => i compare j
54+
case c => -c
55+
56+
val seq = SortableSeq(Seq(1,3,5,2,4))
57+
val expected = SortableSeq(Seq(4, 2, 5, 3, 1))
58+
assert(seq.sortBy1a(i => -i) == expected) // <1>
59+
assert(seq.sortBy1b(i => -i) == expected)
60+
assert(seq.sortBy2(i => -i) == expected)
61+
62+
assert(seq.sortBy1a(i => -i)(using evenOdd) == expected) // <2>
63+
assert(seq.sortBy1b(i => -i)(using evenOdd) == expected)
64+
assert(seq.sortBy2(i => -i)(using evenOdd) == expected)
65+
66+
// end::oddEvenGivenOrdering[]
67+
68+
@main def checkUsingClauses() =
69+
defaultOrdering()
70+
oddEvenImplicitOrdering()
71+
evenOddGivenOrdering()

src/main/scala/progscala3/contexts/typeclass/old/ToJSONTypeClasses.scala

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,47 @@ package progscala3.contexts.typeclass.old
44

55
import progscala3.introscala.shapes.{Point, Shape, Circle, Rectangle, Triangle}
66

7-
trait ToJSONOld[T]:
8-
def toJSON(name: String = "", level: Int = 0): String // <1>
7+
// DeanW: September 14, 2025. Scala is dropping support for implicit classes, so the
8+
// `implicit final class PointToJSON`, etc. are now replaced by extension methods
9+
// and the trait ToJSONOld is converted to an object for its methods.
910

10-
protected val indent = " "
11-
protected def indentation(level: Int): (String,String) =
11+
object ToJSONOld:
12+
val indent = " "
13+
def indentation(level: Int): (String,String) =
1214
(indent * level, indent * (level+1))
13-
protected def handleName(name: String): String =
15+
def handleName(name: String): String =
1416
if name.length > 0 then s""""$name": """ else ""
15-
// end::trait[]
17+
end ToJSONOld
1618

17-
// tag::pointcircle[]
18-
implicit final class PointToJSON(
19-
point: Point) extends ToJSONOld[Point]:
20-
def toJSON(name: String = "", level: Int = 0): String =
21-
val (outdent, indent) = indentation(level)
22-
s"""${handleName(name)}{
19+
extension(point: Point)
20+
def toJSON(name: String, level: Int): String =
21+
val (outdent, indent) = ToJSONOld.indentation(level)
22+
s"""${ToJSONOld.handleName(name)}{
2323
|${indent}"x": "${point.x}",
2424
|${indent}"y": "${point.y}"
2525
|$outdent}""".stripMargin
2626

27-
implicit final class CircleToJSON(
28-
circle: Circle) extends ToJSONOld[Circle]:
29-
def toJSON(name: String = "", level: Int = 0): String =
30-
val (outdent, indent) = indentation(level)
31-
s"""${handleName(name)}{
27+
extension(circle: Circle)
28+
def toJSON(name: String, level: Int): String =
29+
val (outdent, indent) = ToJSONOld.indentation(level)
30+
s"""${ToJSONOld.handleName(name)}{
3231
|${indent}${circle.center.toJSON("center", level + 1)},
3332
|${indent}"radius": ${circle.radius}
3433
|$outdent}""".stripMargin
35-
// end::pointcircle[]
3634

37-
implicit final class RectangleToJSON(
38-
rect: Rectangle) extends ToJSONOld[Rectangle]:
39-
def toJSON(name: String = "", level: Int = 0): String =
40-
val (outdent, indent) = indentation(level)
41-
s"""${handleName(name)}{
35+
extension(rect: Rectangle)
36+
def toJSON(name: String, level: Int): String =
37+
val (outdent, indent) = ToJSONOld.indentation(level)
38+
s"""${ToJSONOld.handleName(name)}{
4239
|${indent}${rect.lowerLeft.toJSON("lowerLeft", level + 1)},
4340
|${indent}"height": ${rect.height}
4441
|${indent}"width": ${rect.width}
4542
|$outdent}""".stripMargin
4643

47-
implicit final class TriangleToJSON(
48-
tri: Triangle) extends ToJSONOld[Triangle]:
49-
def toJSON(name: String = "", level: Int = 0): String =
50-
val (outdent, indent) = indentation(level)
51-
s"""${handleName(name)}{
44+
extension(tri: Triangle)
45+
def toJSON(name: String, level: Int): String =
46+
val (outdent, indent) = ToJSONOld.indentation(level)
47+
s"""${ToJSONOld.handleName(name)}{
5248
|${indent}${tri.point1.toJSON("point1", level + 1)},
5349
|${indent}${tri.point2.toJSON("point2", level + 1)},
5450
|${indent}${tri.point3.toJSON("point3", level + 1)},

src/main/scala/progscala3/dsls/payroll/Money.scala

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,21 @@ given Floating[Dollars]: // <3>
99
given Floating[Percentage]:
1010
def fromDigits(digits: String): Percentage = Percentage(digits.toDouble)
1111

12-
implicit class dsc(sc: StringContext): // <4>
12+
/**
13+
* DeanW (September 2025): as of Scala 3.7.X, implicit classes are no longer
14+
* supported. Alternative are extension methods and declaring regular classes
15+
* with given conversions. Here, we'll use the latter:
16+
implicit class dsc(sc: StringContext):
1317
def $(tokens: Any*) =
1418
val str = StringContextUtil.foldTokens(tokens.toSeq, sc.parts)
1519
Dollars(str.toDouble)
20+
*/
21+
22+
class dsc(sc: StringContext): // <4>
23+
def $(tokens: Any*) =
24+
val str = StringContextUtil.foldTokens(tokens.toSeq, sc.parts)
25+
Dollars(str.toDouble)
26+
given Conversion[StringContext, dsc] = sc => dsc(sc)
1627

1728
extension (amount: Double) // <5>
1829
def dollars: Dollars = Dollars(amount)

src/script/scala/progscala3/contexts/UsingClauses.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ defaultOrdering()
2525

2626
// tag::oddEvenImplicitOrdering[]
2727
def oddEvenImplicitOrdering() =
28-
implicit val oddEven: Ordering[Int] = new Ordering[Int]:
28+
// DeanW - Sept 2025: implicit vals are no longer supported:
29+
// implicit val oddEven: Ordering[Int] = new Ordering[Int]:
30+
given oddEven: Ordering[Int] = new Ordering[Int]:
2931
def compare(i: Int, j: Int): Int = i%2 compare j%2 match
3032
case 0 => i compare j
3133
case c => c

src/test/scala/progscala3/contexts/CustomStringInterpolatorSuite.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import munit.*
66
class CustomStringInterpolatorSuite extends FunSuite:
77

88
object JSONToMapInterpolator:
9-
implicit class mapForStringContext(val sc: StringContext): // <1>
9+
/* DeanW: September 2025. Scala is dropping support for implicit classes,
10+
* so we use an extension method instead:
11+
* implicit class mapForStringContext(val sc: StringContext):
12+
* def map(values: String*): Map[String, String] = ...
13+
*/
14+
extension (sc: StringContext) // <1>
1015
def map(values: String*): Map[String, String] = // <2>
1116
val keyRE = """^[\s{,]*(\S+):\s*""".r // <3>
1217
val keys = sc.parts map { // <4>

src/test/scala/progscala3/contexts/ImplicitConversionResolutionSuite.scala

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,27 @@ class ImplicitConversionResolutionSuite extends FunSuite:
88

99
case class Foo(s: String)
1010
object Foo:
11-
implicit def fromString(s: String): Foo =
12-
Foo(s"Foo's implicit conversion: $s")
11+
/* DeanW: September 2025. Scala is dropping support for implicit classes,
12+
* so we use a given Conversion instead:
13+
* implicit def fromString(s: String): Foo =
14+
* Foo(s"Foo's implicit conversion: $s")
15+
*/
16+
given Conversion[String, Foo] =
17+
s => Foo(s"Foo's implicit conversion: $s")
1318

1419
object scope1:
1520
import Foo.*
1621
def apply(foo: Foo): String = foo.s
1722

1823
object scope2:
1924
object implicits:
20-
implicit def overridingConversion(s: String): Foo =
21-
Foo(s"scope2's implicit conversion: $s")
25+
/* DeanW: September 2025. Scala is dropping support for implicit classes,
26+
* so we use a given Conversion instead:
27+
* implicit def overridingConversion(s: String): Foo =
28+
* Foo(s"scope2's implicit conversion: $s")
29+
*/
30+
given Conversion[String, Foo] =
31+
s => Foo(s"scope2's implicit conversion: $s")
2232

2333
def apply(foo: Foo): String = foo.s
2434

@@ -28,7 +38,9 @@ class ImplicitConversionResolutionSuite extends FunSuite:
2838
}
2939

3040
test("The closest implicit conversion in scope is invoked") {
31-
import scope2.implicits.*
41+
// DeanW - September 2025: Now we need to import the given:
42+
// import scope2.implicits.*
43+
import scope2.implicits.given
3244
assert(scope2(Foo("no use of conversion")) == "no use of conversion")
33-
assert(scope2("foo") == "scope2's implicit conversion: foo")
45+
assert(scope2("foo") == "scope2's implicit conversion: foo", scope2("foobar"))
3446
}

src/test/scala/progscala3/contexts/TypeClassesSubtypingSuite.scala

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,22 @@ import munit.*
55

66
class TypeClassesSubtypingSuite extends FunSuite:
77

8-
trait Stringizer[+T]:
9-
def stringize: String
10-
11-
implicit class AnyStringizer(a: Matchable) extends Stringizer[Matchable]:
8+
/* DeanW: September 2025. Scala is dropping support for implicit classes,
9+
* so we use an extension method instead:
10+
*
11+
* trait Stringizer[+T]:
12+
* def stringize: String
13+
*
14+
* implicit class AnyStringizer(a: Matchable) extends Stringizer[Matchable]:
15+
* def stringize: String = a match
16+
* case s: String => s
17+
* case i: Int => (i*10).toString
18+
* case f: Float => (f*10).toString
19+
* case other =>
20+
* throw UnsupportedOperationException(s"Can't stringize $other")
21+
*/
22+
23+
extension (a: Matchable)
1224
def stringize: String = a match
1325
case s: String => s
1426
case i: Int => (i*10).toString

0 commit comments

Comments
 (0)