Showing posts with label product. Show all posts
Showing posts with label product. Show all posts

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

Wednesday, November 30, 2011

Learning Scala : "case class", twitter interview question?

Authored by Win Myo Htet


I have been learning Scala actively and reading blogs, stackoverflow, and books; browsing repo, getting my hands dirty coding snippet and writing blog. I come to this subject "case class". Well, I have to admit that reading the books is boring unlike blog, yet, the books are more reliable sources of information (oh yeah? You don't know that ?) Then, there is stackoverflow, which is more reliable, does not detour and gives instant enlightened answers/insights proof-read by professionals. Still, the book does a better job of explaining (better experience on ebook with the help of Glossary and Index) Yes, I will talk about the "case class". I digress in introduction!

As I crawl the internet to learn Scala, I have downloaded some twitter's open source popular repos on github. I have browsed, skimmed and searched the repos to see how the early scala adopter is using it and defining the Scala usage pattern. Here are the downloaded repos(watchers) : flockdb (1,133), finagle(592), gizzard(966), killdeer(18) and ostrich(179). With only these opened in the eclipse, I use eclispe search tool for some of the thing I am interested in. Search keyword and number found paired.

tailrec 17
foldLeft 19
foldRight 0
for 2945 /*This includes "for" from code comment*/
while 152 /*This also includes "while" from code comment*/

Yup, at that time, I want to know how pervasive is the TCO'ed recursion and fold usages. I guess, for-comprehension rules the day. And I also skim through the codes and notice quite a lot of case block matchings. So here are some stats:

case 1658 /*This includes "just in case ..." kind of comment*/
case class 187 /*don't know if this words pair can be in comment*/

Now I hope to have your attention on the seriousness of the subject matter. It will be on the twitter interview questions! Ok, how is "case class" different from "class"? What is "case class"? "case class" is just a class. Ok... It adds extra niceties-methods : copy, equal, hashcode and (prettier) toString. The important part is that when "case class" is used, the compiler creates companion object which has both apply and unapply methods. "apply(variables)" method in Scala can be replaced with just "(variables)", thus "case class" enables constructor without keyword "new".

scala> case class MyCaseClass(name:String,isCase:Boolean)
defined class MyCaseClass

scala> val myCaseClass=MyCaseClass("myCaseClass",true)
myCaseClass: MyCaseClass = MyCaseClass(myCaseClass,true)

scala> class MyClass(name:String,isCase:Boolean)
defined class MyClass

scala> val myClass=MyClass("myClass",false)
<console>:7: error: not found: value MyClass
       val myClass=MyClass("myClass",false)
                   ^

scala> val myClass=new MyClass("myClass",false)
myClass: MyClass = MyClass@be7f971

So the interviewer asks "Why is that "case class" constructor does not require "new" keyword while the regular one does?" Then, you will answer "Cause the compiler generate "companion object" with "apply" method which can be omitted" Then, the interviewer will ask "We don't care much about "new" keyword but we just want to use regular class and want the benefit of case pattern matching. How would you do that?" Don't worry. Here is the answer for you. "case class" pattern matching is made possible by companion object's unapply extractor method. So we just need to do what "case class" does!

Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_26).
Type in expressions to have them evaluated.
Type :help for more information.

scala> case class MyCaseClass(name:String,isCase:Boolean)
defined class MyCaseClass

scala> :paste
// Entering paste mode (ctrl-D to finish)

class MyClass(val name:String,val isCase:Boolean)
object MyClass {
  def unapply(myClass:MyClass):Option[(String,Boolean)]={
    if(myClass eq null) None
    Some((myClass.name,myClass.isCase))
  }
}

// Exiting paste mode, now interpreting.

defined class MyClass
defined module MyClass

scala> val myCaseClass=MyCaseClass("myCaseClass",true)
myCaseClass: MyCaseClass = MyCaseClass(myCaseClass,true)

scala> val myClass=new MyClass("myClass",false)
myClass: MyClass = MyClass@7bc2f501

