Chapter 4 Scala book review and notes by Rick Hightower
Chapter 4 Scala book review and notes by Rick Hightower. (Note link is to second edition book, but I am reading the first edition because that is the one I own).
Notes and random thoughts. Any opinions stated in this document are not carved in stone. Read at your own risk.
Chapter 3 spoke of the ill of var versus val. Chapter 4 ChecksumAccumulator
uses a var. Earlier I noticed the use of semicolons in the Scala examples, which I thought strange given Scala does not need them. Here I see methods that use return which Scala does not need. When I write Scala, I do it with IntelliJ so it can warn me not to use this superfluous concepts. I guess Author(s) did not have IntelliJ available.
Spoke too soon. They just dropped the return (but the semicolons in the other examples were never mentioned so probably wont be there in the second edition).
Ok so vars and val are public by default, which I think means is that Scala generates sum() and sum(int) by default (accessor methods). But that is what it seemed to do when I was using it with/Java.
The recommended style for methods is in fact to avoid having explicit, and especially multiple, return statements. Instead, think of each method as an expression that yields one value, which is returned. This philosophy will encourage you to make methods quite small, to factor larger methods into multiple smaller ones. On the other hand, design choices depend on the design context, and Scala makes it easy to write methods that have multiple, explicit returns if that's what you desire. --Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 1408-1412). Artima Press. Kindle Edition.
Ok. Tons of one liner methods that are just expressions. Oh joy. That will take some getting.
As mentioned in Chapter 1, one way in which Scala is more object-oriented than Java is that classes in Scala cannot have static members. Instead, Scala has singleton objects. A singleton object definition looks like a class definition, except instead of the keyword class you use the keyword object. Listing 4.2 shows an example. --Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 1455-1458). Artima Press. Kindle Edition.
So static methods are bad, and we use singleton objects instead.
I too was once a singleton lover. If you prevent yourself from doing singletons for 1 year straight, you will find out one thing....singletons are evil. If you have not prevented yourself for 1 straight year, you have no right to talk. That is like never eating ice cream and saying you know the fries are better. Maybe the fries are better but shut up until you have tried the ice cream first! You can still represent the real world and only have one thing (though naturally you just need to make sure you only instantiate one). That is usually done in some top level module and passed down to services (ie. Dependency Injection). The worst thing I have found out about singletons is when it comes to wanting to write reset code (specifically in tests). In a no singleton system, I can just new Service() and if it was designed right, the use of static keyword is rarely used except maybe for constants and all state will be reset....Great for the test setup method. So, you are suggesting that your reset is accomplished through the constructor? This forces the constructor to look for previously existing conditions on the real world device and do the appropriate things to knock the device down before setting it back up again. Okay, I can buy that. Now, how do you "make sure" that only one instance of the device is present in your system? --http://c2.com/cgi/wiki?SingletonsAreEvil
I find the dogmatic proclamations of evilness are evil and don't see much difference between static methods which are associated with the Class object and having an object Foo
notation in a language (there are pros and cons to both approaches). I respect his thoughts, but...
My point is... So this book says don't use static methods and singletons are good, and classic Spring DI, IoC, etc. injections and traditional Java say that singletons are evil. Joy. No wonder people quit being developers and become managers. So much conflicting dogma to deal with.
I actually don't care. When in Rome (Scala), I will use object singleton, when back in Java/Spring world I will not use singleton and when not in Spring/Guice world of DI,.... When back in Boon/QBit with no Spring world I will use static methods where they make sense, and design by interface most of the time. I even design by interface when I write Python, and it drives my Python pals batty. I believe in the concept. I believe in mock objects, and testability.
I finally learned how to write a proper main method. I have been calling my Scala main from a Java main method. (My hidden super power is to hack things to work until I find time to do it the right way. First make it work. Then fix it up to work right. It is a skill brought on by always meeting schedule deadlines even if it means sacrificing personal health, and always cleaning up tech debt even if it is months later after 1.0 has been delivered and the project has not been canceled. There is the right way, and there is right now way. You have to pick you battles. End users don't care if you are using the right way. They care if there app works. You care because later you have to maintain this ball of goo, and your name and reputation is on it. I write comments a lot on contracts because I want to be able to hand this code off to someone else when I move on, and/or when I come back to this code in three months or a year, I want to be able to read what I wrote. Which reminds me, I add less comments to QBit then I do to code I use on contracts. QBit microservices lib is my calling card. It should have a lot more comments.)
object ReadFileShowWithLinesScala {
def main(args: Array[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)
}
}
}
def widthOfLength(s: String) = s.length.toString.length
Why I had to wait until chapter 4!
I don't have to do this shit now.
package foo;
import scala.collection.JavaConversions;
import scala.collection.mutable.Buffer;
import java.util.Arrays;
import java.util.List;
public class ReadFileShowWithLines {
public static void main(String... args) {
... // Java code
//This was how I was launching Scala code since they decide
//not to show me how to do this until Chapter 4,
//BTW I have some production code that does this because I could not find this online easily.
Buffer<String> seq = JavaConversions.asScalaBuffer(Arrays.asList(args));
ReadFileShowWithLinesScala.main(seq);
}
}
Thanks Chapter 4. Thanks for finally telling me what a Scala main method looks like. I tried everything and gave up and called it from Java.
One difference between Scala and Java is that whereas Java requires you to put a public class in a file named after the class—for example, you'd put class SpeedRacer in file SpeedRacer.java—in Scala, you can name .scala files anything you want, no matter what Scala classes or code you put in them. In general in the case of non-scripts, however, it is recommended style to name files after the classes they contain as is done in Java, so that programmers can more easily locate classes by looking at file names. --Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 1526-1531). Artima Press. Kindle Edition.
Yeah... so Java forces the convention and Scala does not. I think will become important when we are working with a lot of case classes. I wish they added case classes to Java. I am sick of Java beans which is almost reason enough to switch to Scala. Almost.
ChecksumAccumulator
uses a var. Earlier I noticed the use of semicolons in the Scala examples, which I thought strange given Scala does not need them. Here I see methods that use return which Scala does not need. When I write Scala, I do it with IntelliJ so it can warn me not to use this superfluous concepts. I guess Author(s) did not have IntelliJ available.
The recommended style for methods is in fact to avoid having explicit, and especially multiple, return statements. Instead, think of each method as an expression that yields one value, which is returned. This philosophy will encourage you to make methods quite small, to factor larger methods into multiple smaller ones. On the other hand, design choices depend on the design context, and Scala makes it easy to write methods that have multiple, explicit returns if that's what you desire. --Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 1408-1412). Artima Press. Kindle Edition.
As mentioned in Chapter 1, one way in which Scala is more object-oriented than Java is that classes in Scala cannot have static members. Instead, Scala has singleton objects. A singleton object definition looks like a class definition, except instead of the keyword class you use the keyword object. Listing 4.2 shows an example. --Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 1455-1458). Artima Press. Kindle Edition.
I too was once a singleton lover. If you prevent yourself from doing singletons for 1 year straight, you will find out one thing....singletons are evil. If you have not prevented yourself for 1 straight year, you have no right to talk. That is like never eating ice cream and saying you know the fries are better. Maybe the fries are better but shut up until you have tried the ice cream first! You can still represent the real world and only have one thing (though naturally you just need to make sure you only instantiate one). That is usually done in some top level module and passed down to services (ie. Dependency Injection). The worst thing I have found out about singletons is when it comes to wanting to write reset code (specifically in tests). In a no singleton system, I can just new Service() and if it was designed right, the use of static keyword is rarely used except maybe for constants and all state will be reset....Great for the test setup method. So, you are suggesting that your reset is accomplished through the constructor? This forces the constructor to look for previously existing conditions on the real world device and do the appropriate things to knock the device down before setting it back up again. Okay, I can buy that. Now, how do you "make sure" that only one instance of the device is present in your system? --http://c2.com/cgi/wiki?SingletonsAreEvil
object Foo
notation in a language (there are pros and cons to both approaches). I respect his thoughts, but...object ReadFileShowWithLinesScala {
def main(args: Array[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)
}
}
}
def widthOfLength(s: String) = s.length.toString.length
package foo;
import scala.collection.JavaConversions;
import scala.collection.mutable.Buffer;
import java.util.Arrays;
import java.util.List;
public class ReadFileShowWithLines {
public static void main(String... args) {
... // Java code
//This was how I was launching Scala code since they decide
//not to show me how to do this until Chapter 4,
//BTW I have some production code that does this because I could not find this online easily.
Buffer<String> seq = JavaConversions.asScalaBuffer(Arrays.asList(args));
ReadFileShowWithLinesScala.main(seq);
}
}
One difference between Scala and Java is that whereas Java requires you to put a public class in a file named after the class—for example, you'd put class SpeedRacer in file SpeedRacer.java—in Scala, you can name .scala files anything you want, no matter what Scala classes or code you put in them. In general in the case of non-scripts, however, it is recommended style to name files after the classes they contain as is done in Java, so that programmers can more easily locate classes by looking at file names. --Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 1526-1531). Artima Press. Kindle Edition.
Application trait
This is probably why I could not easily google how to do a Scala main method. You don't, you use the Application trait.
To use the trait, you first write "extends Application" after the name of your singleton object. Then instead of writing a main method, you place the code you would have put in the main method directly between the curly braces of the singleton object. --Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 1562-1565). Artima Press. Kindle Edition.
Ok.. I like this. Damn Odersky, quit making me like Scala. This defeats my previous narratives.
Ok.. Application trait is deprecated. So that was a quick elation with an even quicker deflation. Maybe I should not be using a book that is five years old. Maybe I should commit to going through a book before I buy it.
Here I am...
So it seems that I can use the App
instead of the Application
trait. Chapter 1-4 does not really cover a trait yet, but since Scala people can never shut up about how great Scala is, I sort of know what a trait is already, and have in fact already used them. Although, I wish I knew more.. Must read on...
Here is the code refactored to use App.
package foo
import scala.io.Source
object ReadFileShowWithLinesScala extends App {
def widthOfLength(s: String) = s.length.toString.length
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)
}
}
}
It is kicking Java ass just a little bit more now.
package foo;
import org.boon.IO;
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);
}
}
}
}
Not sure why he did not use foreach
so I did not use forEach
, but imagine if I did.. About the same. Except of course, Java is more consistent with came case then Scala.
Inheriting from Application is shorter than writing an explicit main method, but it also has some shortcomings. First, you can't use this trait if you need to access command-line arguments, because the args array isn't available. --Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 1570-1573). Artima Press. Kindle Edition.
I write a lot of microservices and I almost always get arguments from a config file or from environment variables or etcd or consul or..... I can't remember the last time I used main args
in production code. I don't see this as much of a limitation but if I was writing command line tools like I do in Python or Groovy from time to time, I would not like this.
I guess if you are writing scripts in Scala you use the Script form which has the args (covered in Chapter 2). Hmmm...
Chapter 4 is in the can. I feel like I learned a few things. Despite the dogma which makes me angry at this point in my career as I have come to love and hate things many time by wave after wave of dogma. I am more pragmatic. I try not to be dogmatic. I try to follow the precepts of the Bloch book which I think Scala supports more or less better than Java.
I would like Scala a lot more with less Dogma and the holier than though typical Scala developers. Granted they are not as bad as the early Ruby high-priests but still. Caustic Scala developers are tools. I like the attitude of Python and Groovy developers. I use it because it is productive. I love it, but you don't have to. Scala still seems to be more more pontification.
So far to me, Scala is a better language than Java. This does not mean I agree with all of the decisions that were made in the language design, but I can get used to the ones that I don't agree with.
To use the trait, you first write "extends Application" after the name of your singleton object. Then instead of writing a main method, you place the code you would have put in the main method directly between the curly braces of the singleton object. --Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 1562-1565). Artima Press. Kindle Edition.
App
instead of the Application
trait. Chapter 1-4 does not really cover a trait yet, but since Scala people can never shut up about how great Scala is, I sort of know what a trait is already, and have in fact already used them. Although, I wish I knew more.. Must read on...package foo
import scala.io.Source
object ReadFileShowWithLinesScala extends App {
def widthOfLength(s: String) = s.length.toString.length
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)
}
}
}
package foo;
import org.boon.IO;
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);
}
}
}
}
foreach
so I did not use forEach
, but imagine if I did.. About the same. Except of course, Java is more consistent with came case then Scala.
Inheriting from Application is shorter than writing an explicit main method, but it also has some shortcomings. First, you can't use this trait if you need to access command-line arguments, because the args array isn't available. --Odersky, Martin; Spoon, Lex; Venners, Bill (2010-12-13). Programming in Scala: A Comprehensive Step-by-Step Guide (Kindle Locations 1570-1573). Artima Press. Kindle Edition.
args
in production code. I don't see this as much of a limitation but if I was writing command line tools like I do in Python or Groovy from time to time, I would not like this.
No comments:
Post a Comment