I am trying to understand the scala implementation of List pattern match and
have question on how the first case statement(:: which splits the list by 1st, 2nd and rest) is implemented under the hood in List.scala
list match{
case h1 :: h2 :: rest => println(h2)
case _ =>
}
I was trying to understand by comparing this against Option Monad and below is my understanding on how Option case pattern match works:
When i wrap an object inside Option below is called from Option.scala and it gives me either Some or None
def apply[A](x: A): Option[A] = if (x == null) None else Some(x)
then When i pattern match on Option, one of the below is matched and I can call methods of the same
final case class Some[+A](x: A) extends Option[A] {
def isEmpty = false
def get = x
}
case object None extends Option[Nothing] {
def isEmpty = true
def get = throw new NoSuchElementException("None.get")
}
When i look for similar implementation in List.scala, i could find case implementation for Nil(case object Nil extends List[Nothing]) which will be called for case Nil pattern match
but i am trying to understand how the statement case h1 :: h2 :: rest is matched with List.scala case implementation
Thanks for taking a look and please add comment if my question is missing requirded information to look into it.
Related
I have recently started reading the book Functional Programming in Scala by Paul Chiusano and Rúnar Bjarnason, as a means to learn FP. I want to learn it because it will open my head a bit, twist my way of thinking and also hopefully make me a better programmer overall, or so I hope.
In their book, Chp. 3, they define a basic singly-linked-list type as follows:
package fpinscala.datastructures
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 sum(ints: List[Int]): Int = ints match {
case Nil => 0
case Cons(x,xs) => x + sum(xs)
}
def product(ds: List[Double]): Double = ds match {
case Nil => 1.0
case Cons(0.0, _) => 0.0
case Cons(x,xs) => x * product(xs)
}
def apply[A](as: A*): List[A] =
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
}
I'm now working on implementing the tail method, which shall work similarly to the tail method defined in Scala libraries. I guess that the idea here, is to define a tail method inside the List object, what they call a companion method, and then call it normally in another file (like a Main file).
So far, I have this:
def tail[A](ls: List[A]): List[A] = ls match {
case Nil => Nil
case Cons(x,xs) => xs
}
Then I created a Main file in another folder:
package fpinscala.datastructures
object Main {
def main(args:Array[String]):Unit = {
println("Hello, Scala !! ")
val example = Cons(1, Cons(2, Cons(3, Nil)))
val example2 = List(1,2,3)
val example3 = Nil
val total = List.tail(example)
val total2 = List.tail(example3)
println(total2)
}
}
This works and gives me:
Hello, Scala !!
Cons(2,Cons(3,Nil))
My question is:
Is this the correct way to write the tail method, possibly, as the authors intended? And is this package structure correct? Because it feels very wrong to me, although I just followed the authors package.
I also don't know if I should have used a specific type instead of writing a polymorphic method (is this the name?)...
Bear with me, for I am a newbie in the art of FP.
In the default Scala list implementation, attempting to take the tail of an empty list would throw an UnsupportedOperationException. So you might want something more like
def tail[A](ls: List[A]): List[A] = ls match {
case Nil => throw new UnsupportedOperationException()
case Cons(x,xs) => xs
}
Also, qantik's answer where he suggests using the :: operator would work with Scala's default list implementation, but since there isn't a :: method defined on this custom list implementation it won't work.
Finally, you may want to consider defining tail so that instead of doing
val list = List(1, 2, 3)
val restOfList = tail(list).
you could instead do
val list = List(1, 2, 3)
val restOfList = list.tail
That would require defining the method on the List trait, as opposed to in the List object.
Looks okay. What about?
def tail[A](xs: List[A]): List[A] = xs match {
case Nil => Nil
case head :: xxs => xxs
}
I've been parsing a proprietary file format that has sections and each section has a number of records. The sections can be in any order and the records can be in any order. The order is not significant. While sections should not be duplicated, I can't guarantee that.
I've been using parboiled2 to generate the AST using a format like the following:
oneOrMore( Section1 | Section2 | Section3 )
Where every section generates a case class. They don't inherit from anything resulting in Seq[Any]
These section case classes also contain a Seq[T] of records specific to the section type.
I would like to transform the Seq[Any] into a
case class (section1:Seq[T1], section2:Seq[T2], section3:Seq[T3] )
Does someone have a clever and easy to read technique for that or should I make some mutable collections and use a foreach with a match?
I always feel like I am missing some Scala magic when I fall back to a foreach with vars.
EDIT 1:
It was brought up that I should extend a common base class, it is true that I could. But I don't see what that changes about the solution if I still have to use match to identify the type. I want to separate out the different case class types, for instance below I want to collect all the B's, C's, E's, and F's together into a Seq[B], Seq[C], Seq[E], and Seq[F]
class A()
case class B(v:Int) extends A
case class C(v:String) extends A
case class E(v:Int)
case class F(v:String)
val a:Seq[A] = B(1) :: C("2") :: Nil
val d:Seq[Any] = E(3) :: F("4") :: Nil
a.head match {
case B(v) => v should equal (1)
case _ => fail()
}
a.last match {
case C(v) => v should equal ("2")
case _ => fail()
}
d.head match {
case E(v) => v should equal (3)
case _ => fail()
}
d.last match {
case F(v) => v should equal ("4")
case _ => fail()
}
EDIT 2: Folding solution
case class E(v:Int)
case class F(v:String)
val d:Seq[Any] = E(3) :: F("4") :: Nil
val Ts = d.foldLeft((Seq[E](), Seq[F]()))(
(c,r) => r match {
case e:E => c.copy(_1=c._1 :+ e)
case e:F => c.copy(_2=c._2 :+ e)
}
)
Ts should equal ( (E(3) :: Nil, F("4") :: Nil) )
EDIT 3: Exhaustivity
sealed trait A //sealed is important
case class E(v:Int) extends A
case class F(v:String) extends A
val d:Seq[Any] = E(3) :: F("4") :: Nil
val Ts = d.foldLeft((Seq[E](), Seq[F]()))(
(c,r) => r match {
case e:E => c.copy(_1=c._1 :+ e)
case e:F => c.copy(_2=c._2 :+ e)
}
)
Ts should equal ( (E(3) :: Nil, F("4") :: Nil) )
While this could be done with shapeless to make a solution that is more terse (As Travis pointed out) I chose to go with a pure Scala solution based on Travis' feedback.
Here is an example of using foldLeft to manipulate a tuple housing strongly typed Seq[]. Unfortunately every type that is possible requires a case in the match which can become tedious if there are many types.
Also note, that if the base class is sealed, then the match will give an exhaustivity warning in the event a type was missed making this operation type safe.
sealed trait A //sealed is important
case class E(v:Int) extends A
case class F(v:String) extends A
val d:Seq[A] = E(3) :: F("4") :: Nil
val Ts = d.foldLeft((Seq[E](), Seq[F]()))(
(c,r) => r match {
case e:E => c.copy(_1=c._1 :+ e)
case e:F => c.copy(_2=c._2 :+ e)
}
)
Ts should equal ( (E(3) :: Nil, F("4") :: Nil) )
The context of my question is similar to some others asked in the forum, but I cannot find an exact match and it still remains a mystery to me after viewing those answers. So I appreciate it if someone can help. The context of my question is to match a singleton class object using a pattern match.
For example, if I am implementing a list structure of my own, like this
// An implementation of list
trait AList[+T] // covariant
case class Cons[+T](val head: T, val tail: AList[T]) extends AList[T]
case object Empty extends AList[Nothing] // singleton object
// an instance of implemented list
val xs = Cons(1, Cons(2, Cons(3, Empty)))
// pattern matching in a method - IT WORKS!
def foo[T](xs: AList[T]) = xs match {
case Empty => "empty"
case Cons(x, xss) => s"[$x...]"
}
println(foo(xs)) // => [1...]
// pattern matching outside - IT RAISES ERROR:
// pattern type is incompatible with expected type;
// found : Empty.type
// required: Cons[Nothing]
val r: String = xs match {
case Empty => "EMPTY"
case Cons(x, xss) => s"[$x...]"
}
println(r) // does NOT compile
To me they look like the same "matching" on the same "objects", how come one worked and the other failed? I guess the error had something to do with the different of matching expr in and out of methods, but the message given by the compiler was quite misleading. Does it mean we need to explicitly cast xs like xs.asInstanceOf[AList[Int]] when "matching" outside?
Compiler tells you that type of xs is Cons and it can't be Empty, so your first case is pointless.
Try this:
val r: String = (xs: AList[Int]) match {
case Empty => "EMPTY"
case Cons(x, xss) => s"[$x...]"
}
Or this:
val ys: AList[Int] = xs
val r: String = ys match {
case Empty => "EMPTY"
case Cons(x, xss) => s"[$x...]"
}
In this case compiler don't knows that case Empty is pointless.
It's exactly what you are doing with def foo[T](xs: AList[T]) = .... You'd get the same compilation error with def foo[T](xs: Cons[T]) = ....
In this particular example valid and exhaustive match looks like this:
val r: String = xs match {
// case Empty => "EMPTY" // would never happened.
case Cons(x, xss) => s"[$x...]"
}
Addition: you should make your AList trait sealed:
sealed trait AList[+T]
It allows compiler to warn you on not exhaustive matches:
val r: String = (xs: AList[Int]) match {
case Cons(x, xss) => s"[$x...]"
}
<console>:25: warning: match may not be exhaustive.
It would fail on the following input: Empty
val r: String = (xs: AList[Int]) match {
^
The parameter of foo is a AList[T], so in the first case the matching is being done on a AList[T]. In the second case the matching is being done on a Cons[+T].
Basically matching is done on a object type, not on a object.
In attempting to do validation with application functors (Monad to catch multiple exceptions (not just fail on single)), I came across a hard limit in scalaz that disallows more than 14 functors, so a helpful comment from here (https://github.com/scalaz/scalaz/issues/504#issuecomment-23626237) navigated me to use HLists instead of applicative functors
Now it works perfectly fine (after having to manually put in this sequence file from here since its not in maven https://github.com/typelevel/shapeless-contrib/blob/master/scalaz/main/scala/sequence.scala?source=c)
My question is, and I know this is possible, how would you go about automatically instantiating the case class Foo(i:Int,s:String) without having to manually match the pattern with a case, only to just reapply the parameters again
Essentially I want to do something like this
case class Foo(i:Int,s:String)
implicit def TwoFoo = Iso.hlist(Foo.apply _, Foo.unapply _)
val someFoo = sequence(
1.successNel[Int] ::
"2".successNel[String] ::
HNil
).map { Foo.apply _} // Note this doesn't work
someFoo match {
case Success(a) => println(a)
case Failure(a) => {
println("failure")
println(a)
}
}
First for a minor point: the type parameter for successNel is the error type, not the success type, so it needs to be the same across all the arguments to sequence.
So we can write the following (assuming our errors are strings):
import shapeless._, contrib.scalaz._
import scalaz._, syntax.validation._
case class Foo(i: Int, s: String)
implicit val fooIso = Iso.hlist(Foo.apply _, Foo.unapply _)
val valHList = sequence(1.successNel[String] :: "2".successNel[String] :: HNil)
This gives us an Int :: String :: HNil inside a validation. Now we can use our isomorphism:
scala> valHList.map(fooIso.from)
res0: scalaz.Validation[scalaz.NonEmptyList[String],Foo] = Success(Foo(1,2))
No need to deconstruct the list and apply the Foo constructor manually.
I would like to convert a List[Box[T]] into a Box[List[T]].
I know that I could use foldRight, but I can't find an elegant way into doing so.
EDIT I would like to keep the properties of Box, that is to say, if there is any failure, return a Box with this failure.
If you only want to collect the "Full" values
I'm not sure why you'd want a Box[List[T]], because the empty list should suffice to signal the lack of any values. I'll assume that's good enough for you.
I don't have a copy of Lift handy, but I know that Box is inspired by Option and has a flatMap method, so:
Long form:
for {
box <- list
value <- box
} yield value
Shorter form:
list.flatMap(identity)
Shortest form:
list.flatten
If you want to collect the failures too:
Here's the mapSplit function I use for this kind of problem. You can easily adapt it to use Box instead of Either:
/**
* Splits the input list into a list of B's and a list of C's, depending on which type of value the mapper function returns.
*/
def mapSplit[A,B,C](in: Traversable[A])(mapper: (A) ⇒ Either[B,C]): (Seq[B], Seq[C]) = {
#tailrec
def mapSplit0(in: Traversable[A], bs: Vector[B], cs: Vector[C]): (Seq[B], Seq[C]) = {
in match {
case t if t.nonEmpty ⇒
val a = t.head
val as = t.tail
mapper(a) match {
case Left(b) ⇒ mapSplit0(as, bs :+ b, cs )
case Right(c) ⇒ mapSplit0(as, bs, cs :+ c)
}
case t ⇒
(bs, cs)
}
}
mapSplit0(in, Vector[B](), Vector[C]())
}
And when I just want to split something that's already a Seq[Either[A,B]], I use this:
/**
* Splits a List[Either[A,B]] into a List[A] from the lefts and a List[B] from the rights.
* A degenerate form of {#link #mapSplit}.
*/
def splitEither[A,B](in: Traversable[Either[A,B]]): (Seq[A], Seq[B]) = mapSplit(in)(identity)
It's really easier to do this with a tail-recursive function than with a fold:
final def flip[T](l: List[Option[T]], found: List[T] = Nil): Option[List[T]] = l match {
case Nil => if (found.isEmpty) None else Some(found.reverse)
case None :: rest => None
case Some(x) :: rest => flip(rest, x :: found)
}
This works as expected:
scala> flip(List(Some(3),Some(5),Some(2)))
res3: Option[List[Int]] = Some(List(3, 5, 2))
scala> flip(List(Some(1),None,Some(-1)))
res4: Option[List[Int]] = None
One can also do this with Iterator.iterate, but it's more awkward and slower, so I would avoid that approach in this case.
(See also my answer in the question 4e6 linked to.)