while learning how to use lists:
val oneTwo = List(1, 2)
val threeFour = List(3, 4)
val oneTwoThreeFour = oneTwo :: threeFour //forgot one ":", I really meant to ":::" (concatenate)
val filteredList = oneTwoThreeFour.filter(n => n > 1)
scalac informed me:
Error:Error:line (16)error: value > is not a member of scala.this.Any
val l2 = oneTwoThreeFour.filter(n => n > 1)
Commenting out the offending line and replacing it with
println("oneTwoThreeFour " + oneTwoThreeFour)
yields
oneTwoThreeFour List(List(1, 2), 3, 4)
which clearly shows that the first element is not an Int.
However, the following line compiles:
val filteredList = oneTwoThreeFour.filter(n => n == 1)
probably because '==' is defined for scala.this.Any
January 19, 2009
December 4, 2008
Pattern Guards fooled by null
A sadness:
Pattern guards don't handle nulls well, and, also, introducing a pattern guard appears to make a default _ not match on null values:
scala> def guard_fails(s: String) {
| s match {
| case s: String if s == null => println("null")
| case s: String if s != null => println("s=" + s)
| case _ => println("default")
| }
| }
guard_fails: (String)Unit
scala> guard_fails("test")
s=test
scala> guard_fails(null)
scala.MatchError
at .guard_fails(:5)
at .(:6)
at .()
at RequestResult$.(:3)
at RequestResult$.()
at RequestResult$result()
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMeth...
scala>
scala> def guard_fails2(s: String) {
| s match {
| case s: String if s != null => println("s=" + s)
| case _ => println("default")
| }
| }
guard_fails2: (String)Unit
scala> guard_fails2("test")
s=test
scala> guard_fails2(null)
scala.MatchError
at .guard_fails2(:5)
at .(:6)
at .()
at RequestResult$.(:3)
at RequestResult$.()
at RequestResult$result()
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMet...
scala>
To code this match defensively, you have to define a null pattern explicitly:
scala> def ok(s: String) {
| s match {
| case s: String if true => println("s=" + s)
| case null => println("null")
| case _ => println("default")
| }
| }
ok: (String)Unit
scala> ok("test")
s=test
scala> ok(null)
null
scala>
Pattern guards don't handle nulls well, and, also, introducing a pattern guard appears to make a default _ not match on null values:
scala> def guard_fails(s: String) {
| s match {
| case s: String if s == null => println("null")
| case s: String if s != null => println("s=" + s)
| case _ => println("default")
| }
| }
guard_fails: (String)Unit
scala> guard_fails("test")
s=test
scala> guard_fails(null)
scala.MatchError
at .guard_fails(
at .
at .
at RequestResult$.
at RequestResult$.
at RequestResult$result(
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMeth...
scala>
scala> def guard_fails2(s: String) {
| s match {
| case s: String if s != null => println("s=" + s)
| case _ => println("default")
| }
| }
guard_fails2: (String)Unit
scala> guard_fails2("test")
s=test
scala> guard_fails2(null)
scala.MatchError
at .guard_fails2(
at .
at .
at RequestResult$.
at RequestResult$.
at RequestResult$result(
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMet...
scala>
To code this match defensively, you have to define a null pattern explicitly:
scala> def ok(s: String) {
| s match {
| case s: String if true => println("s=" + s)
| case null => println("null")
| case _ => println("default")
| }
| }
ok: (String)Unit
scala> ok("test")
s=test
scala> ok(null)
null
scala>
October 24, 2008
Scala RichString is not comparable to String
A sadness:
val r: scala.runtime.RichString = "1"
val s: String = "1"
if (r == s) {
println("ok1")
} else {
println("sad1")
}
if (s == r) {
println("ok2")
} else {
println("sad2")
}
if (s.equals(r)) {
println("ok3")
} else {
println("sad3")
}
if (r.equals(s)) {
println("ok4")
} else {
println("sad4")
}
Will print:
sad1
sad2
sad3
sad4
This isn't as sad as:
sad1
ok2
sad3
ok4
which would precipitate Robey's "ultimate sadness".
I got into this state by adding a .drop to a String, and chaos resulted. The details are tangentally described in: http://www.nabble.com/String-and-RichString-equality-td14888607.html
Still, this is another thing to keep in your head when coding along. I suppose it's best to coerce RichStrings back to Strings immediately, so as not to allow RichStrings to propagate too far.
val r: scala.runtime.RichString = "1"
val s: String = "1"
if (r == s) {
println("ok1")
} else {
println("sad1")
}
if (s == r) {
println("ok2")
} else {
println("sad2")
}
if (s.equals(r)) {
println("ok3")
} else {
println("sad3")
}
if (r.equals(s)) {
println("ok4")
} else {
println("sad4")
}
Will print:
sad1
sad2
sad3
sad4
This isn't as sad as:
sad1
ok2
sad3
ok4
which would precipitate Robey's "ultimate sadness".
I got into this state by adding a .drop to a String, and chaos resulted. The details are tangentally described in: http://www.nabble.com/String-and-RichString-equality-td14888607.html
Still, this is another thing to keep in your head when coding along. I suppose it's best to coerce RichStrings back to Strings immediately, so as not to allow RichStrings to propagate too far.
October 13, 2008
NumberFormatException
scala> "true".toBoolean
res1: Boolean = true
That's pretty cool. Does it work for "false" too?
scala> "false".toBoolean
res1: Boolean = false
Yeah! Awesome! How does it handle other values, I wonder?
scala> "1".toBoolean
java.lang.NumberFormatException: For input string: "1"
... Err, what?
August 20, 2008
Handling nulls
There's been a bunch of posts about handling nulls in Scala (and even Java), so let's throw one more into the mix.
This morning, I've been writing some code to read test data from the filesystem, using resources. Following David's lead on showing Java-Scala code, here's a typical Java take on it, note that I need to handle the case of reading the file in IntelliJ differently than I do in the build (IntelliJ now places resources in a subdirectory per module):
Now, here's the code rewritten using
This code is nicer than the first as it explicitly shows that you're sequencing through the nullness and is much more concise (we could make it more concise too if we wish).
For those still stuck in Javaland, there's always Functional Java's Option to achieve a similar thing (though with more verbosity).
This morning, I've been writing some code to read test data from the filesystem, using resources. Following David's lead on showing Java-Scala code, here's a typical Java take on it, note that I need to handle the case of reading the file in IntelliJ differently than I do in the build (IntelliJ now places resources in a subdirectory per module):
def dataFile(path: String): FilePath = {
val resource = getResource(path)
if (resource == null) {
val intellijResource = getResource("furnace/" + path)
if (intellijResource == null) {
error("No resource found at '" + path + "' or 'furnace/" + path + "'")
} else {
intellijResource
}
} else {
resource
}
}
def getResource(path: String) = Foo.getClass.getResource(path)
Now, here's the code rewritten using
Option for handling a null returned from getResource (apologies for formatting). I'm using Scalaz's OptionW, as it provides a nice (implicit) conversion from null to an Option (onull) and for throwing an error on None (err). As an aside, these would make a fine addition to the standard API.
def dataFile(path: String): FilePath = dataFileFromAntBuild(path)
def dataFileFromAntBuild(path: String) = getResource(path).getOrElse(dataFileFromIntelliJ(path))
def dataFileFromIntelliJ(path: String) = {
val intellijPath = "furnace" + path
onull(getResource(intellijPath)).err("No resource found at '" + path + "' or '" + intellijPath + "'")
}
def getResource(path: String) = Foo.getClass.getResource(path)
This code is nicer than the first as it explicitly shows that you're sequencing through the nullness and is much more concise (we could make it more concise too if we wish).
For those still stuck in Javaland, there's always Functional Java's Option to achieve a similar thing (though with more verbosity).
Accumulation
It's a pretty common operation to take a list of stuff and accumulate the items. For example, if I have a collection of word frequencies and I want to aggregate the counts by word.
The "Java" imperative way of doing this is to create a HashMap of stuff and populating the HashMap. For example (this is Java-style Scala code):
What are we doing? We're creating some state and iteratively updating the state.
My preferred way of doing this is thinking about the issue as "transformative". We're transforming the input to the output, without mutating anything.
The transformative approach is shorter and I believe, sweeter.
The "Java" imperative way of doing this is to create a HashMap of stuff and populating the HashMap. For example (this is Java-style Scala code):
def accumulate(in: Seq[(String, Int)]): Seq[(String, Int)] = {
val map = new HashMap[String, Int]
for ((str, cnt) <- in) {
if (map.isDefinedAt(str)) map(str) = map(str) + cnt
else map(str) = cnt
}
map.toList
}
What are we doing? We're creating some state and iteratively updating the state.
My preferred way of doing this is thinking about the issue as "transformative". We're transforming the input to the output, without mutating anything.
def acc(in: Seq[(String, Int)]): Seq[(String, Int)] =
in.foldLeft[Map[String, Int]](Map.empty){
case (map, (str, cnt)) => map + (str -> (map.getOrElse(str, 0) + cnt))
}.toList
The transformative approach is shorter and I believe, sweeter.
August 18, 2008
Invoking Java varargs methods from Scala
A quick little example of invoking a Java varags method from Scala. Suppose you've code like the following:
This will generate a nice error message telling you exactly what to do:
What this is telling you is that you need to help the inferencer by type annotating the argument:
Nice one! Type annotating is something you'll get used to doing with Scala...
object MyApplication {
private lazy val javaApi = new JavaApi
def main(args: Array[String]) {
val numbers = Array(1, 2, 3)
javaApi.methodWithVarArgs(numbers)
}
}
This will generate a nice error message telling you exactly what to do:
.../MyApplication.scala:6: warning: I'm seeing an array passed into a Java vararg.
I assume that the elements of this array should be passed as individual arguments to the vararg.
Therefore I wrap the array in a `: _*', to mark it as a vararg argument.
If that's not what you want, compile this file with option -Xno-varargs-conversion.
javaApi.methodWithVarArgs(numbers)
^
one warning found
Compile suceeded with 1 warning; see the compiler output for details.
What this is telling you is that you need to help the inferencer by type annotating the argument:
object MyApplication {
private lazy val javaApi = new JavaApi
def main(args: Array[String]) {
val numbers = Array(1, 2, 3)
javaApi.methodWithVarArgs(numbers: _*)
}
}
Nice one! Type annotating is something you'll get used to doing with Scala...
Subscribe to:
Posts (Atom)