Recursive monad execution with cats - scala

There is simple abstract look up service with possibility to retrieve value by key:
trait LookUp[F[_]] {
def read(key: String): F[Option[String]]
}
There is use case of this service, idea is to give implemented storage and accumulator with starting key, then ask for value from db if the result is None then stop and Return None, if the value is found then add it to accumulator list and look up for next value as key from previous call result. Execution stops when retrieved value already is found before or None is retrieved. Then a string of all acc elements is returned as result.
Tried like this:
def readWhileFound[F[_]: Monad](db: LookUp[F], acc: List[String]): F[Option[String]] = {
for{ result <- db.read(acc.head)} yield result match {
case Some(value) if(!acc.contains(value)) => readWhileFound(db, value::acc)
case _ => acc.mkstring("")
}
}
But I'm not able to get types right getting mismatch errors like:
found : F[cats.data.OptionT[[_]F[_],String]]
required: cats.data.OptionT[F,String]
Approach number 2:
def readWhileFound[F[_]: Monad](key: String, db: LookUp[F])(implicit m: Monad[F]): F[Option[String]] = {
m.tailRecM((Option(key), List.empty[String])) { case (currentK, accum) =>
currentK match {
case Some(value) if(!accum.contains(value)) => m.pure(Left((db.read(value), value :: accum)))
case _ => m.pure(Right(Some(accum.mkString(""))))
}
}
}
Getting compiler error:
(Found) F[Option[String]]
required: Option[String]
case Some(value) if(!accum.contains(value)) => m.pure(Left((db.read(value), value :: accum)))
Looks like db.read(value) somehow should be unwrapped out of F

This looks like a great use case for fs2:
You should be able to do something like this:
import fs2.Stream
def readWhileFound[F[_]: Concurrent](db: LookUp[F])(initialKey: String): F[List[String] =
Stream.unfoldEval(initialKey) { currentKey =>
db.read(key = currentKey).map(k => (k, k))
}.compile.toList

You are match-ing on the wrong expression in your first implementation. You should match on result, not on the entire for-comprehension. The implementation below should do what you're after.
def readWhileFound[F[_]: Monad](db: LookUp[F], startKey: String): F[Option[String]] = {
def loop(currKey: String, seenKeys: Set[String]): F[Option[String]] = {
db.read(currKey).flatMap {
case Some(nextKey) if !seenKeys.contains(nextKey) =>
loop(nextKey, seenKeys + nextKey)
case _ if seenKeys.nonEmpty => seenKeys.mkString("").some.pure[F]
case _ => none[String].pure[F]
}
}
loop(startKey, Set.empty)
}
I've replaced List with Set for the accumulated values because its contains method is more efficient, but if you care about the order in the returned result then you'll have to either go back to List (less efficient) or use two accumulators (one Set, the other List).

Related

how to check return value type in scala

I am pretty a fresh man to learn scala.
I want to ask how to check the function return value type?
For example :
def decode(list :List[(Int, String)]):List[String] = {
//val result = List[String]()
//list.map(l => outputCharWithTime(l._1,l._2,Nil))
//result
excuteDecode(list,List[String]())
def excuteDecode(list:List[(Int,String)],result:List[String]):List[String] = list match {
case Nil => Nil
case x::Nil=>outputCharWithTime(x._1,x._2,result)
case x::y =>excuteDecode(y,outputCharWithTime(x._1,x._2,result))
}
def outputCharWithTime(times:Int,str:String , result :List[String]):List[String]={
times match{
case 0 => result
case x => outputCharWithTime(times-1,str,str::result)
}
}
}
In this code , all the function return type is set to List[String], also created one empty List[String] parameter for excuteDecode() function .
However I get a compilation error:
Error:(128, 5) type mismatch;
found : Unit
required: List[String]
}
Anyone can tell me why there exist problem and how to check the actual return type by ourself ?
The order of statements matters here.
def decode(list :List[(Int, String)]):List[String] = {
def excuteDecode(list:List[(Int,String)],result:List[String]):List[String] = list match {
case Nil => Nil
case x::Nil=>outputCharWithTime(x._1,x._2,result)
case x::y =>excuteDecode(y,outputCharWithTime(x._1,x._2,result))
}
def outputCharWithTime(times:Int,str:String , result :List[String]):List[String]={
times match{
case 0 => result
case x => outputCharWithTime(times-1,str,str::result)
}
}
excuteDecode(list,List[String]()) // Moved here
}
In Scala, the last expression in a block defines, what the whole block returns; statements such as def are defined to produce a Unit (()).

