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.
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.
Let's say we have the following traits:
trait MyValue
object MyValue {
case class MyBoolean(record: Boolean) extends MyValue
case class MyLong(record: Long) extends MyValue
}
trait MyValueExtractor[T] {
def apply(record: T): Option[MyValue]
}
trait MyThing[T] {
def name: String
def myValueExtractor: MyValueExtractor[T]
def myValue(record: T): Option[MyValue] = myValueExtractor(record)
}
What I want is something like this but without the second type parameter.
Note: I can't actually update the MyThing trait; I'm just using this as an illustration of the intended functionality.
trait MyThing[T, U] {
def name: String
def myValueExtractor: MyValueExtractor[T]
def myValue(record: T): Option[MyValue] = myValueExtractor(record)
def myRelatedValue(record: T): Option[U]
}
I'm wondering if I could use the type class pattern to help solve this (i.e., import some rich class that implicitly gives me a myRelatedValue method)?
Here's the rub. Every time T (above) is MyValue.MyBoolean, U must be a String. Every time T is MyValue.MyLong, U must be a Double. In other words, there's a sort of underlying mapping between T and U.
Is there a good way to do this using type class?
Sure. You just need to define some Mapping typeclass with implementations for your desired pairs of types. Then MyThing can have a method that takes an implicit typeclass instance and simply invokes its method.
Here's the code (I removed the unneeded details)
// types
case class MyBoolean(record: Boolean)
case class MyLong(record: Long)
// trait which uses the Mapping typeclass
trait MyThing[T] {
def myRelatedValue[U](record: T)(implicit ev: Mapping[T, U]): Option[U] = ev.relatedValue(record)
}
// typeclass itself
trait Mapping[T, U] {
def relatedValue(record: T): Option[U]
}
object Mapping {
implicit val boolStringMapping = new Mapping[MyBoolean, String] {
def relatedValue(record: MyBoolean) = Some(record.record.toString)
}
implicit val longDoubleMapping = new Mapping[MyLong, Double] {
def relatedValue(record: MyLong) = Some(record.record)
}
}
// usage
val myBoolThing = new MyThing[MyBoolean] {}
val myLongThing = new MyThing[MyLong] {}
val myStringThing = new MyThing[String] {}
myBoolThing.myRelatedValue(MyBoolean(true)) // Some(true)
myLongThing.myRelatedValue(MyLong(42L)) // Some(42.0)
myStringThing.myRelatedValue("someString") // error: could not find implicit value
Note that e.g. myBoolThing.myRelatedValue(MyBoolean(true)) will yield a type Option[U]. However, since myRelatedValue is parameterized, you can help the compiler and invoke it as myBoolThing.myRelatedValue[String](MyBoolean(true)), in which case you will obtain an Option[String]. If you try something other than String for MyBoolean, you will get an error.
Suppose that I have the following trait that defines an interface and takes a couple of type parameters...
trait Foo[A, B] {
// implementation details not important
}
I want to use the companion object as a factory for concrete implementations of the trait. I also want to force users to use the Foo interface instead of sub-classing So I hide the concrete implementations in the companion object like so:
object Foo {
def apply[A, B](thing: Thing): Foo[A, B] = {
???
}
private case class FooImpl[A1, B1](thing: Thing) extends Foo[A1, B1]
private case class AnotherFooImpl[A2, B1](thing: Thing) extends Foo[A2, B1]
}
I want to be able to use the factory as follows:
val foo = Foo[A1, B1](thing) // should be an instance of FooImpl
val anotherFoo = Foo[A2, B1](thing) // should be an instance of AnotherFooImpl
How do I implement the apply method to make this happen? This SO post seems close to the mark.
How about:
trait Foo[A, B]
trait Factory[A, B] {
def make(thing: Thing): Foo[A, B]
}
class Thing
object Foo {
def apply[A, B](thing: Thing)(implicit ev: Factory[A, B]) = ev.make(thing)
private case class FooImpl[A, B](thing: Thing) extends Foo[A, B]
private case class AnotherFooImpl[A, B](thing: Thing) extends Foo[A, B]
implicit val fooImplFactory: Factory[Int, String] = new Factory[Int, String] {
override def make(thing: Thing): Foo[Int, String] = new FooImpl[Int, String](thing)
}
implicit val anotherFooImplFactory: Factory[String, String] = new Factory[String, String] {
override def make(thing: Thing): Foo[String, String] = new AnotherFooImpl[String, String](thing)
}
And now:
def main(args: Array[String]): Unit = {
import Foo._
val fooImpl = Foo[Int, String](new Thing)
val anotherFooImpl = Foo[String, String](new Thing)
println(fooImpl)
println(anotherFooImpl)
}
Yields:
FooImpl(testing.X$Thing#4678c730)
AnotherFooImpl(testing.X$Thing#c038203)
Using TypeTags (to overcome erasure of type parameters), we can call the respective hidden implementations based on the type parameters passed in to the apply method like below. It correctly instantiates the respective implementations but the type information for Foo is lost, in fact its coming some garbage like _202 etc? I don't know why that is happening and how to retain the correct types for Foo. Maybe someone can throw light on this.
trait Foo[A,B]
object Foo {
def apply[A: TypeTag, B: TypeTag](thing: Thing) =
if(typeTag[A] == typeTag[Int])
FooImpl(thing)
else if(typeTag[A] == typeTag[String])
AnotherFooImpl(thing)
else
new Foo[Double,Double] {}
private case class FooImpl(thing: Thing) extends Foo[Int, String]
private case class AnotherFooImpl(thing: Thing) extends Foo[String, String]
}
Foo[Int,String](new Thing) // Foo[_202, _203] = FooImpl($sess.cmd123$Thing#50350b75)
The actual types for _203 and _203 are: ???
// type _203 >: String with _201, type _202 >: Int with _200
Foo[String,String](new Thing) //Foo[_202, _203] = AnotherFooImpl($sess.cmd123$Thing#51d80d6)
I have a type hierarchy and want to ask a lookup method for an implementation. I am having trouble doing this without resorting to an asInstanceOf call.
So giving a simple type hierarchy like so
trait Vehicle
trait Flying extends Vehicle
class Plane extends Flying
trait Driving extends Vehicle
class Car extends Driving
trait Swimming extends Vehicle
class Boat extends Swimming
my lookup method is like this
def findVehicle[V <: Vehicle](implicit tag: TypeTag[V]): Option[V] = {
val v = tag.tpe match {
case t if t =:= typeOf[Flying] => Some(new Plane)
case t if t =:= typeOf[Driving] => Some(new Car)
case t if t =:= typeOf[Swimming] => Some(new Boat)
case _ => None
}
v.map(_.asInstanceOf[V])
}
with a lookup like so
println(findVehicle[Flying]) // Some(Plane#5b7fd935)
Is it possible to achieve a lookup like this without the asInstanceOf at the end?
You can avoid using TypeTag and use a type class instead.
trait Builder[+T] {
def build: Option[T]
}
implicit val flyingBuilder = new Builder[Flying] {
def build = Some(new Plane)
}
implicit val drivingBuilder = new Builder[Driving] {
def build = Some(new Car)
}
implicit val swimmingBuilder = new Builder[Swimming] {
def build = Some(new Boat)
}
implicit val anyBuilder = new Builder[Nothing] {
def build = None
}
def findVehicle[V <: Vehicle](implicit b: Builder[V]): Option[V] = b.build
No reflection involved and it's definitely more idiomatic.
Note how defining a Builder[Nothing] reproduces the same behavior you achieved by returning a None. This is not necessarily a good idea, as you're now forced to check whether the method was able to produce a value or not.
I would rather prefer a compile-time error if it's impossible to build the instance of the desired type, and you achieve it by directly returning T as opposed to Option[T] from build (and of course getting rid of the Nothing case).
When I compile following code with Scala 2.11.1 and Akka 2.3.4, I get Error:
Error:(24, 35) Parameter type in structural refinement may not refer to an abstract type defined outside that refinement
def behavior(list: List[T], ele: T):Receive={
^
Error:(24, 35) Parameter type in structural refinement may not refer to a type member of that refinement
def behavior(list: List[T], ele: T):Receive={
^
The code:
package exp
import akka.actor.{ActorSystem, Props, Actor}
trait myTrait[T <: myTrait[T]]{
def computeDistance(that: T): Double
}
class otherClass[T <: myTrait[T]](){
def func(data: List[T]):List[String]={
val system = ActorSystem()
system.actorOf(Props(new Actor{
case object START
case class Job(e1: T, e2: T)
def receive = {
case START =>
context become behavior(data.tail, data.head)
}
def behavior(list: List[T], ele: T):Receive={
case _ =>
sender ! Job(list.head, ele)
context become behavior(list.tail, ele)
}
}))
List.empty[String]
}
}
object Test {
class myClass(val x:Double) extends myTrait[myClass]{
override def computeDistance(that: myClass): Double = this.x - that.x
}
def main(args: Array[String]){
val fc = new otherClass[myClass]()
fc.func(List.empty[myClass])
}
}
The reason I define myTrait as myTrait[T <: myTrait[T]] is that I want subclass of myTrait can override computeDistance method with subclass argument. Follows this post.
otherClass provides a method func to produce some computation on a list of myClass instances. (Actually the above code is an abstraction of my need.)
Adding generic type on behavior can eliminate the above error, but it will cause new error.
def behavior[S](list: List[T], ele: S):Receive={
case _ =>
sender ! Job(list.head, ele)
context become behavior(list.tail, ele)
}
Then, in Job(list.head, ele), the ele of type S mismatches type T.
How to solve this problem?
Adding a type annotation to widen the type to plain Actor fixes the problem:
system.actorOf(Props(new Actor{
// ...
}: Actor))
class otherClass[T <: myTrait[T]](){
def func(data: List[T]):List[String]={
val system = ActorSystem()
class FooActor extends Actor {
case object START
case class Job(e1: T, e2: T)
def receive = {
case START =>
context become behavior(data.tail, data.head)
}
def behavior(list: List[T], ele: T):Receive={
case _ =>
sender ! Job(list.head, ele)
context become behavior(list.tail, ele)
}
}
system.actorOf(Props(new FooActor))
List.empty[String]
}
}
Just get ride of that structural actor creation by wrapping it as a new class
Add generic type for behavior method:
def behavior[T](map: HashMap[Int, List[T]], list: List[(T, Int)], ele: T):Receive={
case _ =>
}
https://issues.scala-lang.org/browse/SI-1906
you can't use the abstract type defined outside the structural type due to missing type information at runtime.
Check this answer, Using Scala structural types with abstract types.