Rick

Rick
Rick

Saturday, September 26, 2015

Scala book chapter 3 review, notes, stream of conscienceness (Part 2)

Scala 2 Book Chapter 3 review.
More of my random thoughts as I go through the Scala book.
Going through Chapter 3, ....
    val numNames = Array("zero", "one", "two", "three")
    numNames(0) = "0"

    for (i <-0 to numNames.length-1) {
      val name = numNames(i)
      println (s"name i $i = $name")
    }
For some reason I did not think that Scala had a for loop as most of the example I see use a while loop. But it does.
Java version of this:
        final String[] numNames = {"zero", "one", "two", "three"};
        numNames[0] = "0";

        for (int i = 0; i < numNames.length; i++) {
            out.printf ("name i %d = %s \n", i, numNames[i]);
        }
Other than the Java not supporitng implicit typing which it does sort of on the right hand side of the assingment, which is oddly inconsistent, there is not much difference LOC wise between these two. Scala one if you iterating of millions and million in a tight loop which was iterating of millions and million which was getting called in a microservice a million times a second, would cause a lot more GC given the even Integers are objects mantra (I would imagine but a good hotspot and Scala compiler could optmize most of that away.. do they? Based on perf of JSON parsers written in purse Scala, I have doubts, but I am no Scala expert.)
Scala doesn't technically have operator overloading, because it doesn't actually have operators - Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 923-924). Artima Press. Kindle Edition.
Which is technially what Python and C++ have but they call it operator overloading and so should you. Prior art. But you know whatever.
The book brags about the consitency of always using the object model.
Let's cover that a bit.
Here are two different ways to access a Map in scala.
    x += (1 -> "foo")
    x(1) = "foo2"
Here are five different ways to iterate through the list of numNames.
    val numNames = Array("zero", "one", "two", "three")
    numNames(0) = "0"

    for (i <-0 to numNames.length-1) {
      val name = numNames(i)
      println (s"name i $i = $name")
    }

    for (i <-0.to(numNames.length-1)) {
      val name = numNames(i)
      println (s"name i $i = $name")
    }

    var i = 0
    while (i < numNames.length) {
      val name = numNames(i)
      println (s"name i $i = $name")
      i+=1
    }


    i = 0
    numNames.foreach(name => {
      println (s"name i $i = $name")
      i+=1
    })


    i = 0
    for (name <- numNames) {
      println (s"name i $i = $name")
      i+=1
    }
Note the only difference between the first and second way is how I invoke the to method. One way is the operator way. One way is the normal Java . method invoke way.
I find Scala about as consistent at doing things as Perl so far, but I am new. I could see two developers writing the same program completely different.
I do find a lot of value in Scala. Its consistent Object method invocation is not one of them (so far).

List and Cons

::: :: Yuck!
Appending lists. Just as simple as this:
In the expression "1 :: twoThree", :: is a method of its right operand, the list, twoThree. ...If a method is used in operator notation, such as a  b, the method is invoked on the left operand, as in a.(b)—unless the method name ends in a colon. If the method name ends in a colon, the method is invoked on the right operand. --Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 985-997). Artima Press. Kindle Edition.
I guess I willget used to this. Right now I am not liking it much. I prefer Python's view of consistency.
I thought I understood what they said.
I tried it first in Java.
        final List<String> aList = asList("1", "2", "3");
        final List<String> bList = asList("4", "5", "6");

        final List<String> both = new ArrayList<>();

        both.addAll(aList);
        both.addAll(bList);

        out.printf("Both %s\n", both);
I got this
Both [1, 2, 3, 4, 5, 6]
Then I tried the same thing in Scala.
    val aList = List("1", "2", "3")
    val bList = List("4", "5", "6")

    val both = aList :: bList

    println(s"Both $both")
I got this
Both List(List(1, 2, 3), 4, 5, 6)
Ok.. that was not what I wanted. Let me try the extra colon :::.
    val aList = List("1", "2", "3")
    val bList = List("4", "5", "6")

    val both = aList ::: bList

    println(s"Both $both")
So the extra colon is if you want to add to the list.
Do you remember how += worked with map. Certainly += makes even more sense with list. right?
    both += "new item" //DOES NOT WORK
Ok.. += only works for maps and not for lists. Consistent. Not.
So how does one add an item to a list. I guess we find out in chapter 16 but chapter 3 give us some hints and I am not happy so far.
Let's compare again Java with what we know in chapter 3.
        final List<String> aList = asList("1", "2", "3");
        final List<String> bList = asList("4", "5", "6");

        final List<String> both = new ArrayList<>();

        both.addAll(aList);
        both.addAll(bList);

        both.add("foo");
        out.printf("Both %s\n", both);
    val aList = List("1", "2", "3")
    val bList = List("4", "5", "6")



    val both = aList ::: bList

    val newList = both :+ "foo"
    println(s"Both $newList")
To append to a list we need a ListBuffer which is only mentioned in ths chapter but not covered.
The rest of the list operations look pretty cool. I like the drop, dropRight, etc. I added these to Boon for Java (not callled that.. they are called split after the Python lingo for these operations), and they work with the Java util collections.

Tuples

I see the importance of Tuples. (Python has them and I get this).

Set

