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.
Related
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.
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 _ =>
}
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.
I'm looking for a way to have classes that behave just like case classes, but that are automatically hash consed.
One way to achieve this for integer lists would be:
import scala.collection.mutable.{Map=>MutableMap}
sealed abstract class List
class Cons(val head: Int, val tail: List) extends List
case object Nil extends List
object Cons {
val cache : MutableMap[(Int,List),Cons] = MutableMap.empty
def apply(head : Int, tail : List) = cache.getOrElse((head,tail), {
val newCons = new Cons(head, tail)
cache((head,tail)) = newCons
newCons
})
def unapply(lst : List) : Option[(Int,List)] = {
if (lst != null && lst.isInstanceOf[Cons]) {
val asCons = lst.asInstanceOf[Cons]
Some((asCons.head, asCons.tail))
} else None
}
}
And, for instance, while
scala> (5 :: 4 :: scala.Nil) eq (5 :: 4 :: scala.Nil)
resN: Boolean = false
we get
scala> Cons(5, Cons(4, Nil)) eq Cons(5, Cons(4, Nil))
resN: Boolean = true
Now what I'm looking for is a generic way to achieve this (or something very similar). Ideally, I don't want to have to type much more than:
class Cons(val head : Int, val tail : List) extends List with HashConsed2[Int,List]
(or similar). Can someone come up with some type system voodoo to help me, or will I have to wait for the macro language to be available?
You can define a few InternableN[Arg1, Arg2, ..., ResultType] traits for N being the number of arguments to apply(): Internable1[A,Z], Internable2[A,B,Z], etc. These traits define the cache itself, the intern() method and the apply method we want to hijack.
We'll have to define a trait (or an abstract class) to assure your InternableN traits that there is indeed an apply method to be overriden, let's call it Applyable.
trait Applyable1[A, Z] {
def apply(a: A): Z
}
trait Internable1[A, Z] extends Applyable1[A, Z] {
private[this] val cache = WeakHashMap[(A), Z]()
private[this] def intern(args: (A))(builder: => Z) = {
cache.getOrElse(args, {
val newObj = builder
cache(args) = newObj
newObj
})
}
abstract override def apply(arg: A) = {
println("Internable1: hijacking apply")
intern(arg) { super.apply(arg) }
}
}
The companion object of your class will have to be a mixin of a concrete class implementing ApplyableN with InternableN. It would not work to have apply directly defined in your companion object.
// class with one apply arg
abstract class SomeClassCompanion extends Applyable1[Int, SomeClass] {
def apply(value: Int): SomeClass = {
println("original apply")
new SomeClass(value)
}
}
class SomeClass(val value: Int)
object SomeClass extends SomeClassCompanion with Internable1[Int, SomeClass]
One good thing about this is that the original apply need not be modified to cater for interning. It only creates instances and is only called when they need to be created.
The whole thing can (and should) also be defined for classes with more than one argument. For the two-argument case:
trait Applyable2[A, B, Z] {
def apply(a: A, b: B): Z
}
trait Internable2[A, B, Z] extends Applyable2[A, B, Z] {
private[this] val cache = WeakHashMap[(A, B), Z]()
private[this] def intern(args: (A, B))(builder: => Z) = {
cache.getOrElse(args, {
val newObj = builder
cache(args) = newObj
newObj
})
}
abstract override def apply(a: A, b: B) = {
println("Internable2: hijacking apply")
intern((a, b)) { super.apply(a, b) }
}
}
// class with two apply arg
abstract class AnotherClassCompanion extends Applyable2[String, String, AnotherClass] {
def apply(one: String, two: String): AnotherClass = {
println("original apply")
new AnotherClass(one, two)
}
}
class AnotherClass(val one: String, val two: String)
object AnotherClass extends AnotherClassCompanion with Internable2[String, String, AnotherClass]
The interaction shows that the Internables' apply method executes prior to the original apply() which gets executed only if needed.
scala> import SomeClass._
import SomeClass._
scala> SomeClass(1)
Internable1: hijacking apply
original apply
res0: SomeClass = SomeClass#2e239525
scala> import AnotherClass._
import AnotherClass._
scala> AnotherClass("earthling", "greetings")
Internable2: hijacking apply
original apply
res1: AnotherClass = AnotherClass#329b5c95
scala> AnotherClass("earthling", "greetings")
Internable2: hijacking apply
res2: AnotherClass = AnotherClass#329b5c95
I chose to use a WeakHashMap so that the interning cache does not prevent garbage collection of interned instances once they're no longer referenced elsewhere.
Code neatly available as a Github gist.
Maybe a little hacky, but you could try defining your own intern() method, like Java's String has:
import scala.collection.mutable.{Map=>MutableMap}
object HashConsed {
val cache: MutableMap[(Class[_],Int), HashConsed] = MutableMap.empty
}
trait HashConsed {
def intern(): HashConsed =
HashConsed.cache.getOrElse((getClass, hashCode), {
HashConsed.cache((getClass, hashCode)) = this
this
})
}
case class Foo(bar: Int, baz: String) extends HashConsed
val foo1 = Foo(1, "one").intern()
val foo2 = Foo(1, "one").intern()
println(foo1 == foo2) // true
println(foo1 eq foo2) // true