In scala source, I found:
case object Nil extends List[Nothing] {
...
}
I can't understand why it is declared as case object rather than object?
I found this question [ Difference between case object and object ] is useful, and I guess this reason is the key:
default implementations of serialization
because we often send list of data to another actor, so Nil must be serializable, right?
With the provided answers(thanks), I try to write some code to verify it:
trait MyList[+T]
object MyNil extends MyList[Nothing]
val list: MyList[String] = MyNil
list match {
case MyNil => println("### is nil")
case _ => println("### other list")
}
You can see MyNil is not case object, but I can still use it in pattern matching. Here is the output:
### is nil
Do I misunderstand something?
In general for immutable data, the question should never be "why is this a case object (or class)" but rather "Can I make this a case object?". With a few small exceptions (mostly due to inheritance), data elements in Scala should be immutable, and should be implemented via case classes/objects. Given that, implementing Nil and :: as a case object and case class (respectively) is just standard practice, for which there is no downside.
As mentioned in the comments of that linked question, one thing you get is a prettier .toString result
scala> MyNil.toString
res0: String = MyNil$#51aa572b
scala> case object MyNil2 extends MyList[Nothing]
defined module MyNil2
scala> MyNil2.toString
res2: String = MyNil2
scala> Nil.toString
res1: String = List()
Related
I'm learning Scala and in a book that I'm reading (Functional Programming in Scala) I came across an example of a custom List implementation in Scala which goes like this:
sealed trait MyList[+A]
case object MyNil 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) MyNil
else Cons(as.head, apply(as. tail: _*))
}
I would like to extend MyList to add the following functionality:
add a tail method that returns all elements of a MyList instance without the first one, e.g. val x = MyList(1,2,3); x.tail == MyList(2,3).
Add a sum method that is only applicable when MyList contains Ints (or even better for all numeric types). So e.g. val x = MyList(1,2,3); x.sum == 6
The idea above 2 questions is to understand: (1) how to interact with the instance of my class and (2) how to use polymorphism in a situation like this. After some searching around, I'm not even sure how to begin with these problems, which is why I'm asking this question.
Any tips would be appreciated. Many thanks!
UPDATE:
A few updates:
First, I'd like to point out that the solution to the programming challenges in the Functional Programming course that I mentioned earlier can be found here, however, I'm looking for something a little different than what the author is asking for.
I've managed to find an answer to my first question "how can I use tail on my instance itself, e.g. MyList(1,2,3).tail?". To solve this, I had to modify the original trait in the following manner:
sealed trait MyList[+A] {
def tail: MyList[A] = MyList.tail(this)
}
I'm not sure if this is the best way of doing what I want to do, but it works. If anyone has better suggestions, please let me know.
The second part is harder. I wanted to add the following inside the same trait:
def sum[Int]: MyList[Int] = MyList.sum(this)
But IntelliJ is complaining about the type of this which is A and I need to apply this conditionally on this being of type Int.
Another alternative is to do the following:
def sum: Int = this match {
case x: MyList[Int] => MyList.sum(x)
}
But what if we want to create another implementation for String that will also return a String? This cannot be the right solution and I haven't found one yet. Please help :)
.tail
I note that your Cons class already has a public tail member. I'd be tempted to start there and just make it universal...
sealed trait MyList[+A] {
def tail: MyList[A]
}
...and add the MyNil implementation.
case object MyNil extends MyList[Nothing] {
def tail: MyList[Nothing] =
throw new java.lang.UnsupportedOperationException("tail of empty list")
}
This is how the standard library List handles the tail of an empty list. Another, perhaps gentler, option would be to return this so that the tail of an empty MyList is just the empty MyList.
Leaving class Cons and object MyList unchanged, we get the expected results.
MyList('s','h','o','w').tail //res0: MyList[Char] = Cons(h,Cons(o,Cons(w,MyNil)))
MyList(9).tail.tail //java.lang.Unsupported...
.sum
This is a bit trickier. We want each .sum invocation to compile only if the elements are of a sum-able type, such as Int. The Scala way to achieve this to require that the call site provide implicit "evidence" that the element type is acceptable.
sealed trait MyList[+A] {
def sum(implicit ev : A =:= Int) : Int //can sum only if A is Int
}
Alas, this won't compile because MyList is covariant on A, but being the type of a passed parameter puts A in a contra-variant position.
Error: covariant type A occurs in invariant position in type A =:= Int of value ev
Fortunately there's a fix for that: use a different type parameter, related to A but not restricted to its covariant relationship.
sealed trait MyList[+A] {
def sum[B >: A](implicit ev : B =:= Int) : Int = 0 //default behavior
}
case object MyNil extends MyList[Nothing] { ... //unchanged
case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A] {
override def sum[B >: A](implicit ev :B =:= Int) : Int = head + tail.sum[B]
}
object MyList { ... //unchanged
MyList(23,31,12).sum //res0: Int = 66
MyList("as","is").sum //won't compile
Numeric[A]
Well that works for Int, but it would be a pain to have to do the same for every sum-able type. Fortunately the standard library offers the Numeric typeclass which provides some basic values (zero and one) and operations (plus(), minus(), times(), etc.) for all the numeric types under its umbrella (Short, Long, Float, etc.).
So, putting it all together:
sealed trait MyList[+A] {
val tail: MyList[A]
def sum[B >: A](implicit ev : Numeric[B]): B = ev.zero
}
case object MyNil extends MyList[Nothing] {
val tail: MyList[Nothing] = this
}
case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A] {
override def sum[B >: A](implicit ev : Numeric[B]): B = ev.plus(head, tail.sum[B])
}
object MyList {
def apply[A](as: A*): MyList[A] =
if (as.isEmpty) MyNil else Cons(as.head, apply(as.tail: _*))
}
I am redoing a previous question to make it more focused and clear.
I have the following situation:
case class Hello(u:String) extends A
case class Goodbye extends A
case class GoodbyeAck(u:String) extends B
val myProcessor: Flow[A, B, Unit] = Flow[A]
.map {
case Hello(u:String) => // I want to cache u (the username)
case Goodbye => GoodbyeAck(u)
}
So, at the beginning of the stream I get a Hello(username) and at the end of the stream, I receive a Goodbye. I want to reply with Goodbye(username).
How should I cache the username (u) so it will be available for the lifetime of the stream, so I can have it when it's time to say good bye? Is there some context I can leverage? Or do I need to do it outside the framework?
This seems like one of the few examples where mutable state is necessary. Though not "purely functional", the state can be isolated in such a way as to not allow outside influence but still provide the functionality you are looking for.
Note: your question does not specify what B value should be returned by the map in the case of receiving a Hello. A value of type B is necessary since simply caching is of type Unit. Therefore a flatMapConcat is used to conform to the requirements of the Flow in the question, which is to return nothing when a Hello is received:
def cacheProcessor(defaultString : String = "") : Flow[A, B, Unit] = {
var userCache : String = defaultString
Flow[A] flatMapConcat {
case Hello(u : String) => {
userCache = u
Source.empty[B]
}
case Goodbye => Source.single[B](GoodbyeAck(userCache))
}
}//end def cacheProcessor
Although userCache is mutable state it cannot be accessed outside of the returned Flow.flatMapConcat. One important point is that cacheProcessor has to be a def so that there is a unique userCache for each Flow.
As I am working a design model, I am torn between two different methods of indicating a parameter of type List must be nonEmpty. I began by using List[Int] with an accompanying require statement to verify the List is nonEmpty.
case class A(name: String, favoriteNumbers: List[Int]) {
require(favoriteNumbers.nonEmpty, "favoriteNumbers must not be empty")
}
I then needed to make the list optional. If the List is provided, it must be nonEmpty. I'm using using Option[List[Int]] with an accompanying require statement to verify, if the Option is nonEmpty, the list must also be nonEmpty.
case class B(name: String, favoriteNumbers: Option[List[Int]]) {
require(
favoriteNumbers.isEmpty || favoriateNumbers.get.nonEmpty
, "when defined, favoriteNumbers.get must be nonEmpty"
)
}
However, I need to use this non-empty List all over the system I am modeling. This means that my code has these same require statements duplicated everywhere. Is there a (non-ScalaZ) way to have a new type, say NeList, which is defined and behaves identically to List, with the only change being an exception is thrown when NeList attempts to be instantiated with no elements?
I tried to Google for this and couldn't find a set of search terms to hone on this area. I either got really simple List how-tos, or all sorts of references to ScalaZ's NEL (Non Empty List). So, if there is a link out there that would help with this, I would love to see it.
If you
def foo[A](x: ::[A]) = "List has length "+x.length
then you insist that the list be nonempty. But of course your lists are all typed as List, so you need a helper method to give you a nonempty list:
implicit class NonEmptyList[A](private val underlying: List[A]) {
def ifNonEmpty[B](f: ::[A] => B): Option[B] = {
underlying match {
case x: ::[A #unchecked] => Some(f(x))
case _ => None
}
}
}
Now you can safely apply the operation to get an Option out. (You could also run side-effecting functions in a foreach-like method.)
Now, this is rather non-idiomatic Scala. But it is safe at compile time (the #unchecked notwithstanding--Scala's compiler isn't quite smart enough to realize that the type parameter hasn't changed).
You could implement a non-empty list yourself with implicit conversions between List[A] and Nel[A]:
case class Nel[A](val head: A, val tail: List[A] = Nil)
implicit def list2Nel[A](list: List[A]): Nel[A] = {
require(!list.isEmpty)
Nel(list.head, list.tail)
}
implicit def nel2List[A](nel: Nel[A]): List[A] = nel.head :: nel.tail
Then you can define your functions where this is needed such that they take a Nel[A] as a parameter:
def f(l: Option[Nel[String]]) = { ... }
And call them with normal lists (assuming the implicit defs are in scope):
f(Some(List("hello", "world")) // works
f(Some(Nil)) // throws IllegalArgumentException
f(None) // works
EDIT: It should be noted that this does not provide compile time guarantees that the List[A] passed in will not be empty. If that's what you want, then get rid of the implicit def list2Nel and require clients of your function to pass in an Nel[A] explicitly, thus guaranteeing at compile time that the list is not empty.
Also, this is a very basic NonEmptyList implementation. A more complete solution is found in scalaz (granted it was specifically requested in the question that scalaz not be used): https://github.com/scalaz/scalaz/blob/series/7.2.x/core/src/main/scala/scalaz/NonEmptyList.scala
Can anybody help me with a begginer problem? I wrote a function to check Declaration, but I Cannot add element Symbol() to List env. I dont understand what the matter is.
def check_decl(env: List[Symbol])(decl: Declare): Unit = decl match{
case VarDec(varName, varType)=>{
lookupForInsert(env)(varName.toString()) match {
case Some(_) => throw Redeclared(Variable,varName.toString())
case None => {
varType match{
case ArrayType(lower, upper, element)=>{
if(lower > upper) throw SizeIsNotNegative(decl)
}
case IntType=>
case RealType=>
case BoolType=>
case StringType=>
}
}
}
}
In Scala you have immutable Lists by default, and you can't "add" something to it. All operations that would modify a list (or more similar, a stack) in Java will result in a new, modified list in Scala. Alternatively you have mutable classes like ListBuffer, which behave more like Java lists.
How does this work in Scala?
val something = List(1,2,3)
List is abstract, and you can't construct it by invoking new List(), but List(1,2,3) works just fine.
Because it is a call to the apply method of the list companion object. In scala, a method called apply can be called with the method name ommitted (i.e. just with parens). It is by this mechanism that sequence and Map access works
Hence, List(1, 2, 3) is in fact:
List.apply(1, 2, 3)
So this is then a call to the apply method of List's companion object, the implementation of which is:
override def apply[A](xs: A*): List[A] = xs.toList
So you can see that apply is a method which takes a repeating parameter (a sequence), and calls the toList method on this sequence. This toList on Seq is inherited from TraversableOnce:
def toList: List[A] = new ListBuffer[A] ++= seq toList
So you can see that this creates a list via a ListBuffer and the ++= method:
override def ++=(xs: TraversableOnce[A]): this.type =
if (xs eq this) ++= (this take size) else super.++=(xs)
This ultimately gets its implementation of ++= from Growable:
def ++=(xs: TraversableOnce[A]): this.type = { xs.seq foreach += ; this }
Invoking new List() does not work because List is a trait (and a sealed one at that) - you would have had to apply implementations for the abstract methods. The fact that it is sealed means that it can only be implemented by a class in the same source file.
List(1,2,3) is "magic syntax" to call the apply method in the companion object of trait or class List.