Yeah.. They have +=. I hope ListBuffer has +=. I assume it does. After I read more and try more out... My earlier criticisms are no longer valid. I sort of get the two operations for adding lists as one is for mutable list and the other is for immutable lists. I am also understanding their implementation details of Tuple a bit better with the disucssion of (1)->"Foo" which is actually a method that returns a tuple.
That said, bear in mind that neither vars nor side effects are inherently evil. Scala is not a pure functional language that forces you to program everything in the functional style. Scala is a hybrid imperative/functional language. You may find that in some situations an imperative style is a better fit for the problem at hand, and in such cases you should not hesitate to use it. --Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 1269-1272). Artima Press. Kindle Edition.
This is true. And I agree with this. We might disagree with when/how this is true , i.e., where the line is drawn. For performant programs, you are trying to reduce buffer copies, but buffer copies are better for functional programming. This is to say that functional programming has its place, but depending on the type of software you are building (in-memory rules engine, JSON parser), functional programming might not be the best fit. The Martin Thompson talks come to mind (Mechanical Sympathy).
Prefer vals, immutable objects, and methods without side effects. Reach for them first. Use vars, mutable objects, and methods with side effects when you have a specific need and justification for them. -- Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 1275-1277). Artima Press. Kindle Edition.
I agree with this. Even if you are programming Java, prefer finals to non-finals. More and more this is the direction that I was moving to with my Java programming.
Now let's compare the Java version (my Java version) to an example like the one in the book.
Here is my text file.
Fox jumped over the moon
Love is in the air
Death and taxes, the two constants in life
To be or not to be that is the question
Tis nobler to suffer the arrows of tyrants

Here is the Java version that follows the example in the book.
package foo;

import org.boon.IO;
import java.util.Arrays;
import java.util.List;
import static java.lang.System.out;
import static org.boon.Str.lpad;
import static org.boon.Str.str;

public class ReadFileShowWithLines {


    public static int widthOfLength(final String s) {
        return Integer.toString(s.length()).length();
    }

    public static void main(String... args) {

        if (args.length > 0) {
            final List<String> lines = IO.readLines(args[0]);
            final String longestLine = lines.stream().reduce(
                    (a, b) -> a.length() > b.length() ? a : b).get();

            final int maxWidth = widthOfLength(longestLine);

            for (String line : lines) {
                final int numSpaces = maxWidth - widthOfLength(line);
                out.println(lpad(str(line.length()), numSpaces, ' ') + " | " + line);
            }

        }
    }

}
output
24 | Fox jumped over the moon
18 | Love is in the air
42 | Death and taxes, the two constants in life
39 | To be or not to be that is the question
42 | Tis nobler to suffer the arrows of tyrants

Here is the Scala version.
package foo

import scala.io.Source

def widthOfLength(s: String) = s.length.toString.length

object ReadFileShowWithLinesScala {

  def main(args: String*) {
    if (args.nonEmpty) {
      val lines = Source.fromFile(args(0)).getLines().toList

      val longestLine: String = lines.reduceLeft(
        (a, b) => if (a.length > b.length) a else b
      )

      val maxWidth: Int = widthOfLength(longestLine)
      for (line <- lines) {
        val numSpaces: Int = maxWidth - widthOfLength(line)
        val padding = " " * numSpaces
        println(padding + line.length + " | " + line)
      }
    }
  }

}
output
24 | Fox jumped over the moon
18 | Love is in the air
42 | Death and taxes, the two constants in life
39 | To be or not to be that is the question
42 | Tis nobler to suffer the arrows of tyrants
The Scala version is shorter. Partly due to Source being a nice lib utitliy. And partly due to Scala not needing libs to do the same. Take out the imports and they get closer but Scala still wins.
def widthOfLength(s: String) = s.length.toString.length
    public static int widthOfLength(final String s) {
        return Integer.toString(s.length()).length();
    }
Scala is shorter.
Here is where this is not as much difference.
    if (args.nonEmpty) {
      val lines = Source.fromFile(args(0)).getLines().toList

      val longestLine: String = lines.reduceLeft(
        (a, b) => if (a.length > b.length) a else b
      )

      val maxWidth: Int = widthOfLength(longestLine)
      for (line <- lines) {
        val numSpaces: Int = maxWidth - widthOfLength(line)
        val padding = " " * numSpaces
        println(padding + line.length + " | " + line)
      }
    }
        if (args.length > 0) {
            final List<String> lines = IO.readLines(args[0]);
            final String longestLine = lines.stream().reduce(
                    (a, b) -> a.length() > b.length() ? a : b).get();

            final int maxWidth = widthOfLength(longestLine);

            for (String line : lines) {
                final int numSpaces = maxWidth - widthOfLength(line);
                out.println(lpad(str(line.length()), numSpaces, ' ') + " | " + line);
            }
        }
Here is get longest line in Java:
            final String longestLine = lines.stream().reduce(
                    (a, b) -> a.length() > b.length() ? a : b).get();
Here is get longest line in Scala
      val longestLine: String = lines.reduceLeft(
        (a, b) => if (a.length > b.length) a else b
      )
Scala has to make two less hops. And for some reason the Java version of reduce returns an Optional, and although Scala has Option, it opts not to use it (pun intended). Java is a bit bigger because it is backwards compatible with the older Collection lib and added a stream instead of adding reduce direct to Collections (this is more of a library choice than a langauge difference). And it opts to use Optional. In other words, they are linguistic very similar. Ternary operator in Java and Scala if blocks that return values (like Python btw).

No comments:

Post a Comment

Kafka and Cassandra support, training for AWS EC2 Cassandra 3.0 Training