Scala check a Sequence of Eithers

I want to update a sequence in Scala, I have this code :
def update(userId: Long): Either[String, Int] = {
Logins.findByUserId(userId) map {
logins: Login => update(login.id,
Seq(NamedParameter("random_date", "prefix-" + logins.randomDate)))
} match {
case sequence : Seq(Nil, Int) => sequence.foldLeft(Right(_) + Right(_))
case _ => Left("error.logins.update")
}
}
Where findByUserId returns a Seq[Logins] and update returns Either[String, Int] where Int is the number of updated rows,
and String would be the description of the error.
What I want to achieve is to return an String if while updating the list an error happenes or an Int with the total number of updated rows.
The code is not working, I think I should do something different in the match, I don't know how I can check if every element in the Seq of Eithers is a Right value.
If you are open to using Scalaz or Cats you can use traverse. An example using Scalaz :
import scalaz.std.either._
import scalaz.std.list._
import scalaz.syntax.traverse._
val logins = Seq(1, 2, 3)
val updateRight: Int => Either[String, Int] = Right(_)
val updateLeft: Int => Either[String, Int] = _ => Left("kaboom")
logins.toList.traverseU(updateLeft).map(_.sum) // Left(kaboom)
logins.toList.traverseU(updateRight).map(_.sum) // Right(6)
Traversing over the logins gives us a Either[String, List[Int]], if we get the sum of the List we get the wanted Either[String, Int].
We use toList because there is no Traverse instance for Seq.
traverse is a combination of map and sequence.
We use traverseU instead of traverse because it infers some of the types for us (otherwise we should have introduced a type alias or a type lambda).
Because we imported scalaz.std.either._ we can use map directly without using a right projection (.right.map).
You shouldn't really use a fold if you want to exit early. A better solution would be to recursively iterate over the list, updating and counting successes, then return the error when you encounter one.
Here's a little example function that shows the technique. You would probably want to modify this to do the update on each login instead of just counting.
val noErrors = List[Either[String,Int]](Right(10), Right(12))
val hasError = List[Either[String,Int]](Right(10), Left("oops"), Right(12))
def checkList(l: List[Either[String,Int]], goodCount: Int): Either[String, Int] = {
l match {
case Left(err) :: xs =>
Left(err)
case Right(_) :: xs =>
checkList(xs, (goodCount + 1))
case Nil =>
Right(goodCount)
}
}
val r1 = checkList(noErrors, 0)
val r2 = checkList(hasError, 0)
// r1: Either[String,Int] = Right(2)
// r2: Either[String,Int] = Left(oops)
You want to stop as soon as an update fails, don't you?
That means that you want to be doing your matching inside the map, not outside. Try is actually a more suitable construct for this purpose, than Either. Something like this, perhaps:
def update(userId: Long): Either[String, Int] = Try {
Logins.findByUserId(userId) map { login =>
update(login.id, whatever) match {
case Right(x) => x
case Left(s) => throw new Exception(s)
}
}.sum
}
.map { n => Right(n) }
.recover { case ex => Left(ex.getMessage) }
BTW, a not-too-widely-known fact about scala is that putting a return statement inside a lambda, actually returns from the enclosing method. So, another, somewhat shorter way to write this would be like this:
def update(userId: Long): Either[String, Int] =
Logins.findByUserId(userId).foldLeft(Right(0)) { (sum,login) =>
update(login.id, whatever) match {
case Right(x) => Right(sum.right + x)
case error#Left(s) => return error
}
}
Also, why in the world does findUserById return a sequence???

Using Scala reflection to check for a method on an object or to find key type on a Map

