Making my first steps in Scala, I have run into the first misunderstanding.
I took a classic example of a linked list.
sealed trait List[+A] // `List` data type, parameterized on a type, `A`
case object Nil extends List[Nothing] // A `List` data constructor representing the empty list
/* Another data constructor, representing nonempty lists. Note that `tail` is another `List[A]`,
which may be `Nil` or another `Cons`.
*/
case class Cons[+A](head: A, tail: List[A]) extends List[A]
object List { // `List` companion object. Contains functions for creating and working with lists.
def sum(ints: List[Int]): Int = ints match { // A function that uses pattern matching to add up a list of integers
case Nil => 0 // The sum of the empty list is 0.
case Cons(x,xs) => x + sum(xs) // The sum of a list starting with `x` is `x` plus the sum of the rest of the list.
}
def product(ds: List[Double]): Double = ds match {
case Nil => 1.0
case Cons(0.0, _) => 0.0
case Cons(x,xs) => x * product(xs)
}
def apply[A](as: A*): List[A] = // Variadic function syntax
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
val x = List(1,2,3,4,5) match {
case Cons(x, Cons(2, Cons(4, _))) => x
case Nil => 42
case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y
case Cons(h, t) => h + sum(t)
case _ => 101
}
}
There are a few questions:
1) why custom linked List class doesn't conflict with built-in scala.collection.immutable.List.type
2) why a piece of code is supposed to be correct when we are matching built-in List to the custom linked list?
val x = List(1,2,3,4,5) match {
case Cons(x, Cons(2, Cons(4, _))) => x
case Nil => 42
case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y
case Cons(h, t) => h + sum(t)
case _ => 101
}
The custom linked-list class doesn't conflicts with the built-in scala.collection.immutable.List.type because local declarations, such as your custom List type, has higher precedence than an import (even non-explicit ones such as Scala's built-in List). See Chapter 2 of the Scala Specification for the full precedence order.
The referred matching code is not matching the built-in List, but your own locally declared List. You can see it yourself, by renaming your List to something like CustomList and see that some errors will appear, or to fully qualify the built-in List as the following code.
The following code actually matches the built-in List with your custom List structures and won't compile:
val x = scala.collection.immutable.List(1,2,3,4,5) match {
case Cons(x, Cons(2, Cons(4, _))) => x
case Nil => 42
case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y
case Cons(h, t) => h + sum(t)
case _ => 101
}
Your question is really about scope, I believe. You have defined your own List which is unrelated to that in scala.collection.immutable... The same with Cons and Nil.
When you instantiate the List in part 2), you are instantiating your List, not the one in the Scala library.
Or am I missing something?
Related
I was trying to implement the SKI combinator calculus in Dotty using match types.
A quick description of the SKI combinator calculus:
S, K, and I are terms
(xy) is a term if x and y are terms and is like function application
(((Sx)y)z) (same asSxyz) returns xz(yz) (same as (xz)(yz))
((Kx)y) (same as Kxy) returns x
(Ix) returns x
Below is what I used (R reduces the term as much as possible). A term (xy) is written as a tuple (x,y), and S, K, and I are traits.
trait S
trait K
trait I
type R[T] = T match {
case (((S,x),y),z) => R[((x,z),(y,z))]
case ((K,x),y) => R[x]
case (I,x) => R[x]
case (a,b) => R[a] match {
case `a` => (a, R[b])
case _ => R[(R[a], R[b])]
}
case T => T
}
However, the following 2 lines don't compile (both for the same reason) (Scastie):
val check: (K, K) = ??? : R[(((S,I),I),K)]
val check2: (K, K) = ??? : R[((I,K),(I,K))]
The error says that it required (K,K) but found R[((I, K), (I, K))]. I expected it first see the S and turn it into (IK)(IK), or R[((I,K),(I,K))], after which it should match the evaluation of the first (I, K) and see that it is K, which is not the same as (I, K), making it return R[(R[(I,K)], R[(I,K)])], which becomes R[(K,K)], which becomes just (K,K).
However, it doesn't go beyond R[((I,K),(I,K))]. Apparently, it does not reduce the term if it's nested. This is odd, because I tried the same approach but with an actual runtime function, and it appears to work properly (Scastie).
case object S
case object K
case object I
def r(t: Any): Any = t match {
case (((S,x),y),z) => r(((x,z),(y,z)))
case ((K,x),y) => r(x)
case (I,x) => r(x)
case (a,b) => r(a) match {
case `a` => (a, r(b))
case c => (c, r(b))
}
case _ => t
}
The output from println(r((((S, I), I), K))) is (K,K), as expected.
Interestingly enough, removing the rule for K lets it compile, but it doesn't recognize (K, K) and R[(K, K)] as the same type. Perhaps this is what is causing the problem? (Scastie)
def check2: (K, K) = ??? : R[(K, K)]
//Found: R[(K, K)]
//Required: (K, K)
S, K, and I are not disjoint. The intersections K with I etc. are inhabited:
println(new K with I)
In a match type, a case is only skipped when the types are disjoint. So, e.g. this fails:
type IsK[T] = T match {
case K => true
case _ => false
}
#main def main() = println(valueOf[IsK[I]])
because the case K => _ branch cannot be skipped, since there are values of I that are Ks. So, e.g. in your case R[(K, K)] gets stuck on case (I, x) => _, since there are some (K, K)s that are also (I, x)s (e.g. (new K with I, new K {})). You never get to the case (a,b) => _, which would take us to (K, K).
You can make S, K, and I classes, which makes them disjoint, since you can't inherit from two classes at once.
class S
class K
class I
type R[T] = T match {
case (((S,x),y),z) => R[((x,z),(y,z))]
case ((K,x),y) => R[x]
case (I,x) => R[x]
case (a,b) => R[a] match {
case `a` => (a, R[b])
case _ => R[(R[a], R[b])]
}
case T => T
}
#main def main(): Unit = {
println(implicitly[R[(K, K)] =:= (K, K)])
println(implicitly[R[(((S,I),I),K)] =:= (K, K)])
}
Scastie
Another solution is making them all singleton types:
object S; type S = S.type
object K; type K = K.type
object I; type I = I.type
// or, heck
type S = 0
type K = 1
type I = 2
I was trying to implement the SKI combinator calculus in Dotty using match types.
A quick description of the SKI combinator calculus:
S, K, and I are terms
(xy) is a term if x and y are terms and is like function application
(((Sx)y)z) (same asSxyz) returns xz(yz) (same as (xz)(yz))
((Kx)y) (same as Kxy) returns x
(Ix) returns x
Below is what I used (R reduces the term as much as possible). A term (xy) is written as a tuple (x,y), and S, K, and I are traits.
trait S
trait K
trait I
type R[T] = T match {
case (((S,x),y),z) => R[((x,z),(y,z))]
case ((K,x),y) => R[x]
case (I,x) => R[x]
case (a,b) => R[a] match {
case `a` => (a, R[b])
case _ => R[(R[a], R[b])]
}
case T => T
}
However, the following 2 lines don't compile (both for the same reason) (Scastie):
val check: (K, K) = ??? : R[(((S,I),I),K)]
val check2: (K, K) = ??? : R[((I,K),(I,K))]
The error says that it required (K,K) but found R[((I, K), (I, K))]. I expected it first see the S and turn it into (IK)(IK), or R[((I,K),(I,K))], after which it should match the evaluation of the first (I, K) and see that it is K, which is not the same as (I, K), making it return R[(R[(I,K)], R[(I,K)])], which becomes R[(K,K)], which becomes just (K,K).
However, it doesn't go beyond R[((I,K),(I,K))]. Apparently, it does not reduce the term if it's nested. This is odd, because I tried the same approach but with an actual runtime function, and it appears to work properly (Scastie).
case object S
case object K
case object I
def r(t: Any): Any = t match {
case (((S,x),y),z) => r(((x,z),(y,z)))
case ((K,x),y) => r(x)
case (I,x) => r(x)
case (a,b) => r(a) match {
case `a` => (a, r(b))
case c => (c, r(b))
}
case _ => t
}
The output from println(r((((S, I), I), K))) is (K,K), as expected.
Interestingly enough, removing the rule for K lets it compile, but it doesn't recognize (K, K) and R[(K, K)] as the same type. Perhaps this is what is causing the problem? (Scastie)
def check2: (K, K) = ??? : R[(K, K)]
//Found: R[(K, K)]
//Required: (K, K)
S, K, and I are not disjoint. The intersections K with I etc. are inhabited:
println(new K with I)
In a match type, a case is only skipped when the types are disjoint. So, e.g. this fails:
type IsK[T] = T match {
case K => true
case _ => false
}
#main def main() = println(valueOf[IsK[I]])
because the case K => _ branch cannot be skipped, since there are values of I that are Ks. So, e.g. in your case R[(K, K)] gets stuck on case (I, x) => _, since there are some (K, K)s that are also (I, x)s (e.g. (new K with I, new K {})). You never get to the case (a,b) => _, which would take us to (K, K).
You can make S, K, and I classes, which makes them disjoint, since you can't inherit from two classes at once.
class S
class K
class I
type R[T] = T match {
case (((S,x),y),z) => R[((x,z),(y,z))]
case ((K,x),y) => R[x]
case (I,x) => R[x]
case (a,b) => R[a] match {
case `a` => (a, R[b])
case _ => R[(R[a], R[b])]
}
case T => T
}
#main def main(): Unit = {
println(implicitly[R[(K, K)] =:= (K, K)])
println(implicitly[R[(((S,I),I),K)] =:= (K, K)])
}
Scastie
Another solution is making them all singleton types:
object S; type S = S.type
object K; type K = K.type
object I; type I = I.type
// or, heck
type S = 0
type K = 1
type I = 2
I understand (kind of) how pattern matching works in Scala,
Let's say I have two lists of the form:
sealed abstract class IntList
case class Empty() extends IntList // The empty list, often called Nils
case class Element(n: Int, tail: IntList) extends IntList // Element is usually called Cons
Let's say I want to create the function take(n, xs)
It should return the first n elements of xs.
I tried with normal pattern matching:
def take(n: Int, xs: IntList): IntList = xs match {
case n == 0 => Empty()
case xs : Empty => Empty()
case xs : Element => Element(xs.n, take(n-1, xs))
}
But then of course, n is not recognised, error: not found: value == case n == 0 => Empty()
How can I do this, it is probably simple but I am a beginner in Scala?
You have a typo in the first case where it should be
case _ if n == 0 => Empty()
and a bug in third case where you forgot to pass the tail
case xs: Element => Element(xs.n, take(n-1, xs.tail))
Try
sealed trait IntList
case object Empty extends IntList
case class Element(n: Int, tail: IntList) extends IntList
def take(n: Int, xs: IntList): IntList = xs match {
case _ if n == 0 => Empty
case Empty => Empty
case Element(n, tail) => Element(n, take(n-1, tail))
}
val list = Element(1, Element(2, Element(3, Empty)))
take(2, list) // res0: IntList = Element(1,Element(2,Empty))
Consider case object instead of case class Empty() when there is no data as per Differences between case object T and case class T() when defining ADT?
Specifically:
scala> def f(n: Seq[Any]) = n match {
case Nil => "Empty"
case h :: t => "Non-empty"
}
f: (n: Seq[Any])String
scala> f(Stream())
res1: String = Empty
scala> f(List(1))
res17: String = Non-empty
scala> f(Stream(1))
scala.MatchError: Stream(1, ?) (of class scala.collection.immutable.Stream$Cons)
at .f(<console>:13)
... 33 elided
There are a lot of other ways to implement this, but at written the code was statically safe and failed at run time. What's going on?
For Stream, the concat symbol should be #::, the pattern match should like:
def f(n: Seq[Any]) = n match {
case Nil => "Empty"
case h :: t => "Non-empty"
case h #:: t => "Non-empty stream"
}
for :: is for List / Seq type(List extends from Seq:) ), see:
final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] {
You can only use :: to deconstruct a List and not a Stream. Since the Stream you provide does not match a List, you get a MatchError. If you want f to support streams using that type of matching (extractors), you can use #::.
def f(n: Seq[Any]) = n match {
case Nil => "Empty"
case h :: t => "Non-empty"
case h #:: t => "Non-empty"
}
In general, this approach is very fragile because both extractor types shown above will only work for those two types of Seq. Others maybe break. If all you care about is determining whether or not the Seq is empty or not, then simply use n.nonEmpty or n.isEmpty and deal with the Boolean result. Otherwise, trying to provide an exhaustive match on a trait that is not sealed is bound to fail.
You can also use the Seq extractor:
def f(n: Seq[Any]) = n match {
case Nil => "Empty"
case Seq(_*) => "Non-empty"
}
While other answers show correctly an extractor specific for Stream, which is #::, there exists an extractor for Seq (and anything derived from SeqLike) - it is +:, this works for both List (as ::) and Stream (as `#::). With this extractor you can easily write a function which works with both:
def f(n: Seq[Any]) = n match {
case h +: t => "Non-empty"
case _ => "Empty"
}
See also Scala pattern matching on sequences other than Lists
I have a list of elements, and want to group them by class and then process the results.
trait H
case class A(x: Int) extends H
case class B(x: Int) extends H
val x = List(A(1),B(2),B(3))
val y = x.groupBy(_.getClass)
y.map(_ match {case (A, alist) => println("found me some As")
case (B, blist) => println("not As")})
Unfortunately this produces errors like:
<console>:17: error: pattern type is incompatible with expected type;
found : A.type
required: Class[_ <: Product]
Note: if you intended to match against the class, try `case _: A`
y.map(_ match {case (A, alist) => println("found me some As")
That is, I can't seem to find the proper way to do case matching when the item being matched is a class not just an instance of one.
A partial solution is the following:
val z = y.map(_ match {case (atype, alist) => alist })
z.map(_ match {
case alist if alist.head.isInstanceOf[A] => alist
case blist => List()
})
But it feels like there should be a better way of doing this using the keys to the initial Map returned from groupBy.
A in case (A, alist) refers to the companion object of A, which has the type A.type. That's why you're getting that error message. If you want to match against the class meta data, you should refer to classOf[A]. There's also no reason to use match, as you can pattern match with map already.
y.map {
case (c, alist) if(c == classOf[A]) => println("found me some As")
case (c, blist) if(c == classOf[B]) => println("not As")
}