Wednesday, December 21, 2011

Learning Scala : Reading the exotic and essential List API scaladoc 2

Authored by Win Myo Htet


Before we look into Abstract Value Members of List API, I assume that you have refreshed the List API scaladoc page so that the Search Section is back to default. There are two functions productArity and productElement under Abstract Value Members. In the search box, type "product" and change Ordering to By inheritance. We see that the two stated functions along with extra two functions are inherited from Product and there are also two other separate funtions, product from different classes. Product is, sort of, Tuple's cousin. I have covered a bit of Product in my blog: Learning Scala : "case class", twitter interview question? Please read up that blog to learn more about Product. I will still cover some of its usage here.

scala> val list=List(1,2,3,4,5,6,7,8,9,10)
list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> list.productArity
res0: Int = 2

scala> list.productElement(0)
res1: Any = 1

scala> list.productElement(1)
res2: Any = List(2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> list.productPrefix
res3: java.lang.String = ::

productArity talks about the number of field in the class. We have 2 fields. The first being 1 and the rest being the list. Hmmm..... Then, the productPrefix, which is a toString methods of the derived class, is "::". If we look at the source code at line 390, :: class.
final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extends List[B] {
 override def head : B = hd
      override def tail : List[B] = tl
      override def isEmpty: Boolean = false
    .
    .
    .
It all makes sense now. The first field is head and the rest is tail. It is a case class and you will have a sense that the very well used List's pattern matching format of head::tail is coming from here. Scala List API author has done the bulk of lifting(converting) for the developer ease of use of head::tail from ::(head, tail). That process is beyond the scope of this blog. Daniel C. Sobral has talked a bit about it on SO.

That leaves us with two functions: product and product.
def product : A
def product [B >: A] (implicit num: Numeric[B]): B
Generally, they both serve the same purpose: Multiplies up the elements of this collection. The first product is for number literal and does not take any parameter. Leaving out the parameter is a Scala idiomatic usage meaning that the function does not have side effect. The second one has restriction in a form of lower bound, [B >: A],  and takes the implicit parameter.

scala> val numList=List(1,2,3)
numList: List[Int] = List(1, 2, 3)

scala> numList.product
res0: Int = 6

scala> numList.product()
<console>:9: error: not enough arguments for method product: (implicit num: Numeric[B])B.
Unspecified value parameter num.
              numList.product()
                             ^

scala> numList.sum
res2: Int = 6

scala> numList.sum()
<console>:9: error: not enough arguments for method sum: (implicit num: Numeric[B])B.
Unspecified value parameter num.
              numList.sum()
                         ^

scala> val stringList=List("one","two","three")
stringList: List[java.lang.String] = List(one, two, three)

scala> stringList.product
<console>:9: error: could not find implicit value for parameter num: Numeric[java.lang.String]
              stringList.product
                         ^

scala> stringList.sum
<console>:9: error: could not find implicit value for parameter num: Numeric[java.lang.String]
              stringList.sum
                         ^

scala> implicit object StringNumeric extends math.Numeric[String] {
     |   override def one=""
     |   override def zero=""
     |   def plus(x: String, y: String) =(x,y)match {case ("",y) =>y
     |                                             case (x,y)   =>  x+" + "+y}
     |   def minus(x: String, y: String) = x+" - "+y
     |   def times(x: String, y: String)=(x,y)match {case ("",y) =>y
     |                                             case (x,y)   =>  x+" * "+y}
     |   def negate(x: String): String ="-"+x
     |   def fromInt(x: Int) = x.toString
     |   def toInt(x: String) = -1
     |   def toLong(x: String) = toInt(x)
     |   def toFloat(x: String) = toInt(x)
     |   def toDouble(x: String) = toInt(x)
     |   def compare(x:String,y:String) = -1
     | }
defined module StringNumeric

scala> stringList.product
res6: java.lang.String = one * two * three

scala> stringList.sum
res7: java.lang.String = one + two + three

In the code snippet, we see the usage of the first function product on List with integers. Since product function is defined without the () to begin with, the error is thrown. However, if the function is defined as product(), then adding or leaving () won't be any issue. It is also the same for function call sum. When we try to apply(abuse) product and sum on stringList, it throws error. I have created(abused) the implicit object to show you the power of the Scala implicit feature and the product(and sum) function with implicit paramter. Here we also leave out the parenthesis along with the parameter (which is fed from the List)  when we are using the function product. (Scala allows to leave out the parenthesis, dot and parameter for function with one or zero parameter.)

Well, we have finished reading the Abstract Value Members section and 6 functions from the Concrete Value Members section : product, product, productIterator, productPrefix, sum and sum. Let's go take a look at those cryptic functions : ++ :+ :: :\ /:\ ...


Authored by Win Myo Htet

No comments:

Post a Comment