I am porting a class from Ruby to Scala 2.11 that implements variable merging in messages. I would like to pass an array of objects to the merge method and have it search each object for the keys that are referenced in the text of the message.
The core of this is a method called lookUp(key: String, obj: AnyRef) which receives a single key for which to search, and a single object on which to search for the key. If the object is a Map, and the Map's keys are either Symbol or String, then it will look in the Map for the requested key. Otherwise it will check if the object has a method with the same name of the key, and if so, it will invoke that method.
In the existing Ruby code, it is trivially easy to do this:
def look_up(key, obj)
if obj.respond_to?(:has_key?) && obj.has_key?(key)
obj[key]
elsif obj.respond_to?(key)
obj.send(key)
elsif obj.instance_variable_defined?(ivar = "##{key}")
obj.instance_variable_get(ivar)
end
end
Since it's easy to do so, the Ruby code also looks for an instance variable of the same name. I don't necessarily need that functionality in my Scala version.
One of the issues I am having is that the examples I've found require that I know the object's class ahead of time so I can call ru.typeOf[MyClass].declaration(ru.TermName("key")) (where ru is scala.reflect.runtime.universe).
Another issue is that this message merge can be happening hundreds of times per minute, and reflection seems to be a slow, involved process. If this all works as planned, I'll likely cache the reflection results by object type.
UPDATE: I was thinking about something like this, but is this overkill? Or is it necessary to properly capture the types in the Map? Also, this doesn't compile. Map, Symbol and String aren't the right types for their context.
def lookUp[T](obj: T, key: String)(implicit tag: ru.TypeTag[T]): Option[String] = tag.tpe match {
case ru.TypeRef(a, Map, List(Symbol, _)) => if (obj.contains(Symbol(key))) Some(obj(Symbol(key)).toString) else None
case ru.TypeRef(a, Map, List(String, _)) => if (obj.contains(key)) Some(obj(key).toString) else None
case _ =>
if (/* obj.key() exists */)
// Some(obj.key().toString)
else
None
}
UPDATE 2: It never occurred to me that I could use asInstanceOf with something like Map[String, _]. I used #johny's second example of code to come up with my solution. I cache the method names by class in a mutable.HashMap[Class[_], Set[String]].
def lookUp(obj: AnyRef, key: String): Option[String] = obj match {
case m: Map[_, _] =>
if (m.asInstanceOf[Map[String, _]].contains(key))
extractValue(m.asInstanceOf[Map[String, _]](key))
else if (m.asInstanceOf[Map[Symbol, _]].contains(Symbol(key)))
extractValue(m.asInstanceOf[Map[Symbol, _]](Symbol(key)))
else
None
case _ =>
val klass = obj.getClass
if (!methodsCache.contains(klass))
methodsCache(klass) = klass.getMethods.toList.filter(_.getParameterCount == 0).map(_.getName).toSet
val methodNames = methodsCache(klass)
if (methodsCache(klass).contains(key))
extractValue(klass.getDeclaredMethod(key).invoke(obj))
else
None
}
def extractValue(obj: Any): Option[String] = obj match {
case null | None => None
case Some(x) => Some(x.toString)
case x => Some(x.toString)
}
def lookUp(key: String, obj: AnyRef) {
obj match {
case x: Map[String, _] => x(key)
case _ => obj.getClass.getDeclaredMethod(key).invoke(obj)
}
}
Edit: To make sure key of Map is either String or scala.Symbol
def lookUp(key: String, obj: AnyRef)= {
obj match {
case x: Map[_, _] =>
if(x.asInstanceOf[Map[String,_]].contains(key))
x.asInstanceOf[Map[String,_]].get(key)
else if(x.asInstanceOf[Map[scala.Symbol,_]].contains(scala.Symbol(key)))
x.asInstanceOf[Map[scala.Symbol,_]].get(scala.Symbol(key))
else
None
case _ => Some(obj.getClass.getDeclaredMethod(key).invoke(obj))
}
}
The above too doesnt make sure the returned output is from the intended Map.
Edit: Following #JimCain's lead
def lookUp[T:ru.TypeTag](obj: T, key: String): Option[Any] = ru.typeTag[T].tpe match {
case ru.TypeRef(a, m, l) if(m.name.toString=="Map"&&l.head =:= ru.typeOf[java.lang.String])=> obj.asInstanceOf[Map[String,_]].get(key)
case ru.TypeRef(a, m,l) if(m.name.toString=="Map"&&l.head =:= ru.typeOf[Symbol])=> obj.asInstanceOf[Map[Symbol,_]].get(scala.Symbol(key))
case _ => Try(obj.getClass.getDeclaredMethod(key).invoke(obj)) match {
case Success(x) => Some(x)
case Failure(_) => None
}
}

