What is the benefit we declare object Nil to extends TweetList?
why not still use class Nil?
trait TweetList {
def head: Tweet
def tail: TweetList
def isEmpty: Boolean
def foreach(f: Tweet => Unit): Unit =
if (!isEmpty) {
f(head)
tail.foreach(f)
}
}
object Nil extends TweetList {
def head = throw new java.util.NoSuchElementException("head of EmptyList")
def tail = throw new java.util.NoSuchElementException("tail of EmptyList")
def isEmpty = true
}
class Cons(val head: Tweet, val tail: TweetList) extends TweetList {
def isEmpty = false
}
Here's a link for how to use singleton class.
Above comments also mentioned about it, but I explain it again.
In scala, object declaration is used for singleton objects.
In this case, Nil's role is of representing 'emptiness' and is used as last parameters of successive cons
cons(a, Nil) => List(a)
cons(a, cons(b, Nil)) => List(a, b)
So why Nil is object and extends List? cause,
Nil is used as single representation of emptiness of List.
We don't need multiple instances of Nil.
It also makes sense that 2nd parameter of cons is List type.
Related
Beginner question. I have a minimal List implementation:
sealed trait MyList[+A]
case object Nil extends MyList[Nothing]
case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A]
object MyList {
def apply[A](as: A*): MyList[A] =
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
private def toString(l: MyList[Any]): String = {
def f(l: MyList[Any]): String = {
l match {
case Nil => "]"
case Cons(x, Nil) => x.toString + "]"
case Cons(x, xs) => x.toString + ", " + f(xs)
}
}
"[" + f(l)
}
def main(args: Array[String]): Unit = {
println(toString(MyList(1, 2, 3)))
}
}
The output of this program is [1, 2, 3]. Is it possible to get the same output without explicitly calling the private method toString()? That is, to make this the default print output when calling println.
I have tried to add this overriding method:
override def toString: String = ...
But I ran into two problems. The first is that it's not actually affecting the output of println, no matter what this overriding method returns (even though println ultimately calls toString). The second is that I don't know how to print the list content without a parameter for the list itself (toString does not take any parameters, so I assume it must reference this, or something else?).
object MyList and trait MyList are 2 different classes in runtime, so whether you put toString in one or the other changes what will be overriden
sealed trait MyList[+A] {
override def toString = "x"
}
case object Nil extends MyList[Nothing]
case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A]
object MyList {
// ...
override def toString = "y"
}
println(Nil)
println(Cons("string", Nil))
println(MyList)
Additionally case class/case object creates its own override def toString by default so if you want to override toString in both Nil and Cons I would suggest putting final before override def toString in sealed trait MyList[+A] to make sure it is respected.
I tried to implement a StreamInt (without using generic types) and got an error message from REPL
<console>:29: error: constructor cannot be instantiated to expected type;
found : Cons
required: StreamInt.type
case Cons(hd,tl)=> if (n==0) hd else tl().nth(n-1)
The code is pasted below. Anyone can show me how to get it right?
trait StreamInt
case object Empty extends StreamInt
case class Cons (hd: Int, tl: ()=>StreamInt) extends StreamInt
object StreamInt{
def cons(hd:Int, tl: => StreamInt):StreamInt = {
val head=hd
lazy val tail=tl
Cons(head, ()=>tail)
}
def empty:StreamInt = Empty
def apply(as: Int*):StreamInt={
if (as.isEmpty) empty
else cons(as.head, apply(as.tail: _*))
}
def nth(n:Int):Int= this match {
case Cons(hd,tl)=> if (n==0) hd else tl().nth(n-1)
case _ => throw new Exception("out of bound!")
}
}
this match {
That line means you are matching the containing object, in your case that is object StreamInt. That (companion) object can never be a Cons cell. You probably want to have the method nth on the class or trait StreamInt:
sealed trait StreamInt {
def nth(n:Int):Int= this match {
case Cons(hd,tl)=> if (n==0) hd else tl().nth(n-1)
case Empty => throw new Exception("out of bound!")
}
}
case object Empty extends StreamInt
case class Cons (hd: Int, tl: ()=>StreamInt) extends StreamInt
object StreamInt{
def cons(hd:Int, tl: => StreamInt):StreamInt = {
val head=hd
lazy val tail=tl
Cons(head, ()=>tail)
}
def empty:StreamInt = Empty
def apply(as: Int*):StreamInt={
if (as.isEmpty) empty
else cons(as.head, apply(as.tail: _*))
}
}
Now this refers to the trait StreamInt which may indeed by a Con or Empty.
I also added the sealed modifier, that ensures that the compiler can warn you if you miss a particular case when using pattern matching.
Let's say I have a setting such as this:
sealed trait Annotation {
def notes : Seq[String]
}
trait Something extends Annotation{
//do something funny
}
case class A(val i:Int)(val notes:Seq[String] = Nil) extends Something
object A{
def apply(a:A)(notes:Seq[String] = Nil):A = A(a.i)(notes)
}
case class B(val b:Boolean)(val notes:Seq[String] = Nil) extends Something
object B{
def apply(b:B)(notes:Seq[String] = Nil):B = B(b.b)(notes)
}
case class C(val s:String)(val notes:Seq[String] = Nil) extends Something
object C{
def apply(c:C)(notes:Seq[String] = Nil) :C = C(c.s)(notes)
}
Trying to compile this will result in
Main.scala:10: error: in object A, multiple overloaded alternatives of method apply define
default arguments.
object A{
^
Main.scala:15: error: in object B, multiple overloaded alternatives of method apply define
default arguments.
object B{
^
Main.scala:20: error: in object C, multiple overloaded alternatives of method apply define
default arguments.
object C{
^
three errors found
I have read this, so I do at least have an idea as to why this is happening, what I don't know, however, is how I am supposed to resolve the issue.
One possibility would - of course - be to simply omit the default values and force the client to provide Nil when no notes are to be stored, but is there a better solution?
My first guess was to simply make the default arguments explicit:
case class A(i: Int)(val notes: Seq[String]) extends Something
object A {
def apply(i: Int): A = new A(i)(Nil)
def apply(a: A)(notes: Seq[String]): A = new A(a.i)(notes)
def apply(a: A): A = new A(a.i)(Nil)
}
However, now, because of currying, you just have a function Int => A and Int => Seq[String] => A (and analogous for A => A) with the same name in scope.
If you refrain from currying you can manually define the overloaded methods:
case class B(b: Boolean, notes: Seq[String]) extends Something
object B {
def apply(b: Boolean): B = B(b, Nil)
def apply(b: B, notes: Seq[String] = Nil): B = B(b.b, notes)
}
But, since notes is now part of the same parameter list as b, the behavior of the case-class methods such as toString is changed.
println(B(true)) // B(true,List())
println(B(true, List("hello"))) // B(true,List(hello))
println(B(B(false))) // B(false,List())
Finally, to mimic the original behavior more closely, you can implement your own equals, hashCode, toString, and unapply methods:
class C(val s:String, val notes:Seq[String] = Nil) extends Something {
override def toString = s"C($s)"
override def equals(o: Any) = o match {
case C(`s`) => true
case _ => false
}
override def hashCode = s.hashCode
}
object C{
def apply(s: String, notes: Seq[String]) = new C(s, notes)
def apply(s: String): C = C(s, Nil)
def apply(c:C, notes:Seq[String] = Nil): C = C(c.s, notes)
def unapply(c: C): Option[String] = Some(c.s)
}
Example:
val c1 = C("hello")
val c2 = C("hello", List("world"))
println(c1) // C(hello)
println(c2) // C(hello)
println(c1 == c2) // true
c1 match { // hello
case C(n) => println(n)
case _ =>
}
Trait GenericLinkedList , case class Cons and case object Nil were created like below.
The question is I want to use this genericLinkedList however as you know when we write this code var list = new GenericLinkedList , it will not cause Traits cannot create any object , Right? I want to create a class which extends GenericLinkedList but I cannot. How can I fix it ?
trait GenericLinkedList [+T] {
def prepend[TT >: T](x: TT): GenericLinkedList[TT] = this match {
case _ => Cons(x,this)
}
}
case class Cons[+T](head: T,tail: GenericLinkedList[T]) extends GenericLinkedList[T]
case object Nil extends GenericLinkedList[Nothing]
Your issue seems to be unable of doing
val list = new GenericLinkedList
Is your goal creating an empty list?
You can do
val list = new GenericLinkedList[Int] { }
since the trait is not abstract, but it's not pretty. You can alternatively define a companion object for your trait
object GenericLinkedList {
def apply[T](): GenericLinkedList[T] = Nil
}
and use it to initialize an empty list this way
scala> val x = GenericLinkedList[Int]()
// x: GenericLinkedList[Int] = Nil
scala> x.prepend(42)
// res0: GenericLinkedList[Int] = Cons(42,Nil)
By the way, the universal match in the prepend implementation is useless. You can just do
def prepend[TT >: T](x: TT): GenericLinkedList[TT] = Cons(x, this)
In his course on Coursera, professor Martin Odesrky uses a linked list as an example in a lecture about polymorphism and parameterized classes:
package week4
trait List[T] {
def isEmpty: Boolean
def head: T
def tail: List[T]
}
class Cons[T](val head: T, val tail: List[T]) extends List[T] {
def isEmpty = false
}
class Nil[T] extends List[T] {
def isEmpty = true
def head = throw new NoSuchElementException("Nil.head")
def tail = throw new NoSuchElementException("Nil.tail")
}
object Main extends App {
def main(args: Array[String]) {
val lst = new Cons("A", new Cons("B", new Cons("C", new Nil())))
}
}
What bothers me is the instantiation of class Nil in the last lines, new Nil().
How would one define Nil as an object instead of as a Scala class, and have it conform to the parameterized type List[T] ?
I'd like to refer to the object Nil as in the following line of code (no instantiation), and make it have the correct type
new Cons("A", new Cons("B", new Cons("C", Nil)))
In the actual Scala library (List.scala) here's how it is done,
case object Nil extends List[Nothing] { ...
Probably in the class he wanted to avoid introducing Nothing, which is the type at the bottom of Scala's type lattice.
Given the trait List[T] definition of list, you can't do it. That definition means you need a distinct Nil for each T, since for every T1 and T2, not identical, List[T1] is not compatible with List[T2]. Since Nil must "be a" List[Tx], any Tx you pick will be incompatible with all others.
To get around that you need co-variance, which, iirc, is explained a couple of lessons later.
Here is the mix of Kipton's suggestion and my own:
trait List[+T] {
def isEmpty: Boolean
def head: T
def tail: List[T]
}
class Cons[+T](val head: T, val tail: List[T]) extends List[T] {
def isEmpty = false
}
case object Nil extends List[Nothing] {
def isEmpty = true
def head = throw new NoSuchElementException("Nil.head")
def tail = throw new NoSuchElementException("Nil.tail")
}
object ListTest {
def main(args: Array[String]) {
val lst = new Cons("A", new Cons("B", new Cons("C", Nil)))
}
}
btw, your code doesn't compile with my Scala installation. App implements "main", so you have to override it or (as is the intention of App) leave it out.
Notice that you need List and Cons to be covariant (e.g. List[+T]), which basically means for subtype U of T, it also holds that List[U] is a subtype of List[T] and by extension that List[Nothing] is a subtype of your list.