How can I change the signature of this method to a method that accepts both mutable and immutable map?
def - [A <: BothType] (o: A): ResourceHashMap = {
o.forall {
case (k, v) => this.contains(k) && this(k) >= v
} match {
case true => map {
case (k, v) => k -> (v - o.getOrElse(k, 0))
}
case _ => null
}
}
I know I can use Map trait, but it has not foreall and getOrElse method
What you call BothType is actually a scala.collection.Map. Try importing it and using then.
Related
I found this method:
def merge[K, V](maps: Seq[Map[K, V]])(f: (K, V, V) => V): Map[K, V] = {
maps.foldLeft(Map.empty[K, V]) { case (merged, m) =>
m.foldLeft(merged) { case (acc, (k, v)) =>
acc.get(k) match {
case Some(existing) => acc.updated(k, f(k, existing, v))
case None => acc.updated(k, v)
}
}
}
}
but it gives me a type mismatch error if i use it like this:
val mergeMsg = (map1: LinkedHashMap[Int, ListBuffer[Int]], map2: LinkedHashMap[Int, ListBuffer[Int]]) =>
{
val ms=Seq(map1, map2)
merge(ms.map(_.mapValues(List(_)))){(_, v1, v2) => v1 ++ v2}
}
The error says:
"Type mismatch, expected: mutable.Seq[Mutable.Map[NotInferedK, NotInferedV]], actual: mutable.Seq[Map[Int, List[ListBuffer[Int]]]]"
How can i solve this? I know it's something simple, but i'm new to scala.
The problem is that you are passing in to merge a sequence of mutable LinkedHashMaps. The function requires a sequence of immutable Maps.
You need to convert your LinkedHashMaps to the correct type first. The simplest way to do this is probably to call .toMap before you perform the mapValues.
merge(ms.map(_.toMap.mapValues(List(_)))){(_, v1, v2) => v1 ++ v2}
Update
Alternatively the method signature for Merge can be change to explicitly use scala.collection.Map. It will, by default, use scala.collection.immutable.Map.
def merge[K, V](maps: Seq[scala.collection.Map[K, V]])(f: (K, V, V) => V): scala.collection.Map[K, V]
val mergeMsg = (map1: LinkedHashMap[Int, ListBuffer[Int]],
map2: LinkedHashMap[Int, ListBuffer[Int]]) => {
val ms = Seq (map1.toMap, map2.toMap)
merge (ms) ((_, lb1, lb2) => (lb1 ++ lb2))
}
So the Type needs just to be converted to Map.
The k is not used for the process of updating, so we use _ instead.
The lbs are for ListBuffers.
There're map/flatMap methods, there're also recover/recoverWith methods in the Scala Future standard API.
Why there's no collectWith ?
The code of the collect method is pretty simple :
def collect[S](pf: PartialFunction[T, S])(implicit executor: ExecutionContext): Future[S] =
map {
r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
}
The code of the collectWith method is then easy to imagine :
def collectWith[S](pf: PartialFunction[T, Future[S]])(implicit executor: ExecutionContext): Future[S] =
flatMap {
r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
}
I know that I can implement it and "extend" the Future standard API easily thanks to this article : http://debasishg.blogspot.fr/2008/02/why-i-like-scalas-lexically-scoped-open.html
I done that in my project :
class RichFuture[T](future: Future[T]) {
def collectWith[S](pf: PartialFunction[T, Future[S]])(implicit executor: ExecutionContext): Future[S] =
future.flatMap {
r => pf.applyOrElse(r, (t: T) => throw new NoSuchElementException("Future.collect partial function is not defined at: " + t))
}
}
trait WithRichFuture {
implicit def enrichFuture[T](person: Future[T]): RichFuture[T] = new RichFuture(person)
}
Maybe my needs for that does not justify to implement it in the standard API ?
Here is why I need this method in my Play2 project :
def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
def handleResponse: PartialFunction[WSResponse, Future[Car]] = {
case response: WSResponse if response.status == Status.CREATED => Future.successful(response.json.as[Car])
case response: WSResponse
if response.status == Status.BAD_REQUEST && response.json.as[Error].error == "not_the_good_one" =>
createCar(key, eligibleCars.tail)
}
// The "carApiClient.createCar" method just returns the result of the WS API call.
carApiClient.createCar(key, eligibleCars.head).collectWith(handleResponse)
}
I don't know how to do that without my collectWith method.
Maybe it's not the right way to do something like this ?
Do you know a better way ?
EDIT:
I have maybe a better solution for the createCar method that does not requires the collectWith method :
def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
for {
mayCar: Option[Car] <- Future.successful(eligibleCars.headOption)
r: WSResponse <- carApiClient.createCar(key, mayCar.get) if mayCar.nonEmpty
createdCar: Car <- Future.successful(r.json.as[Car]) if r.status == Status.CREATED
createdCar: Car <- createCar(key, eligibleCars.tail) if r.status == Status.BAD_REQUEST && r.json.as[Error].error == "not_the_good_one"
} yield createdCar
}
What do you think about this second solution ?
Second edit:
Just for information, here is my final solution thanks to #Dylan answer :
def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
def doCall(head: Car, tail: List[Car]): Future[Car] = {
carApiClient
.createCar(key, head)
.flatMap( response =>
response.status match {
case Status.CREATED => Future.successful(response.json.as[Car])
case Status.BAD_REQUEST if response.json.as[Error].error == "not_the_good_one" =>
createCar(key, tail)
}
)
}
eligibleCars match {
case head :: tail => doCall(head, tail)
case Nil => Future.failed(new RuntimeException)
}
}
Jules
How about:
def createCar(key: String, eligibleCars: List[Car]): Future[Car] = {
def handleResponse(response: WSResponse): Future[Car] = response.status match {
case Status.Created =>
Future.successful(response.json.as[Car])
case Status.BAD_REQUEST if response.json.as[Error].error == "not_the_good_one" =>
createCar(key, eligibleCars.tail)
case _ =>
// just fallback to a failed future rather than having a `collectWith`
Future.failed(new NoSuchElementException("your error here"))
}
// using flatMap since `handleResponse` is now a regular function
carApiClient.createCar(key, eligibleCars.head).flatMap(handleResponse)
}
Two changes:
handleResponse is no longer a partial function. The case _ returns a failed future, which is essentially what you were doing in your custom collectWith implementation.
use flatMap instead of collectWith, since handleResponse now suits that method signature
edit for extra info
If you really need to support the PartialFunction approach, you could always convert a PartialFunction[A, Future[B]] to a Function[A, Future[B]] by calling orElse on the partial function, e.g.
val handleResponsePF: PartialFunction[WSResponse, Future[Car]] = {
case ....
}
val handleResponse: Function[WSResponse, Future[Car]] = handleResponsePF orElse {
case _ => Future.failed(new NoSucheElementException("default case"))
}
Doing so would allow you to adapt an existing partial function to fit into a flatMap call.
(okay, technically, it already does, but you'd be throwing MatchErrors rather than your own custom exceptions)
I had tried to implement a foldLeft on a LinkedList with this code, one curried foldLeft2, and one not, foldLeft:
sealed trait LinkedList[+E] {
#tailrec
final def foldLeft[A](accumulator: A, f: (A, E) => A): A = {
this match {
case Node(h, t) => {
val current = f(accumulator, h)
t.foldLeft(current, f)
}
case Empty => accumulator
}
}
#tailrec
final def foldLeft2[A](accumulator: A)(f: (A, E) => A): A = {
this match {
case Node(h, t) => {
val current = f(accumulator, h)
t.foldLeft2(current)(f)
}
case Empty => accumulator
}
}
}
But when I use foldLeft, it seems I need to declare the type for accumulator and item, but for foldLeft2, I don't. Can someone explain why that is?
class LinkedListSpecification extends Specification {
"linked list" should {
"foldLeft correctly" in {
val original = LinkedList(1,2,3,4)
original.foldLeft(0, (acc: Int, item: Int) => acc + item) === 10
}
}
"linked list" should {
"foldLeft2 correctly" in {
val original = LinkedList(1,2,3,4)
original.foldLeft2(0)((acc, item) => acc + item) === 10
}
}
}
This is because the type inference in Scala works left-to-right across parameter lists.
Thus in the second version foldLeft2 it is able to infer the type A as Int before it continues to the next parameter list where it now expects a function (Int,E)=>Int.
While in the first version foldLeft it is trying to infer A at the same time by both parameters (accumulator and f). It complains about the anonymous function you are passing to it because it hasn't inferred type A yet.
I was wondering whether Scala supports recursive macro expansion e.g. I am trying to write a lens library with a lensing macro that does this:
case class C(d: Int)
case class B(c: C)
case class A(b: B)
val a = A(B(C(10))
val aa = lens(a)(_.b.c.d)(_ + 12)
assert(aa.b.c.d == 22)
Given lens(a)(_.b.c.d)(f), I want to transforms it to a.copy(b = lens(a.b)(_.c.d)(f))
EDIT:
I made some decent progress here
However, I cannot figure out a generic way to create an accessor out of List[TermName] e.g. for the above example, given that I have List(TermName('b'), TermName('c'), TermName('d'))), I want to generate an anonymous function _.b.c.d i.e. (x: A) => x.b.c.d. How do I do that?
Basically, how can I write these lines in a generic fashion?
Actually I managed to make it work: https://github.com/pathikrit/sauron/blob/master/src/main/scala/com/github/pathikrit/sauron/package.scala
Here is the complete source:
package com.github.pathikrit
import scala.reflect.macros.blackbox
package object sauron {
def lens[A, B](obj: A)(path: A => B)(modifier: B => B): A = macro lensImpl[A, B]
def lensImpl[A, B](c: blackbox.Context)(obj: c.Expr[A])(path: c.Expr[A => B])(modifier: c.Expr[B => B]): c.Tree = {
import c.universe._
def split(accessor: c.Tree): List[c.TermName] = accessor match { // (_.p.q.r) -> List(p, q, r)
case q"$pq.$r" => split(pq) :+ r
case _: Ident => Nil
case _ => c.abort(c.enclosingPosition, s"Unsupported path element: $accessor")
}
def join(pathTerms: List[TermName]): c.Tree = (q"(x => x)" /: pathTerms) { // List(p, q, r) -> (_.p.q.r)
case (q"($arg) => $pq", r) => q"($arg) => $pq.$r"
}
path.tree match {
case q"($_) => $accessor" => split(accessor) match {
case p :: ps => q"$obj.copy($p = lens($obj.$p)(${join(ps)})($modifier))" // lens(a)(_.b.c)(f) = a.copy(b = lens(a.b)(_.c)(f))
case Nil => q"$modifier($obj)" // lens(x)(_)(f) = f(x)
}
case _ => c.abort(c.enclosingPosition, s"Path must have shape: _.a.b.c.(...), got: ${path.tree}")
}
}
}
And, yes, Scala does apply the same macro recursively.
I want to make two custom types with the type keyword and make them covariant to some other type, so that I could put them both in one list or map and work with it through pattern-matching, is that possible?
type Reaction
type Condition = () => Boolean
type ComplexReaction extends Reaction = (Condition) => Unit
type SimpleReaction extends Reaction = () => Unit
val map = Map[Condition, Reaction]
def addPair(c: Condition, a: Reaction) { map += (c -> a) }
def executeAll {
for(puffy <- map) puffy match {
case (c, a: ComplexReaction) => a(c)
case (c, a: SimpleReaction) => if(c) a()
}
}
but of course that kind of type construct is not allowed in Scala. Is there any way to acheive a similar result or do I have to make two separate maps?
This is one possibly good way.
type Condition = () => Boolean
sealed trait Reaction
case class ComplexReaction(a: (Condition) => Unit) extends Reaction
case class SimpleReaction(a: () => Unit) extends Reaction
val map = Map[Condition, Reaction]
def addPair(c: Condition, a: Reaction) { map += (c -> a) }
def executeAll {
for(puffy <- map) puffy match {
case (c, ComplexReaction(a)) => a(c())
case (c, SimpleReaction(a)) => if(c()) a()
}
}
As a side note, this is what I would normally do in Haskell (change any conflicting types into newtypes).
i have pretty much the same solution, i have simplify the type Condition , adding call by name parameter and change map to be mutable :
type Condition = Boolean
sealed abstract class Reaction
case class ComplexReaction(rec: (=> Condition) => Unit) extends Reaction
case class SimpleReaction(rec: () => Unit) extends Reaction
var map = Map[Condition, Reaction]()
def addPair(c: Condition, a: Reaction) { map += (c -> a) }
def executeAll {
for(puffy <- map) puffy match {
case (c, ComplexReaction(i)) => i(c)
case (c, SimpleReaction(i)) => if(c) i()
}
}