I'm in the process of learning a bit about Scala in order to see how it compares to F#, which is what I spend most of my time with. I'm attempting a pretty straightforward memoization technique for functions. In F# I would use something like this:
open System.Collections.Generic
let memoize (f : 'a -> 'b) =
// A dictionary in which to cache results of previous
// calls of the function
let dict = new Dictionary<'a, 'b>()
let memoizedFunction input =
match dict.TryGetValue(input) with
| true, x -> x
| false, _ ->
let answer = f input
dict.Add(input, answer)
answer
memoizedFunction
In attempting to produce an equivalent version of this in Scala I've come up with the following:
def memoize[A, B](f: A => B): A => B = {
val dict = collection.mutable.WeakHashMap[A, B]()
def memoizedFunction[A, B](input: A): B =
dict.get(input) match {
case x: Some[B] => x
case _ =>
val answer = f(input)
dict += ((input, answer))
answer
}
memoizedFunction _
}
I'm receiving a few errors in the REPL when I attempt to define this function:
<console>:11: error: type mismatch;
found : input.type (with underlying type A)
required: A
dict.get(input) match {
^
<console>:14: error: type mismatch;
found : input.type (with underlying type A)
required: A
val answer = f(input)
^
<console>:15: error: type mismatch;
found : input.type (with underlying type A)
required: A
dict += ((input, answer))
^
Can anyone point out where I'm going wrong here?
The type parameters on memoizedFunction shadow the outer type parameters. The nitty-gritty of Scala generics escapes me, but that's why input's type of A isn't the A that Scala is looking for.
Also, x: Some[B] conflicts with the return type of memoizedFunction, which is B. The idiomatic way to match Option is Some(x) => // do something with x.
So here's what I've got with those 2 things corrected:
def memoize[A, B](f: A => B): A => B = {
val dict = collection.mutable.WeakHashMap[A, B]()
def memoizedFunction(input: A): B =
dict.get(input) match {
case Some(x) => x
case _ =>
val answer = f(input)
dict += ((input, answer))
answer
}
memoizedFunction _
}
Related
The Option class has a method named fold(). The docs say:
sealed abstract class Option[+A]
fold[B](ifEmpty: ⇒ B)(f: (A) ⇒ B): B
Returns the result of applying f to this scala.Option's value if the scala.Option is nonempty. Otherwise, evaluates expression ifEmpty.
The docs continue:
This is equivalent to scala.Option map f getOrElse ifEmpty.
But is this really true? I've been told that under certain circumstances, with values of certain types, there are differences, but never with a decent explanation. What exactly are the situations where these two constructions will behave differently and why?
Option.fold is safer than .getOrElse. You can see the definition for .fold below, where both ifEmpty and f are of type B (introduced only after scala 2.10, probably):
#inline final def fold[B](ifEmpty: => B)(f: A => B): B =
if (isEmpty) ifEmpty else f(this.get)
which means you will probably not mess up the data types (exception below):
scala> val data = Option("massive data").fold(-1) { _ => 1 }
data: Int = 1
// but if I try to return different type in either of ifEmpty or f
// compiler will curse me right at my face
scala> val data = Option("massive data").fold(-1) { _ => "Let me get caught by compiler" }
<console>:17: error: type mismatch;
found : String("Let me get caught by compiler")
required: Int
val data = Option("massive data").fold(-1) { _ => "Let me get caught by compiler" }
^
While getOrElse is not as safe, unless you provide the type (supertype B in following definition) manually.
#inline final def getOrElse[B >: A](default: => B): B =
if (isEmpty) default else this.get
which means you can return a different type from getOrElse than what the original value wrapped in Option[A] was.
scala> val data = Option("massive data").map(_ => 1).getOrElse(List("I'm not integer"))
data: Any = 1
// you have to manually mention the type to getOrElse to restrict,
// which is not that smart in my opinion
scala> val data = Option("massive data").map(_ => 1).getOrElse[Int](List("I'm not integer"))
<console>:17: error: type mismatch;
found : List[String]
required: Int
val data = Option("massive data").map(_ => 1).getOrElse[Int](List("I'm not integer"))
^
The interesting thing is you can return unit from getOrElse or fold which can introduce bugs in an application unless you catch it in unit tests.
scala> val data = Option("massive data").fold() { _ => 1 }
data: Unit = ()
scala> val data = Option("massive data").map(_ => 1).getOrElse()
data: AnyVal = 1
As a counterpoint to #prayagupd's answer, fold often invites you to mess up types in a specific way.
The problem is that by Scala's rules, only ifEmpty is used to infer B and then f is checked to be suitable. Which means that using None or Nil as ifEmpty, which is quite common, will lead to their singleton types being used as B instead of Option/List[SomeType], no matter what f returns.
Of course, there are workarounds: specify B explicitly, use Option.empty[SomeType] or None: Option[SomeType] instead of None. Or just use pattern-matching.
I am working my way through learning scalaz and Learn You A Haskell For Greater Good and wonder how to translate the filterM example from LYAHFGG to Scala.
fst $ runWriter $ filterM keepSmall [9,1,5,2,10,3]
with keepSmall defined as
keepSmall :: Int -> Writer [String] Bool
keepSmall x
| x < 4 = do
tell ["Keeping " ++ show x]
return True
| otherwise = do
tell [show x ++ " is too large, throwing it away"]
return False
My naive approach ends with compilation errors and I have no clue how to go around that issue!
val keepSmall: (Int => WriterT[Id, Vector[String], Boolean]) = (x: Int) =>
if (x < 4) for {
_ <- Vector("Keeping " + x.shows).tell
} yield true
else for {
_ <- Vector(x.shows + " is too large, throwing it away").tell
} yield false
println(List(9,1,5,2,10,3) filterM keepSmall)
Compilation errors:
Error:(182, 32) no type parameters for method filterM: (p: Int => M[Boolean])(implicit evidence$4: scalaz.Applicative[M])M[List[Int]] exist so that it can be applied to arguments (Int => scalaz.WriterT[scalaz.Scalaz.Id,Vector[String],Boolean])
--- because ---
argument expression's type is not compatible with formal parameter type;
found : Int => scalaz.WriterT[scalaz.Scalaz.Id,Vector[String],Boolean]
required: Int => ?M[Boolean]
println(List(9,1,5,2,10,3) filterM keepSmall)
^
and
Error:(182, 40) type mismatch;
found : Int => scalaz.WriterT[scalaz.Scalaz.Id,Vector[String],Boolean]
required: Int => M[Boolean]
println(List(9,1,5,2,10,3) filterM keepSmall)
^
The issue is due to the fact that Scala can't really know how to fit a type with three holes into the argument expected by filterM, which has only one hole filled with a Boolean.
You could solve your problem using some weird type-lambda syntax like this (not tested, may not work):
val keepSmall: (Int => ({type L[T] = WriterT[Id, Vector[String], T]})#L) = ...
Or (much easier) by introducing a type alias as follows:
type MyWriter[T] = WriterT[Id, Vector[String], T]
val keepSmall: (Int => MyWriter[Boolean]) = ...
This will make sure that the kind of the argument expected by filterM matches with the kind of the argument you are providing.
I am trying to do several dependent Slick/DB calls and then display the resulting data within a twirl template.
def show(slug: String) = Action.async { implicit rs =>
for {
f <- fooDAO.findBySlug(slug) // f is of type Option[foo]
fid <- f.flatMap(a => a.id.map(b => b)) // fid is of type Long
b <- barDAO.findByFooId(fid) // b is of type Seq[bar]
} yield {
f.map {
case Some(f) => Ok(views.html.foobar(f, b))
case _ => NotFound
}
}
}
I first need to get the "ID" to then be able to query other relevant data. The compiler is now producing this error:
play.sbt.PlayExceptions$CompilationException: Compilation error[type mismatch;
found : scala.concurrent.Future[Option[play.api.mvc.Result]]
required: Option[?]]
Any help would be greatly appreciated.
There is a fundamental flaw in your code, in that you're mixing in the same comprehension an Option and a Seq
A for-comprehension is expected to work on the same "container" type, which will be the resulting representation of the yield
e.g. if you combine several Options you get an Option, if you combine Seqs you get a Seq.
In this case you can overcome the problem by converting the Option (foo) to a Seq (which will be empty if the foo is None and have 1 element if not).
The end result would be
val results: Seq[(Foo, Bar)] =
for {
f <- fooDAO.findBySlug(slug).toSeq // f is of type Seq[Foo]
b <- barDAO.findByFooId(f.id) // b is of type Seq[Bar]
} yield (f, b)
But I guess this is not what you need. I suppose you want to get all Bars associated with the retrieved Foo, if any, and present it with your template. If no Foo is present for the slug, you want a NotFound.
We can do it like this
def show(slug: String) = Action.async { implicit rs =>
val f = fooDAO.findBySlug(slug) // f is of type Option[Foo]
f.fold(
NotFound,
foo => Ok(views.html.foobar(foo, barDAO.findByFooId(foo.id))
)
}
You can make it more explicit by defining a supporting method
def show(slug: String) = Action.async { implicit rs =>
def barsOf(f: Foo): Seq[Bar] = barDAO.findByFooId(f.id)
val f = fooDAO.findBySlug(slug) // f is of type Option[Foo]
f.fold(
NotFound,
foo => Ok(views.html.foobar(foo, barsOf(foo))
)
}
It's a bit tricky understanding what you're trying to achieve here, but if the whole thing is predicated on the findbySlug returning a Future[Option[Foo]] and the eventual outcome being a NotFound if that Option is a None, then your yield should probably just be:
...
} yield {
f.fold(NotFound)(foo => Ok(views.html.foobar(foo, b)))
}
Option[T] is a fantastic type for data-retrieval and control-flow, but pattern-matching on it is almost never the right approach. The use of fold feels nicely succinct for the task.
I am having trouble getting my code with parameterized types to pass the scala compiler. My goal is to be able to express (Predicate, Action) pairs as shown in the MyProg object.
trait ProgBase {
type Predicate[T] = T => Boolean
type Action[T] = T => Unit
private var prog = List[(Predicate[Any], Action[Any])]()
final def on[T <: Any](pred: Predicate[T])(action: Action[T]) = {
prog = (pred, action) :: prog // gives type mismatch
}
// remainder of trait elided
}
object MyProg extends ProgBase {
on[String](s => !s.isEmpty) { s =>
println(s + " is not empty")
}
on[Int](i => i.isValidByte) { i =>
println(i + " can fit in a byte")
}
}
By specifying that T has an upper bound of Any, I hoped this would appease the compiler, but clearly I am missing something:
[error] ......ProgBase.scala:8 type mismatch;
[error] found : (T => Boolean, T => Unit)
[error] required: (Any => Boolean, Any => Unit)
[error] prog = (pred, action) :: prog
[error] ^
First of all, answer to your question, if you write:
private var prog = List[(Predicate[_ <: Any], Action[_ <: Any])]()
It all compiles OK. We should use wildcards, 'cause the type of elements are unknown.
Second, maybe you mistyped, you can't use your on function as you used it, use it something like:
on[String](s => !s.isEmpty)(s => !s.isEmpty)
It caused by type mismatch: type Action[T] = T => Unit but println has type Unit,
so as a variant u can simply write: type Action = Unit. Obviously, u can avoid using this type alias at all.
Third, maybe you already know, and I shoudn't tell you, that in fact, you lose all information about predicate types - let's check it using Scala reflection:
import scala.reflect.runtime.{universe => ru}
def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T]
val s: String = "123"
val i: Int = 123
on[String](s => !s.isEmpty)(s => !s.isEmpty)
on[Int](i => i.isValidByte)(i => i.isValidByte)
getTypeTag((MyProg.prog.head._1)).tpe =:= ru.typeOf[(String) => Boolean] //>false!
So you see the problem.
To deal with it you can use heterogeneous lists. Lists and other various powerful structures you can find in shapeless: https://github.com/milessabin/shapeless
I recently happen to work with scalaz >>=. I put all the methods which should be bind with >>= in a list and foldleft as follows,
val dataMap:Map[K,V]
def call[F](funcList:List[funcOb[K, V, F]]):Either[F,Seq[(K,Option[V])]] = {
type t[a] = Either[F,a]
funcList.
map(v => {
v.funcs.
foldLeft((v.name,dataMap.get(v.name)).right[F])( _ >>= _ )
}
).sequence[t,(K,Option[V])]
}
case class funcOb[K,V,F]( name:K,
funcs:List[(K,Option[V]) => Either[F, (K, Option[V])]] = List.empty )
now I'm getting a funny error complaining that the required is similar to found
...: type mismatch;
[error] found : (K, Option[V]) => Either[F,(K, Option[V])]
[error] required: (K, Option[V]) => Either[F,(K, Option[V])]
[error] foldLeft((v.name,dataMap.get(v.name)).right[F])( _ >>= _ )
I cannot understand this behavour. Is there anything missing?
It requires a Function1[(A, B), C]. Your list contains functions of type Function2[A, B, C]. So you need to convert the function to tupled form before you can apply it.
The following works:
def call[F](funcList: List[funcOb[K, V, F]]): Either[F, Seq[(K, Option[V])]] = {
type t[a] = Either[F, a]
funcList.
map(v => {
v.funcs.
foldLeft((v.name, dataMap.get(v.name)).right[F])(_ >>= _.tupled)
}
).sequence[t, (K, Option[V])]
}