What common patterns/solutions have been established in Scala for frequently-encountered problems
-
28-09-2019 - |
Question
I'll start the ball rolling.
Given a sequence of elements, some of which can be contained multiple times, one typical requirement is to count them - in the form of a tally or histogram.
The oft-quoted solution is:
ss.groupBy(identity).mapValues(_.size)
So what other solutions exist for similar commonly encountered problems in Scala?
Solution
Using Monoid
s or Numeric
s to define sensible operations for rich classes, using implicits if necessary.
case class Money(ccy: Currency, amount : BigDecimal) {
def +(that : Money) = {
require(this.currency == that.curency)
copy(amount = this.amount + that.amount)
}
def abs = copy(amount = amount.abs)
}
So then let's say I have a collection of Money
's and I want to sum them:
val turnover = trades.map(_.usdValue.abs).∑ //no implicit monoid :-(
But in order to do this I need to have an implicit Monoid
. But of course, a zero value for a Money
only makes sense if I have some currency already!
implicit def CurrencyMonoid(implicit currency : Currency) = new Monoid[Currency] {
def zero = Money(currency, 0)
def append(m1 : Money, m2 : Money) = m1 + m2
}
So now Scala will use both of these implicits:
implicit val usd = Currency.USD
val turnover = trades.map(_.usdValue.abs).∑ //yay for monoids :-)
OTHER TIPS
Not that I'm banging the same drum again but...
A solution for the problem where we have a number of processes which may produce a successful output, or fail with some error message. The goal is to aggregate the successful outcomes, if all processes produce a success and if one or more fails, to aggregate all error messages.
This can be solved by scalaz validation: firstly, setup some imports
scala> import scalaz._; import Scalaz._
import scalaz._
import Scalaz._
Now let's define our "processes"
scala> def fooI(s : String) : ValidationNEL[Exception, Int] = s.parseInt.liftFailNel
fooI: (s: String)scalaz.Scalaz.ValidationNEL[Exception,Int]
scala> def fooF(s : String) : ValidationNEL[Exception, Float] = s.parseFloat.liftFailNel
fooF: (s: String)scalaz.Scalaz.ValidationNEL[Exception,Float]
scala> def fooB(s : String) : ValidationNEL[Exception, Boolean] = s.parseBoolean.liftFailNel
fooB: (s: String)scalaz.Scalaz.ValidationNEL[Exception,Boolean]
Now use Applicative
to aggregate the failures/successes:
scala> def attempt(ss : String*) = (fooI(ss(0)) <|**|> (fooF(ss(1)), fooB(ss(2)))) match {
| case Success((i, f, b)) => println("Found " + i + " " + f + " " + b)
| case Failure(es) => es foreach println
| }
attempt: (ss: String*)Unit
Now let's try for some failures:
scala> attempt("a", "b", "true")
java.lang.NumberFormatException: For input string: "a"
java.lang.NumberFormatException: For input string: "b"
Now let's try for success:
scala> attempt("1", "2.3", "false")
Found 1 2.3 false
I missed several times a way to generate a cartesian product for Scala collections. In Haskell you can write
import Control.Applicative
(,) <$> [1,2,3] <*> ["a","b"]
-- [(1,"a"),(1,"b"),(2,"a"),(2,"b"),(3,"a"),(3,"b")]
The Scala solution
for(x <- List(1,2,3); y <- List("a","b")) yield (x,y)
is way too clumsy.
Shamelessly stolen from oxbow_lakes' answer to this question: Instantiating a case class from a list of parameters
Calling a method/function using a tuple to supply the arguments:
case class Foo(a: Int, b: String, c: Double)
(Foo.apply _).tupled apply (1, "bar", 3.14)
This can be used for ANY function.
If certain condition cond
holds return Some(x)
, else return None
:
Some(x) filter cond
[ picked up from Paul Phillips' post on mailing list ]
Sometimes you have to use while instead of for, and often you are collecting results:
val buf = new Listbuffer[Int]
while(cond()) {
val x = fancyStuff()
buf += calculation(x)
}
I think it would be very helpful to have for while
the same possibility to "yield" something as for for
, removing some burr on the edge between imperative and functional style:
val buf = while(cond()) {
val x = fancyStuff()
} yield calculation(x)