scala> val classList=List(myClass,myCaseClass)
classList: List[ScalaObject] = List(MyClass@7bc2f501, MyCaseClass(myCaseClass,true))

scala> for( x <- classList) x match{
     |   case MyCaseClass(name,isCase)=>println(x+" "+name+" "+isCase)
     |   case MyClass(name,isCase)=>println(x+" "+name+" "+isCase)
     | }
$line2.$read$$iw$$iw$MyClass@7bc2f501 myClass false
MyCaseClass(myCaseClass,true) myCaseClass true

scala> 


In case, if you miss it, I have started the new repl session and redefine the classes. You will notice the ":paste" before MyClass definition tho. It is because we want the repl session to know that the following object with unapply method is the companion object to the class above. If you forget ":paste", don't worry, the repl will warn you. "unapply" method takes MyClass object and return the fields like the default constructor wrapped in the Option. Then we see the unapply extractor method of MyClass at work for pattern matching in the for loop near the bottom like MyCaseClass. Do you notice how pretty "case class" makes MyCaseClass's toString ? Your interviewer will then said "Ok, I am convinced that "case class" is better. What else does it do? How about if I want a new constructor?" Well, "case class" makes default constructor fields to be immutable (Scala always encourages immutable type.), so you don't have to state "val" explicitly. However, you will have to do that for "var" tho. If you want new constructor, you can create them yourself but to use it, you have to use "new" keyword because there won't be any compiler generated overloaded "apply" method for that new constructor.

Then, the interviewer is happy and you get the job at twitter. Don't forget me when twitter goes IPO and you become rich,OK?

Update
So, one interviewee came back to me and said that the interviewer said there are more to the case class. Well, if it is said so, let's take a look:

aunndroid@ubuntu:/host/linux/learning_scala/notes$ cat MyCaseClass.scala
case class MyCaseClass(name:String,isCase:Boolean){
  override def productPrefix="MyPrettyCaseClass"
}


We will use scalac -print option to peep into it. Please do it at home, it is safe. Actually I have omitted some interesting lines of codes for brevity, that's why.

aunndroid@ubuntu:/host/linux/learning_scala/notes$ scalac -print MyCaseClass.scala 
[[syntax trees at end of cleanup]]// Scala source: MyCaseClass.scala
package <empty> {
  case class MyCaseClass extends java.lang.Object with ScalaObject with Product with Serializable {
  .
  .
  .

  };
  final <synthetic> object MyCaseClass extends scala.runtime.AbstractFunction2 with ScalaObject with Serializable {
  .
  .
  .
  
  }
}


Now, we know that case class also has mixin of Serializable and Product trait. We know SerializableWhat is Product? Product enables the case class' fields to be accessed without reflection. Here is the Product scaladoc API and the following is the code snippet.

aunndroid@ubuntu:/host/linux/learning_scala/notes$ scala
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_26).
Type in expressions to have them evaluated.
Type :help for more information.

scala> :load MyCaseClass.scala
Loading MyCaseClass.scala...
defined class MyCaseClass

scala> val myCaseClass=MyCaseClass("aunnDroidCaseClass",true)
myCaseClass: MyCaseClass = MyPrettyCaseClass(aunnDroidCaseClass,true)

scala> myCaseClass.productArity
res3: Int = 2

scala> myCaseClass.productElement(0)
res4: Any = aunnDroidCaseClass

scala> myCaseClass.productElement(1)
res5: Any = true

scala> for(x <- myCaseClass.productIterator)println(x)
aunnDroidCaseClass
true

scala> myCaseClass.productPrefix
res7: java.lang.String = MyPrettyCaseClass


We have skipped the canEqual function. productArity provides the number of fileds in the class while productElement (n: Int) let us access those field with index. productIterator gives us the Iterator[Any] object which we use with for comprehension to print out the fields. We have overriden productPrefix, which is part of the reason why the case class has a pretty toString method.

Hope the interviewer is happy this time.

Authored by Win Myo Htet