How to match option map values at once?

is it possible to match Option[Map[String,String]] for some key at once (e.g. without nested matches)?
The following snippet is how it is now:
val myOption:Option[Map[String,String]] = ...
myOption match {
case Some(params) =>
params get(key) match {
case Some(value) => Ok(value)
case None => BadRequest
case None => BadRequest
}
Sure! Just flatMap that sh*t!
def lookup(o: Option[Map[String, String]], k: String) =
o.flatMap(_ get k).map(Ok(_)).getOrElse(BadRequest)
If you're using Scala 2.10 you can fold over the Option:
def lookup(o: Option[Map[String, String]], k: String) =
o.flatMap(_ get k).fold(BadRequest)(Ok(_))
(for (params <- myOption; value <- params.get(key)) yield Ok(value)).getOrElse(BadRequest)
You should be able to do this using a couple of higher-order functions. I think this does what you want:
myOption.collect {
case m if (m contains key) => Ok(m(key))
} getOrElse BadRequest
collect takes a partial function, and the getOrElse handles the case where the partial function returned None, which translates it to your BadRequest case.

Optional function parameter with generic return type

How would you implement class that parses some input via regex and transforms founded string to some other type? My approach is:
class ARegex[T](regex:Regex, reform:Option[String => T]){
def findFirst(input:String):Option[T] = {
(regex.findFirstIn(input), reform) match{
case (None, _) => None
case (Some(s), None) => Some(s) // this won't compile because of type mismatch
case (Some(s), Some(fun)) => Some(fun(s))
}
}
}
class BRegex[T](regex:Regex, reform:Option[String => T]) {
def findFirst(input:String) = { //returns Option[Any] - erasure
(regex.findFirstIn(input), reform) match{
case (None, _) => None
case (Some(s), None) => Some(s)
case (Some(s), Some(fun)) => Some(fun(s))
}
}
}
We can solve this problem by eliminating the Option part of the reform's type, and using a different mechanism to indicate that we don't want to change the match in any way. This mechanism is to use identity as a default parameter or pass identity when you don't want the type to change.
class ARegex[T](regex:Regex, reform:String => T = identity[String](_)){
def findFirst(input:String):Option[T] = {
regex.findFirstIn(input) match{
case None => None
case Some(s) => Some(reform(s))
}
}
}
new ARegex("something".r).findFirst("something else") //returns Option[String]
new ARegex("3".r, {x=>x.toInt}).findFirst("number 3") //returns Option[Int]
Well, the problem is the type mismatch, because you are returning either a String or a T, which, of course, are unified at Any. You can't say you are going to return Option[T] and then return Option[String].
Other than that, a simplified version of that code is this:
class ARegex[T](regex: Regex, reform: Option[String => T]) {
def findFirst(input: String): Option[Any] =
regex findFirstIn input map { s => reform map (_(s)) getOrElse s }
}
You could return an Option[Either[String, T]], though. The code would look like this:
class ARegex[T](regex: Regex, reform: Option[String => T]) {
def findFirst(input: String): Option[Either[String, T]] =
regex findFirstIn input map { s => reform map (_(s)) toRight s }
}
Why is reform Option[String => T] instead of just String => T? If you don't pass in a mechanism for creating an instance of your desired type, there's no mechanism for the runtime system to actually create the appropriate object. If you really need to pass in an Option[String => T] then your second case should simply return None.
Also, flatMap is your friend, and will give you the correct behavior (i.e. if reform is None, the method returns None.
class RegexExtractor[T](regex: Regex, reform: Option[String => T]) {
def findFirst(input: String): Option[T] = reform.flatMap(f => regex.findFirstIn(input).map(f))
}