What common patterns/solutions have been established in Scala for frequently-encountered problems
Asked Answered
U

6

7

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?

Unicef answered 25/11, 2010 at 12:23 Comment(0)
P
3

Using Monoids or Numerics 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 :-)
Pyosis answered 25/11, 2010 at 12:23 Comment(1)
I like this one the most! It's my question, so I'm allowed to be subjective :)Unicef
P
4

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
Pyosis answered 25/11, 2010 at 12:23 Comment(0)
P
3

Using Monoids or Numerics 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 :-)
Pyosis answered 25/11, 2010 at 12:23 Comment(1)
I like this one the most! It's my question, so I'm allowed to be subjective :)Unicef
U
3

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.

Unicef answered 25/11, 2010 at 12:23 Comment(0)
P
3

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.

Phlox answered 25/11, 2010 at 12:23 Comment(2)
scalaz: (List(1, 2, 3) |@| List("a", "b"))((_,_))Hornet
Or scalaz: (xs ⊛ ys) { ×(_)(_) } if it comes to thatPyosis
L
2

If certain condition cond holds return Some(x), else return None:

Some(x) filter cond

[ picked up from Paul Phillips' post on mailing list ]

Lippert answered 25/11, 2010 at 12:23 Comment(0)
P
1

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)
Phlox answered 25/11, 2010 at 12:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.