I am really new in Scala and I would like to ask a simple question.
I have a function that returns Future[Option[T]
def findOne(query: JsObject)(implicit reader: Reads[T]): Future[Option[T]] = {
Logger.debug(s"Finding one: [collection=$collectionName, query=$query]")
collection.find(query).one[T]
}
And I have to return Future[Option[PasswordInfo]].
I tried:
def find(loginInfo: LoginInfo): Future[Option[PasswordInfo]] = {
val result = find(Json.obj("loginInfo.providerID" -> loginInfo.providerID, "loginInfo.providerKey" -> loginInfo.providerKey))
result.onSuccess{
case something => Future.successful(Some(something).getOrElse(None))
}
}
My class:
case class PersistentPasswordInfo(
loginInfo: LoginInfo,
authInfo: PasswordInfo
) extends TemporalModel {
override var created: Option[DateTime] = _
override var updated: Option[DateTime] = _
override var _id: Option[BSONObjectID] = _
}
you need to map over the Future to get the Option, then map over the Option to get the PersistedPasswordInfo
def find(loginInfo: LoginInfo): Future[Option[PasswordInfo]] = {
val result = find(Json.obj("loginInfo.providerID" -> loginInfo.providerID, "loginInfo.providerKey" -> loginInfo.providerKey))
result.map(opt => opt.map(ppi => ppi.authInfo))
}
Future and Option are both Functors, they implement a method map with something like the following signature (assuming the type arg to the containing Future is A):
def map[B](f: A => B): Future[B]
so the code above changes Future by applying a function Option[PersistentPasswordInfo] => Option[PasswordInfo]
that function is created by applying map to on the contained Option with a function PersistentPasswordInfo => PasswordInfo
Related
I'm following the tutorial here: http://typelevel.org/cats/datatypes/freemonad.html and trying to modify it to work with a cache in front of the key value store. This is what I've come up with so far but I'm getting a compiler error with valueGetOperation. I understand why I get the compile error, I just don't understand how to work around it. What's the best practice for conditional behavior when using a free monad?
import cats.data.Coproduct
import cats.free.{Free, Inject}
object KvStore {
sealed trait KvOp[A]
case class Get[T](key: String) extends KvOp[Option[T]]
case class Put[T](key: String, value: T) extends KvOp[Unit]
case class Delete[T](key: String) extends KvOp[Unit]
}
object CacheStore {
sealed trait CacheOp[A]
case class Get[T](key: String) extends CacheOp[Option[T]]
case class Put[T](key: String, value: T) extends CacheOp[Unit]
case class Delete[T](key: String) extends CacheOp[Unit]
}
type WriteThruCache[A] = Coproduct[KvStore.KvOp, CacheStore.CacheOp, A]
class KvOps[F[_]](implicit I: Inject[KvStore.KvOp, F]) {
import KvStore._
def get[T](key: String): Free[F, Option[T]] = Free.inject[KvOp, F](Get(key))
def put[T](key: String, value: T): Free[F, Unit] = Free.inject[KvOp, F](Put(key, value))
def delete[T](key: String): Free[F, Unit] = Free.inject[KvOp, F](Delete(key))
}
object KvOps {
implicit def kvOps[F[_]](implicit I: Inject[KvStore.KvOp, F]): KvOps[F] = new KvOps[F]
}
class CacheOps[F[_]](implicit I: Inject[CacheStore.CacheOp, F]) {
import CacheStore._
def get[T](key: String): Free[F, Option[T]] = Free.inject[CacheOp, F](Get(key))
def put[T](key: String, value: T): Free[F, Unit] = Free.inject[CacheOp, F](Put(key, value))
def delete[T](key: String): Free[F, Unit] = Free.inject[CacheOp, F](Delete(key))
}
object CacheOps {
implicit def cacheOps[F[_]](implicit I: Inject[CacheStore.CacheOp, F]): CacheOps[F] = new CacheOps[F]
}
def valueWriteOperation[T](implicit Kv: KvOps[WriteThruCache], Cache: CacheOps[WriteThruCache]): ((String, T) => Free[WriteThruCache, Unit]) = {
(key: String, value: T) =>
for {
_ <- Kv.put(key, value)
_ <- Cache.put(key, value)
} yield ()
}
// This is where I'm stuck
// desired behavior: If the value isn't in the cache, load it from the kv store and put it in the cache
def valueGetOperation[T](implicit Kv: KvOps[WriteThruCache], Cache: CacheOps[WriteThruCache]): ((String) => Free[WriteThruCache, Option[T]]) = {
(key: String) =>
for {
cacheOption <- Cache.get[T](key)
kvOption <- Kv.get[T](key) if cacheOption.isEmpty // value withFilter is not a member of cats.free.Free[A$A39.this.WriteThruCache,Option[T]]
} yield cacheOption.orElse(kvOption)
}
As you know in for comprehension, when you use if it is desugared by compiler to calling withFilter method, and if it's not accessible it falls back to filter method. If they are not implemented you will receive compiler error.
However you can simply use if else!
for {
booleanValue <- myfreeAlbebra.checkCondidtion(arg1, arg2)
valueToReturn <- if (booleanValue) {
myfreeAlbebra.someValue
} else {
myfreeAlbebra.someOtherValue
}
} yield valueToReturn
alternatively you can do something like:
for {
booleanValue <- myfreeAlbebra.checkCondidtion(arg1, arg2)
valueToReturnOpt <- myfreeAlbebra.someValue
fallbackValue <- myfreeAlbebra.someOtherValue
} yield valueToReturnOpt.getOrElse(fallbackValue)
The formar one will assign value to valueToReturn depending on booleanValue. As such only one branch will be interpreted. The later will evaluate both values and return one of them depending on whether or not valueToReturnOpt will be empty.
Personally I would try something like:
def valueGetOperation[T](implicit Kv: KvOps[WriteThruCache], Cache: CacheOps[WriteThruCache]): ((String) => Free[WriteThruCache, Option[T]]) = {
(key: String) =>
for {
cacheOption <- Cache.get[T](key)
returnedValue <- if (cacheOption.isEmpty) Cache.get[T](key) else Kv.get[T](key)
} yield returnedValue
}
Following Mateusz' suggestions, this is what I came up with:
def withFallback[A[_], T](loadedValue: Option[T], fallback: => Free[A, Option[T]]): Free[A, Option[T]] = {
if(loadedValue.isDefined) {
Free.pure[A, Option[T]](loadedValue)
} else {
fallback
}
}
def valueGetOperation[T](implicit Kv: KvOps[WriteThruCache], Cache: CacheOps[WriteThruCache]): ((String) => Free[WriteThruCache, Option[T]]) = {
(key: String) =>
for {
cachedOption <- Cache.get[T](key)
actualValue <- withFallback[WriteThruCache, T](cachedOption, fallback = Kv.get[T](key))
} yield actualValue
}
If there's a standard construct to implement withFallback I'd be glad to know about it.
You could also use OptionT#orElse.
import cats.data.OptionT
type KV[A] = Free[WriteThruCache, A]
def valueGetOperation[T](
implicit
Kv: KvOps[WriteThruCache],
Cache: CacheOps[WriteThruCache]
): String => KV[Option[T]] =
key => OptionT[KV, T](Cache.get[T](key)).orElse(OptionT[KV, T](Kv.get[T](key))).value
Or OptionT#orElseF :
def valueGetOperation[T](
implicit
Kv: KvOps[WriteThruCache],
Cache: CacheOps[WriteThruCache]
): String => KV[Option[T]] =
key => OptionT[KV, T](Cache.get[T](key)).orElseF(Kv.get[T](key)).value
Note that with the -Ypartial-unification flag in Scala 2.12 you don't need the KV type alias and you can write OptionT(...) instead of OptionT[KV, T](...).
I have this fucntion:
def updateInvoiceAdminStatusField(id: Int, newAdminStatus: AdminStatus): Future[Int] = {
db.run {
val adminStatus: Query[Rep[AdminStatus], AdminStatus, Seq] = InvoicesTable.filter(invoice => invoice.id === id).map(invoice => invoice.status)
adminStatus.update(newAdminStatus)
}
}
I thought of making it generic:
def updateInvoiceField[T](id: Int, fieldExtractor: (Invoices) => Rep[T], newValue: T): Future[Int] = {
db.run {
val adminStatus = InvoicesTable.filter(invoice => invoice.id === id).map(invoice => {
fieldExtractor(invoice)
})
adminStatus.update(newValue)
}
}
But this does not compile. Can somebody assist?
It's nearly good. With small changes like below it should work:
// I added below T: ColumnType
def updateInvoiceField[T: ColumnType](id: Int, fieldExtractor: (Invoices) => Rep[T], newValue: T): Future[Int] = {
db.run {
val adminStatus = InvoicesTable.filter(invoice => invoice.id === id).map(invoice => {
fieldExtractor(invoice)
})
adminStatus.update(newValue)
}
}
Notice I added this : ColumnType which basically mean you need to have proper implicit in scope - specifically the one that would convert T => ColumnType[T]. That's simply because otherwise T could be anything and Slick wouldn't be able to figure out how to convert it.
So for things like String, Int etc. you obviously have proper conversations already in place (imported from api in profile/driver). For you custom types you would need to use MappedColumnType to have proper conversion supplied. Example below (typesafe ID):
implicit def columnType[T]: BaseColumnType[Id[T]] =
MappedColumnType.base[Id[T], Long](toLong, fromLong)
private def fromLong[T](dbId: Long): Id[T] = Id(dbId)
private def toLong[T](id: Id[T]): Long = id.value
More specifically, I have:
case class Key (key: String)
abstract class abstr {
type MethodMap = PartialFunction[Key, String => Unit]
def myMap: MethodMap // abstract
def useIt (key: Key, value: String) = {
val meth = myMap(key)
meth(value)
}
def report = {
for (key <- myMap.keySet) // how to do this
println("I support "+key)
}
}
I use it like this:
class concrete extends abstr {
var one: Boolean
def method1(v: String): Unit = ???
def method2(v: String): Unit = ???
def map1: MethodMap = {
case Key("AAA") => method1
}
def map2: MethodMap = {
case Key("AAA") => method2
}
override def myMap: MethodMap = if (one) map1 else map2
}
Of course, this is somewhat simplified, but the report function is necessary.
Some history: I first had it implemented using Map but then I changed it to PartialFunction in order to support the following override def myMap: MethodMap = if (one) map1 else map2.
Any suggestion to refactor my code to support everything is also appreciated.
No. PartialFunction can be defined (and often is) on infinite sets. E.g. what do you expect report to return in these situations:
class concrete2 extends abstr {
def myMap = { case Key(_) => ??? }
}
or
class concrete2 extends abstr {
def myMap = { case Key(key) if key.length > 3 => ??? }
}
? If you have a finite list of values you are interested in, you can do
abstract class abstr {
type MethodMap = PartialFunction[Key, String => Unit]
def myMap: MethodMap // abstract
val keys: Seq[Key] = ...
def report = {
for (key <- keys if myMap.isDefined(key))
println("I support "+key)
}
}
Some history: I first had it implemented using Map but then I changed it to PartialFunction in order to support the last line in second part.
Why? This would work just as well with Map.
In your solution, is there any way to define the domain of the partial function to be the finite set keys
def f: MethodMap = { case key if keys.contains(key) => ... }
Of course, the domain isn't part of the type.
Here is the code:
def transform1(f: String => String): Unit = {
val s = getString
f.andThen(putString)(s)
}
def transform2(f: String => Option[String]): Unit = {
val s = getString
f(s).foreach(putString(_))
}
How do you express these two ideas in one single function?
Method overloading does not work and seems discouraged by the community.
I didn't understand that why anyone may want this but here is a way to do it:
def transform(f: Either[(String => String), (String => Option[String])]: Unit = f match {
case Left(f) => // do transform1 here
case Right(f) => //do transform2 here
}
As I said at the begining you probably shouldn't want to do this; perhaps you should directly ask what you want.
The pattern to avoid overloading is to convert disparate arguments to a common, specific type. There could be any number of such conversions.
Not sure this is the most compelling example, however.
object X {
trait MapFlat[-A, +B] { def apply(x: A): B }
implicit class mapper[A](val f: A => A) extends MapFlat[A, A] {
override def apply(x: A) = {
val res = f(x)
println(res)
res
}
}
implicit class flatmapper[A](val f: A => Option[A]) extends MapFlat[A, Option[A]] {
override def apply(x: A) = {
val res = f(x)
res foreach println
res
}
}
def f[B](g: MapFlat[String, B]) = {
g("abc")
}
}
object Test extends App {
import X._
f((s: String) => s)
f((s: String) => Some(s))
}
One way to do it will be type classes, here's a sample -
trait Transformer[T] {
def transform(foo: String => T)
}
object Transformer {
implicit object StringTransformer extends Transformer[String] {
override def transform(foo: (String) => String): Unit = ??? // Your logic here
}
implicit object OptStringTransformer extends Transformer[Option[String]] {
override def transform(foo: (String) => Option[String]): Unit = ??? // Your logic here
}
}
class SampleClass {
def theOneTransformYouWant[T: Transformer](f: String => T) = {
implicitly[Transformer[T]].transform(f)
}
def canUseBothWays(): Unit = {
theOneTransformYouWant((s: String) => s)
theOneTransformYouWant((s: String) => Some(s))
}
}
Another way would be the magnet pattern
http://spray.io/blog/2012-12-13-the-magnet-pattern/
sealed trait TransformationMagnet {
def apply(): Unit
}
object TransformationMagnet {
implicit def fromString(f: String => String): TransformationMagnet =
new TransformationMagnet {
def apply(): Unit = ??? // Your code goes here
}
implicit def fromOptString(f: String => Option[String]): TransformationMagnet =
new TransformationMagnet {
def apply(): Unit = ??? // your code goes here
}
}
class SampleClass {
def theOneTransformYouWant(f: TransformationMagnet) = {
???
}
def hereWeUseItInBothWays(): Unit = {
theOneTransformYouWant((s: String) => s)
theOneTransformYouWant((s: String) => Some(s))
}
}
add a new parameter on the description typeOfTransform
add a conditional inside the function
if (typeOfTransform == type1){
//functionality1
}else {
//functionality2
}
Just for completeness, you can actually overload methods like this by adding implicit arguments which will always be available:
def transform(f: String => Option[String]): Unit = ...
def transform(f: String => String)(implicit d: DummyImplicit): Unit = ...
I cannot find the way to define a MongoRecord with a Map[String,String] field inside it in Lift - MongoRecord.
The Lift documentation says:
All standard Record Fields are supported. There is also support for Mongo specific types; ObjectId, UUID, Pattern, List, and Map.
How can I define Map and List fields?
I defined a BsonRecordMapField:
class BsonRecordMapField[OwnerType <: BsonRecord[OwnerType], SubRecordType <: BsonRecord[SubRecordType]]
(rec: OwnerType, valueMeta: BsonMetaRecord[SubRecordType])(implicit mf: Manifest[SubRecordType])
extends MongoMapField[OwnerType, SubRecordType](rec: OwnerType) {
import scala.collection.JavaConversions._
override def asDBObject: DBObject = {
val javaMap = new HashMap[String, DBObject]()
for ((key, element) <- value) {
javaMap.put(key.asInstanceOf[String], element.asDBObject)
}
val dbl = new BasicDBObject(javaMap)
dbl
}
override def setFromDBObject(dbo: DBObject): Box[Map[String, SubRecordType]] = {
val mapResult: Map[String, SubRecordType] = (for ((key, dboEl) <- dbo.toMap.toSeq) yield (key.asInstanceOf[String], valueMeta.fromDBObject(dboEl.asInstanceOf[DBObject]))).toMap
setBox(Full(mapResult))
}
override def asJValue = {
val fieldList = (for ((key, elem) <- value) yield JField(key, elem.asJValue)).toList
JObject(fieldList)
}
override def setFromJValue(jvalue: JValue) = jvalue match {
case JNothing | JNull if optional_? => setBox(Empty)
case JObject(fieldList) => val retrievedMap = fieldList.map {
field =>
val key = field.name
val valRetrieved = valueMeta.fromJValue(field.value) openOr valueMeta.createRecord
(key, valRetrieved)
}.toMap
setBox(Full(retrievedMap))
case other => setBox(FieldHelpers.expectedA("JObject", other))
}
}
This is the implicit query for Rogue:
class BsonRecordMapQueryField[M <: BsonRecord[M], B <: BsonRecord[B]](val field: BsonRecordMapField[M, B])(implicit mf: Manifest[B]) {
def at(key: String): BsonRecordField[M, B] = {
val listBox = field.setFromJValue(JObject(List(JField("notExisting", JInt(0)))))
val rec = listBox.open_!.head._2
new BsonRecordField[M, B](field.owner, rec.meta)(mf) {
override def name = field.name + "." + key
}
}
}
object ExtendedRogue extends Rogue {
implicit def bsonRecordMapFieldToBsonRecordMapQueryField[M <: BsonRecord[M], B <: BsonRecord[B]](f: BsonRecordMapField[M, B])(implicit mf: Manifest[B]): BsonRecordMapQueryField[M, B] = new BsonRecordMapQueryField[M, B](f) (mf)
}
You can use the at operator in map now.
What about MongoMapField?