Functional languages are supposed to protect you from nasties like NullPointerException etc but there are some gotchas.
a[NoSuchElementException] should be thrownBy {
List.empty[Int].head
}
Curiously, Haskell does the same.
$ ghci
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
Prelude> head [] :: [Int]
*** Exception: Prelude.head: empty list
But this little annoyance occurred in a Spark job (here illustrated with ScalaTest):
an[UnsupportedOperationException] should be thrownBy {
List.empty[Int].max
}
A neat mitigation can be found here and looks like this:
List.empty[Int].reduceOption(Math.max) shouldBe None
An interesting point was made here on how things should be:
I would suggest that max is not an option. If you have a type (empty[T]), then this fold (I mean max) should return the neutral element regarding max(x,y), that is, the minimal element of the (partial) order of T, if one exists; if it does not, then it makes sense to throw an exception.
Same as with other monoidal operations, e.g. empty.sum is a zero, not an exception.
This would be the right mathematical answer.As a reminder, partial ordering exhibits anti-symmetry, transitivity and reflexivity, that is x ≤ x (contrast this with total ordering which exhibits anti-symmetry, transitivity and totality, that is x ≤ y or y ≤ x).
So, what's being said here is that the minimal element for, say, a Double is Double.MinValue. This is indeed transitive (obviously), reflexive (clearly MinValue ≤ MinValue) and anti-symmetric (if MinValue ≤ x and x ≤ MinValue then the only conclusion is x = MinValue).
Compare this to Double.NaN which is not reflexive and therefore a partial ordering does not exist.
So, to be mathematically correct, List.empty[Int].max should return Double.MinValue.
"Head is a different story; here we have a semigroup, not a monoid. There's no neutral element."
And so it is:
List.empty[Int].sum shouldBe 0
No comments:
Post a Comment