The below code defines a List type and two implementation of case classes one representing empty list and Cons to create actual list
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](head: A, tail: List[A]) extends List[A]
object List {
...
def apply[A](as: A*): List[A] = {
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
}
val example = Cons(1, Cons(2, Cons(3, Nil)))
}
is my assumption correct that the list is created by the recursive apply method above when the val example = ... is executed.
if that is so. the signature for creating Cons is Cons(head, tail) where as the signature of the apply is incompatible variad def apply[A](as: A*): List[A], how does scala actually infers the val example to be List(1, 2, 3, Nil)
When you say
class Foo(s: String, i: Int)
in scala, it generates a class Foo with a constructor taking a String and an Int.
It's pretty much the same as a java class declared as
public class Foo {
public Foo(String s, int i) {
...
}
}
With case classes, the compiler provides extra-goodies such as a companion object with a default apply method taking the same parameters as the constructor.
So that's why you can directly call Cons(head, tail), without explicitly define a constructor.
Related
What is the order of compilation for the following code? It seems to have a cyclic reference. The trait List requires the object List to be defined, the object List requires case object Cons to be defined which in turn requires the trait List to be defined. However, this still compiles.
case class Cons[+A](head: A, tail: List[A]) extends List[A]
trait List[+A] {
def create[A](x: A, t: List[A]) = List.make(x, t)
}
object List {
def make[A](x: A, t: List[A]): List[A] = Cons(x, t)
}
I had a problem coding a function called head, that basically replace the head elements with another for the List invoking it:
List(1,2,3,4).head(4) // List(4,2,3,4)
The code is obviously useless, I was just trying to have fun with Scala. This is the code:
sealed trait List[+A]{
def tail():List[A]
def head[A](x:A):List[A]
}
object Nil extends List[Nothing]{
def tail() = throw new Exception("Nil couldn't has tail")
def head[A](x:A): List[A] = List(x)
}
case class Cons[+A](x :A, xs: List[A]) extends List[A]{
def tail():List[A] = xs
def head[A](a:A): List[A] = Cons(a,xs)
}
object List{
def apply[A](as:A*):List[A] = {
if (as.isEmpty) Nil
else Cons(as.head,apply(as.tail: _*))
}
}
Cons(1,Cons(2,Nil)) == List(1,2)
Cons(1,Cons(2,Cons(3,Cons(4,Nil)))).tail()
List(1,2,3,4,5,6,7).tail()
List(1,2,3,4).head(4)
It doesn't not compile and I have this error:
Error:(11, 39) type mismatch;
found : A$A318.this.List[A(in class Cons)]
required: A$A318.this.List[A(in method head)]
def head[A](a:A): List[A] = Cons(a,xs)
Could you explain why, please?
Regards.
Your problem is that your head method is taking another type A, therefore inside that scope the compiler takes those As as different, i.e., the A defined in the trait is shadowed by the A in head[A].
Also, your head method is taking a covariant element of type A in a contravariant position, so you can't define head as such.
What you can do is defining your head as:
def head[B >: A](x: B): List[B]
Hence, you get:
object S {
sealed trait List[+A] {
def tail(): List[A]
def head[B >: A](x: B): List[B]
}
case object Nil extends List[Nothing] {
def tail() = throw new Exception("Nil doesn't have a tail")
def head[B >: Nothing](x: B): List[B] = Cons(x, Nil)
}
case class Cons[+A](x: A, xs: List[A]) extends List[A] {
def tail(): List[A] = xs
def head[B >: A](a: B): List[B] = Cons(a, xs)
}
object List {
def apply[A](as: A*): List[A] = {
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
}
}
}
Testing this on the REPL:
scala> :load test.scala
Loading test.scala...
defined object S
scala> import S._
import S._
scala> Nil.head(1)
res0: S.List[Int] = Cons(1,Nil)
scala> Cons(1, Nil).head(4)
res1: S.List[Int] = Cons(4,Nil)
You method head does not need a type parameter, since you already have one defined in the class definition and that is exactly the type you want head to receive (if you want head to create a list of the same type of the original).
The error you get is because A is two different types in the context of the method head (the one from the method and the other from the class).
The definition for head should be:
def head(x:A):List[A]
I am reading Functional Programming in Scala from Manning, authored by Paul Chiusano and Runar Bjarnason. In its 3rd chapter, there is a code to create a List and there are assignments to implement various methods of the list. Following is partial implementation of the my List
package src.Cons
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](h:A, t:List[A]) extends List[A]
object List {
//my issue is I do not want to pass a list to sum but want to use objectName.sum notation
def sum(ints:List[Int]):Int = ints match {
case Nil => 0
case Cons(x,xs) => x+sum(xs)
}
}
Question - How can I create my list such that I can call l.sum instead of List.sum(l)?
You can "PmL", as #Gabriele Petronella has suggested, or you can move the sum() method to the Cons class, as #DeadNight wrote, but before either of those can work you have to resolve the current conflict between your List object and your List trait.
The sum() in your List object can only sum a List[Int] but your class definitions use a more generic type member and, as such, you can't use + because the compiler doesn't know how to add two A types.
If you want to restrict your List to only handling numeric types then this will work.
case class Cons[A: Numeric](h:A, t:List[A]) extends List[A] {
def sum: A = List.sum(this)
}
object List {
def sum[A](ints:List[A])(implicit ev: Numeric[A]):A = ints match {
case Nil => ev.zero
case Cons(x,xs) => ev.plus(x, sum(xs))
}
}
val x = Cons(4, Cons(2, Nil))
x.sum // res0: Int = 6
Making sum a member
The problem is, you don't know how to sum the List[A] for every type A, only a List[Int]. If there was a way to allow calls when A is an Int...
Let's take a look at the standard library for that. We're interested in Option#flatten method because:
val o1 = Option(Option(3)).flatten // compiles
val o2 = Option(4).flatten // does not compile
Notice the weird implicit ev: <:<[A, Option[B]]. This is the key here - it's a thing that compiler provides for you, but only if it is known at compile time, that your Option[A] is a subtype of Option[Option[B]] for some type B. This is the trick that we can use.
sealed trait List[+A] {
def sum(implicit ev: A <:< Int): Int = this match {
case Nil => 0
case Cons(x, xs) => x + xs.sum // <- here x is magically converted to Int, so we can use plus
}
}
case object Nil extends List[Nothing]
case class Cons[+A](h:A, t:List[A]) extends List[A]
println(Cons(4, Cons(38, Nil)).sum) // 42
ScalaFiddle
Notice that you can write <:<[A, B] as A <:< B.
NB: there's also =:=[A, B] type, for when your A is exactly Int - you can use either of those
Doing better?
Actually, std library has sum method and it's type is even weirder:
def sum(implicit ev: Numeric[A]). Doing so allows it to work on any number-like type like Double and Int, and has the operations for comparison, subtraction, multiplication, etc. So you can make it even more generic. I suggest you do it after reading a chapter about Monoids, tho :)
You can use the so-called "Pimp my Library" pattern.
Define an implicit class ListOps
implicit class ListOps[+A](list: List[A]) {
def sum = List.sum(this)
}
and now you can call list.sum. The implicit conversion will be triggered and the compiler will interpret it as ListOps(list).sum.
Move the definition of sum inside the definition of List trait
You can leave the concrete definitions to Nil & Cons
package src.Cons
sealed trait List[+A] {
def sum: Int
}
case object Nil extends List[Nothing] {
val sum: Int = 0
}
case class Cons[+A](h:A, t:List[A]) extends List[A] {
def sum: Int = h + t.sum
}
Below code causes compiler error :
Multiple markers at this line
- not found: type A
- not found: type A
at line def headOption :
object LazyList {
println("Welcome to the Scala worksheet")
sealed trait Stream[+A]
case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]
object Stream {
def cons[A](hd: => A, tl: => Stream[A]): Stream[A] = {
lazy val head = hd
lazy val tail = tl
Cons(() => head, () => tail)
}
def empty[A]: Stream[A] = Empty
def apply[A](as: A*): Stream[A] =
if (as.isEmpty) empty else cons(as.head, apply(as.tail: _*))
def headOption : Option[A] = this match {
case Empty => None
case Cons(h, t) => Some(h())
}
}
}
But I think the function is defined correctly ? Since A is a generic type it should not cause this compiler error ?
You need to move def headOption into the Stream trait. Currently, it's in the Stream companion object. An object doesn't have a type parameter, because an object is just one determinate thing in memory, with a completely specific type. The Stream trait describes many possible Stream objects, each of which can have a different type, corresponding to the type filled in for A when the object is created.
Notice that you intend your this in headOption to refer to a specific Stream, not to the Stream companion object.
headOption should be defined as polymorphic function like what you did for empty and apply. So you need to add type annotation to your function as follow:
def headOption[A]: Option[A] = ...
However generally headOption should be moved to the trait and defining it in companion object is wrong. If you move it to the trait, the trait is typed so you do not need to add type annotation to the function and your current implementation will work.
In below apply method what is "as: A*" ?
List(1, 2, 3) constucts a List of type : Cons(1,Cons(2,Cons(3,Nil)))
From reading the method code it appears that its a kind of syntax sugar for
multiple type parameters of same type ?
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](head: A, tail: List[A]) extends List[A]
object List {
def apply[A](as: A*): List[A] =
if (as.isEmpty) Nil
else {
Cons(as.head, apply(as.tail: _*))
}
}
If this is true should'nt this be also valid :
object List {
def apply[A](asHead: A , asTail : A): List[A] =
if (asHead.isEmpty) Nil
else {
Cons(asHead.head, apply(asTail.tail: _*))
}
No, there's a difference here.
When you declare asHead: A, asTail: A, asHead and asTail have obviously the same type. However, in the original code that uses as: A*, the type of as.head is A, and the type of as.tail is Seq[A] -- as can be deduced from the types of head and tail. Therefore, your code is not the same thing.
The declaration A* stands for "a variable number of parameters of type A", usually known as vararg. This is similar to a Seq[A], but differ in the invocation:
apply(1, 2, 3) // A*
apply(Seq(1, 2, 3)) // Seq[A]
The counterpart to that is the : _* declaration when calling a vararg method with a sequence:
apply(Seq(1, 2, 3): _*) // A*
apply(Seq(1, 2, 3)) // Seq[A]