Skip to content

Commit 42be6ff

Browse files
authored
Merge pull request #89 from scala-exercises/enrique-2-13-1-update
Update to Scala 2.13.1
2 parents a3090b5 + 43129f3 commit 42be6ff

16 files changed

+162
-89
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
language: scala
22
scala:
3-
- 2.12.10
3+
- 2.13.1
44
jdk:
55
- openjdk8
66
script:

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import ProjectPlugin.autoImport._
22

3-
val scalaExercisesV = "0.5.0-SNAPSHOT"
3+
val scalaExercisesV = "0.6.0-SNAPSHOT"
44

55
def dep(artifactId: String) = "org.scala-exercises" %% artifactId % scalaExercisesV
66

project/ProjectPlugin.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ object ProjectPlugin extends AutoPlugin {
1515
object autoImport {
1616

1717
lazy val V = new {
18-
val scala212: String = "2.12.10"
18+
val scala213: String = "2.13.1"
1919
val shapeless: String = "2.3.3"
2020
val scalatest: String = "3.1.0"
2121
val scalatestplusScheck: String = "3.1.0.0-RC2"
@@ -39,7 +39,7 @@ object ProjectPlugin extends AutoPlugin {
3939
organizationEmail = "hello@47deg.com"
4040
),
4141
orgLicenseSetting := ApacheLicense,
42-
scalaVersion := V.scala212,
42+
scalaVersion := V.scala213,
4343
scalaOrganization := "org.scala-lang",
4444
resolvers ++= Seq(
4545
Resolver.mavenLocal,

project/build.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version=1.2.8
1+
sbt.version=1.3.7

project/plugins.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ resolvers ++= Seq(
22
Resolver.sonatypeRepo("snapshots")
33
)
44

5-
addSbtPlugin("org.scala-exercises" % "sbt-exercise" % "0.5.0-SNAPSHOT")
5+
addSbtPlugin("org.scala-exercises" % "sbt-exercise" % "0.6.0-SNAPSHOT")
66
addSbtPlugin("com.47deg" % "sbt-org-policies" % "0.12.0-M3")

src/main/scala/scalatutorial/sections/FunctionalLoops.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,10 @@ object FunctionalLoops extends ScalaTutorialSection {
137137
*
138138
* Complete the following method definition that computes the factorial of a number:
139139
*/
140-
def factorialExercise(res0: Int, res1: Int, res2: Int): Unit = {
140+
def factorialExercise(res0: Int, res1: Int): Unit = {
141141
def factorial(n: Int): Int =
142142
if (n == res0) res1
143-
else factorial(n - res2) * n
143+
else factorial(n - 1) * n
144144

145145
factorial(3) shouldBe 6
146146
factorial(4) shouldBe 24

src/main/scala/scalatutorial/sections/HigherOrderFunctions.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,9 @@ object HigherOrderFunctions extends ScalaTutorialSection {
161161
def sum(f: Int => Int, a: Int, b: Int): Int = {
162162
def loop(x: Int, acc: Int): Int =
163163
if (x > b) acc
164-
else loop(x + res0, acc + f(x))
165-
loop(a, res1)
164+
else loop(x + 1, acc + f(x))
165+
loop(a, res0)
166166
}
167-
sum(x => x, 1, 10) shouldBe 55
167+
sum(x => x, 1, res1) shouldBe 55
168168
}
169169
}

src/main/scala/scalatutorial/sections/LazyEvaluation.scala

Lines changed: 67 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,29 @@ object LazyEvaluation extends ScalaTutorialSection {
4343
* - Avoid computing the tail of a sequence until it is needed for the evaluation
4444
* result (which might be never)
4545
*
46-
* This idea is implemented in a new class, the `Stream`.
46+
* This idea is implemented in a new class, the `LazyList`.
4747
*
48-
* Streams are similar to lists, but their tail is evaluated only ''on demand''.
48+
* LazyLists are similar to lists, but their elements are evaluated only ''on demand''.
4949
*
50-
* = Defining Streams =
50+
* = Defining LazyLists =
5151
*
52-
* Streams are defined from a constant `Stream.empty` and a constructor `Stream.cons`.
52+
* LazyLists are defined from a constructor `LazyList.cons`.
5353
*
5454
* For instance,
5555
*
5656
* {{{
57-
* val xs = Stream.cons(1, Stream.cons(2, Stream.empty))
57+
* val xs = LazyList.cons(1, LazyList.cons(2, LazyList.empty))
5858
* }}}
5959
*
60-
* = Stream Ranges =
60+
* = LazyList Ranges =
6161
*
62-
* Let's try to write a function that returns a `Stream` representing a range of numbers
62+
* Let's try to write a function that returns a `LazyList` representing a range of numbers
6363
* between `lo` and `hi`:
6464
*
6565
* {{{
66-
* def streamRange(lo: Int, hi: Int): Stream[Int] =
67-
* if (lo >= hi) Stream.empty
68-
* else Stream.cons(lo, streamRange(lo + 1, hi))
66+
* def llRange(lo: Int, hi: Int): LazyList[Int] =
67+
* if (lo >= hi) LazyList.empty
68+
* else LazyList.cons(lo, llRange(lo + 1, hi))
6969
* }}}
7070
*
7171
* Compare to the same function that produces a list:
@@ -79,113 +79,122 @@ object LazyEvaluation extends ScalaTutorialSection {
7979
* The functions have almost identical structure yet they evaluate quite differently.
8080
*
8181
* - `listRange(start, end)` will produce a list with `end - start` elements and return it.
82-
* - `streamRange(start, end)` returns a single object of type `Stream` with `start` as head element.
82+
* - `llRange(start, end)` returns a single object of type `LazyList` with `start` as head element.
8383
* - The other elements are only computed when they are needed, where
8484
* “needed” means that someone calls `tail` on the stream.
8585
*
86-
* = Methods on Streams =
86+
* = Methods on LazyLists =
8787
*
88-
* `Stream` supports almost all methods of `List`.
88+
* `LazyList` supports almost all methods of `List`.
8989
*
9090
* For instance, to find the second prime number between 1000 and 10000:
9191
*
9292
* {{{
93-
* (streamRange(1000, 10000) filter isPrime)(1)
93+
* (llRange(1000, 10000) filter isPrime)(1)
9494
* }}}
9595
*
9696
* The one major exception is `::`.
9797
*
98-
* `x :: xs` always produces a list, never a stream.
98+
* `x :: xs` always produces a list, never a lazy list.
9999
*
100-
* There is however an alternative operator `#::` which produces a stream.
100+
* There is however an alternative operator `#::` which produces a lazy list.
101101
*
102102
* {{{
103-
* x #:: xs == Stream.cons(x, xs)
103+
* x #:: xs == LazyList.cons(x, xs)
104104
* }}}
105105
*
106106
* `#::` can be used in expressions as well as patterns.
107107
*
108-
* = Implementation of Streams =
108+
* = Implementation of LazyLists =
109109
*
110-
* The implementation of streams is quite close to the one of lists.
110+
* The implementation of lazy lists is quite close to the one of lists.
111111
*
112-
* Here's the trait `Stream`:
112+
* Here's the class `LazyList`:
113113
*
114114
* {{{
115-
* trait Stream[+T] extends Seq[T] {
116-
* def isEmpty: Boolean
117-
* def head: T
118-
* def tail: Stream[T]
119-
*
115+
* final class LazyList[+A] ... extends ... {
116+
* override def isEmpty: Boolean = ...
117+
* override def head: A = ...
118+
* override def tail: LazyList[A] = ...
119+
* ...
120120
* }
121121
* }}}
122122
*
123123
* As for lists, all other methods can be defined in terms of these three.
124124
*
125-
* Concrete implementations of streams are defined in the `Stream` companion object.
125+
* Concrete implementations of streams are defined in the `LazyList.State` companion object.
126126
* Here's a first draft:
127127
*
128128
* {{{
129-
* object Stream {
130-
* def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
131-
* def isEmpty = false
132-
* def head = hd
133-
* def tail = tl
134-
* override def toString = "Stream(" + hd + ", ?)"
135-
* }
136-
* val empty = new Stream[Nothing] {
137-
* def isEmpty = true
138-
* def head = throw new NoSuchElementException("empty.head")
139-
* def tail = throw new NoSuchElementException("empty.tail")
140-
* override def toString = "Stream()"
129+
* private object State {
130+
* object Empty extends State[Nothing] {
131+
* def head: Nothing = throw new NoSuchElementException("head of empty lazy list")
132+
* def tail: LazyList[Nothing] = throw new UnsupportedOperationException("tail of empty lazy list")
141133
* }
134+
*
135+
* final class Cons[A](val head: A, val tail: LazyList[A]) extends State[A]
142136
* }
143137
* }}}
144138
*
145-
* The only important difference between the implementations of `List` and `Stream`
146-
* concern `tl`, the second parameter of `Stream.cons`.
139+
* The only important difference between the implementations of `List` and `LazyList`
140+
* concern `tail`, the second parameter of `LazyList.cons`.
147141
*
148-
* For streams, this is a by-name parameter: the type of `tl` starts with `=>`. In such
142+
* For lazy lists, this is a by-name parameter: the type of `tail` starts with `=>`. In such
149143
* a case, this parameter is evaluated by following the rules of the call-by-name model.
150144
*
151-
* That's why the second argument to `Stream.cons` is not evaluated at the point of call.
145+
* That's why the second argument to `LazyList.cons` is not evaluated at the point of call.
152146
*
153-
* Instead, it will be evaluated each time someone calls `tail` on a `Stream` object.
147+
* Instead, it will be evaluated each time someone calls `tail` on a `LazyList` object.
154148
*
155-
* The other stream methods are implemented analogously to their list counterparts.
149+
* In Scala 2.13, LazyList (previously Stream) became fully lazy from head to tail. To make it possible,
150+
* methods (`filter`, `flatMap`...) are implemented in a way where the head is not being evaluated if is
151+
* not explicitly indicated.
156152
*
157153
* For instance, here's `filter`:
158154
*
159155
* {{{
160-
* class Stream[+T] {
161-
* …
162-
* def filter(p: T => Boolean): Stream[T] =
163-
* if (isEmpty) this
164-
* else if (p(head)) cons(head, tail.filter(p))
165-
* else tail.filter(p)
156+
* object LazyList extends SeqFactory[LazyList] {
157+
* ...
158+
* private def filterImpl[A](ll: LazyList[A], p: A => Boolean, isFlipped: Boolean): LazyList[A] = {
159+
* // DO NOT REFERENCE `ll` ANYWHERE ELSE, OR IT WILL LEAK THE HEAD
160+
* var restRef = ll // val restRef = new ObjectRef(ll)
161+
* newLL {
162+
* var elem: A = null.asInstanceOf[A]
163+
* var found = false
164+
* var rest = restRef // var rest = restRef.elem
165+
* while (!found && !rest.isEmpty) {
166+
* elem = rest.head
167+
* found = p(elem) != isFlipped
168+
* rest = rest.tail
169+
* restRef = rest // restRef.elem = rest
170+
* }
171+
* if (found) sCons(elem, filterImpl(rest, p, isFlipped)) else State.Empty
172+
* }
166173
* }
167174
* }}}
168175
*
169176
* = Exercise =
170177
*
171-
* Consider the following modification of `streamRange`. When you write
172-
* `streamRange(1, 10).take(3).toList` what is the value of `rec`?
178+
* Consider the following modification of `llRange`. When you write
179+
* `llRange(1, 10).take(3).toList` what is the value of `rec`?
180+
*
181+
* Be careful, head is evaluating too!
173182
*/
174-
def streamRangeExercise(res0: Int): Unit = {
183+
def llRangeExercise(res0: Int): Unit = {
175184
var rec = 0
176-
def streamRange(lo: Int, hi: Int): Stream[Int] = {
185+
def llRange(lo: Int, hi: Int): LazyList[Int] = {
177186
rec = rec + 1
178-
if (lo >= hi) Stream.empty
179-
else Stream.cons(lo, streamRange(lo + 1, hi))
187+
if (lo >= hi) LazyList.empty
188+
else LazyList.cons(lo, llRange(lo + 1, hi))
180189
}
181-
streamRange(1, 10).take(3).toList
190+
llRange(1, 10).take(3).toList
182191
rec shouldBe res0
183192
}
184193

185194
/**
186195
* = Lazy Evaluation =
187196
*
188-
* The proposed `Stream` implementation suffers from a serious potential performance
197+
* The proposed `LazyList` implementation suffers from a serious potential performance
189198
* problem: If `tail` is called several times, the corresponding stream
190199
* will be recomputed each time.
191200
*
@@ -210,18 +219,6 @@ object LazyEvaluation extends ScalaTutorialSection {
210219
* lazy val x = expr
211220
* }}}
212221
*
213-
* = Lazy Vals and Streams =
214-
*
215-
* Using a lazy value for `tail`, `Stream.cons` can be implemented more efficiently:
216-
*
217-
* {{{
218-
* def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
219-
* def head = hd
220-
* lazy val tail = tl
221-
* …
222-
* }
223-
* }}}
224-
*
225222
* == Exercise ==
226223
*/
227224
def lazyVal(res0: String): Unit = {

src/main/scala/scalatutorial/sections/StandardLibrary.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ object StandardLibrary extends ScalaTutorialSection {
295295
def triple(x: Int): Int = 3 * x
296296

297297
def tripleEither(x: Either[String, Int]): Either[String, Int] =
298-
x.right.map(triple)
298+
x.map(triple)
299299

300300
tripleEither(Right(1)) shouldBe res0
301301
tripleEither(Left("not a number")) shouldBe res1

src/main/scala/scalatutorial/sections/SyntacticConveniences.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ object SyntacticConveniences extends ScalaTutorialSection {
342342
*/
343343
def repeatedParameters(res0: Double): Unit = {
344344
def average(x: Int, xs: Int*): Double =
345-
(x :: xs.to[List]).sum.toDouble / (xs.size + 1)
345+
(x :: xs.to(List)).sum.toDouble / (xs.size + 1)
346346

347347
average(1) shouldBe 1.0
348348
average(1, 2) shouldBe 1.5

0 commit comments

Comments
 (0)