scala implicit identity - scala

Take a database transaction:
def create(model: Model, orderNum: String) = {
db.handle withSession { implicit ss: Session=>
ss.withTransaction { // auto commit now set to false
val result = for {
uid <- repo.user.create(model)
mid <- repo.membership.create(uid)
oid <- repo.orders.create(model, uid, orderNum)
} yield uid
result fold(
e=> { ss.rollback; Left(e) },
s=> { Cache.remove("member.directory"); Right(s) }
)
}
}
}
If the repository user create implementation takes an implicit Session, is it the same Session as the withTransaction enabled Session above, or is implicit value "is a" rather than "is the" identity?
def create[T <: User](t: T)(implicit ss: Session) = // what Session is this?
for {
uid <- either( Users.insert( Users(t) ), i18n("user not created") )
ur <- either( UserRoles.insert( UserRole(uid, t.role) ), i18n("user role not created") )
} yield uid
I could pass in the Session explicitly repo.user.create(model)(ss) and have create take an explicit Session, but am curious to know if more concise/convenient implicit approach provides the same outcome, the transaction enabled session.

If I correctly understood you, you are using ScalaQuery and you want to have your method working also when the user provide the session from outside.
def withSession
[T] (f: ⇒ T): T Run the supplied thunk with a new session and
automatically close the session at the end.
def withSession [T] (f:
(Session) ⇒ T): T Run the supplied function with a new session and
automatically close the session at the end.
Both of these are creating a new transaction, so the way I would go is to use an Optional[Session] as implicit and default it to None
def onProvidedOrCreatedSession[K](f: Session => K)(session:Option[Session]) = {
session match {
case Some(s) => f(s)
case None => db.withSession { f }
}
}
def create(model: Model, orderNum: String)(implicit session:Option[Session]=None){
onProvidedOrCreatedSession(
implicit s => s.withTransaction { val x = 10 }
)(session)
}

Not sure if the following is a faithful abstraction of what you are trying to do, but I hope it helps:
class Store(var x: Int) {
def flip() { x = -x }
}
object M1 {
implicit val store2 = new Store(2)
def create(s: String) = {
implicit val store3 = new Store(3)
{ implicit store: Store =>
println("[M1.create] store.x = " + store.x)
store.flip()
M2.create("tummy")
println("[M1.create] store.x = " + store.x)
}
}
}
object M2 {
implicit val store4 = new Store(4)
def create(s: String)(implicit store: Store) = {
println("[M2.create] store.x = " + store.x)
store.flip()
}
}
M1.create("dummy")(new Store(1))
The output is:
[M1.create] store.x = 1
[M2.create] store.x = -1
[M1.create] store.x = 1
The new Store(1) that is explicitly passed to M1.create is forwarded to M2.create
The additional implicit stores store2, store3, store4 are apparently ignored by the compiler.

Related

How to properly use IO and OptionT in service layer in for-comprehension?

I have a simple repository interface with CRUD operations (probably, it is a bad idea to pass implicit session as parameter in general trait):
trait Repository[Entity, PK] {
def find(pk: PK)(implicit session: DBSession): OptionT[IO, Entity]
def insert(e: Entity)(implicit session: DBSession): IO[Entity]
def update(e: Entity)(implicit session: DBSession): IO[Entity]
def delete(pk: PK)(implicit session: DBSession): IO[Int]
def findAll()(implicit session: DBSession): IO[List[Entity]]
}
And i want to use it like this:
for {
_ <- repository.insert(???)
_ <- repository.delete(???)
v <- repository.find(???).value
_ <- someFunctionReliesOnReturnedValue(v)
} yield (???)
Also, i want to stop execution if v is None and rollback transaction if there is any error (i use scalikejdbc). So, as i think, i have to do it in my service layer like this (+ wrap it into Try or something like this to cacth business exception):
def logic(???) = {
DB localTx {
implicit session => {
(for {
_ <- repository.insert(???)
_ <- repository.delete(???)
v <- repository.find(???).value
_ <- someFunctionReliesOnReturnedValue(v)
} yield (???)).unsafeRunSync() // to rollback transaction if there is any error
}
}
}
The problem is here: someFunctionReliesOnReturnedValue(v). It can be an arbitrary function which accepts Entity not Option[Entity]. How can i convert result of OptionT[IO, Entity] to IO[Entity] and save semantic of Option[]?
Is it correct approach or i've mistaken somewhere?
import java.nio.file.{Files, Paths}
import cats.data.OptionT
import cats.effect.IO
import scalikejdbc._
import scala.util.Try
case class Entity(id: Long, value: String)
object Entity extends SQLSyntaxSupport[Entity] {
override def tableName: String = "entity"
override def columnNames: Seq[String] = Seq("id", "value")
def apply(g: SyntaxProvider[Entity])(rs: WrappedResultSet): Entity = apply(g.resultName)(rs)
def apply(r: ResultName[Entity])(rs: WrappedResultSet): Entity =
Entity(rs.long(r.id), rs.string(r.value))
}
trait Repository[Entity, PK] {
def find(pk: PK)(implicit session: DBSession): OptionT[IO, Entity]
def insert(e: Entity)(implicit session: DBSession): IO[Entity]
}
class EntityRepository extends Repository[Entity, Long] {
private val alias = Entity.syntax("entity")
override def find(pk: Long)(implicit session: DBSession): OptionT[IO, Entity] = OptionT{
IO{
withSQL {
select(alias.resultAll).from(Entity as alias).where.eq(Entity.column.id, pk)
}.map(Entity(alias.resultName)(_)).single().apply()
}
}
override def insert(e: Entity)(implicit session: DBSession): IO[Entity] = IO{
withSQL {
insertInto(Entity).namedValues(
Entity.column.id -> e.id,
Entity.column.value -> e.value,
)
}.update().apply()
e
}
}
object EntityRepository {
def apply(): EntityRepository = new EntityRepository()
}
object Util {
def createFile(value: String): IO[Unit] = IO(Files.createDirectory(Paths.get("path", value)))
}
class Service {
val repository = EntityRepository()
def logic(): Either[Throwable, Unit] = Try {
DB localTx {
implicit session => {
val result: IO[Unit] = for {
_ <- repository.insert(Entity(1, "1"))
_ <- repository.insert(Entity(2, "2"))
e <- repository.find(3)
_ <- Util.createFile(e.value) // error
//after this step there is possible more steps (another insert or find)
} yield ()
result.unsafeRunSync()
}
}
}.toEither
}
object Test extends App {
ConnectionPool.singleton("jdbc:postgresql://localhost:5433/postgres", "postgres", "")
val service = new Service()
service.logic()
}
Table:
create table entity (id numeric(38), value varchar(255));
And i got compile error:
Error:(69, 13) type mismatch; found : cats.effect.IO[Unit]
required: cats.data.OptionT[cats.effect.IO,?]
_ <- Util.createFile(e.value)
In general, you should convert all of your different results to your "most general" type that has a monad. In this case, that means you should use OptionT[IO, A] throughout your for-comprehension by converting all of those IO[Entity] to OptionT[IO, Entity] with OptionT.liftF:
for {
_ <- OptionT.liftF(repository.insert(???))
_ <- OptionT.liftF(repository.delete(???))
v <- repository.find(???)
_ <- someFunctionReliesOnReturnedValue(v)
} yield (???)
If you had an Option[A] you could use OptionT.fromOption[IO]. The issues come from trying to mix monads within the same for-comprehension.
This will already stop execution if any of these result in a None. As for rolling back the transaction, that depends on how your DB interaction library works, but if it handles exceptions by rolling back, then yes, unsafeRunSync will work. If you also want it to roll back by throwing an exception when the result is None, you could do something like:
val result: OptionT[IO, ...] = ...
result.value.unsafeRunSync().getOrElse(throw new FooException(...))

Casting a Map[String,String] to a case class User(id, name) using implicit method

I've seen examples of how to do the conversion of a Map[String,String] such as ("id"->"1", "name"->"andre") to a case class User(id:String,name:String), but they involve calling a method mapToUser - for example:
val r = User.mapToUser(u.get)
println("information for " + r.name + " with id " + r.id)
case class User(id: String, name: String)
object User {
implicit def userToMap(user: User): Map[String, String] = {
Map("id" -> user.id, "name" -> user.name)
}
implicit def mapToUser(m: Map[String, String]) : User = {
User(m.get("id").get, m.get("name").get)
}
def unapply(arg: Map[String,String]): User = User(arg.get("id").get, arg.get("name").get)
}
Scala understands well on how to use the first implicit conversion of User to Map[String,String] has I don't have to call userToMap but don't understand why it's failing doing the conversion like for example
var r = u.get.asInstanceOf[User]. It says it can't cast the value to User - I tried .to[User] also and that says
Error:(73, 24) User takes no type parameters, expected: one
val r = u.get.to[User]
Full code:
import scredis._
import scala.concurrent.Future
import scala.util.{Failure, Success}
case class User(id: String, name: String)
object User {
implicit def userToMap(user: User): Map[String, String] = {
Map("id" -> user.id, "name" -> user.name)
}
implicit def mapToUser(m: Map[String, String]) : User = {
User(m.get("id").get, m.get("name").get)
}
def unapply(arg: Map[String,String]): User = User(arg.get("id").get, arg.get("name").get)
}
object RedisClient {
val redis = new Redis(host="192.168.122.2", passwordOpt = Some("privatemachine"))
import redis.dispatcher
def save(key:String, x : User) : Unit = {
x.foreach {
f => redis.hSet(key, f._1, f._2).onComplete{
case Success(content) => None
case Failure(e) => e.printStackTrace()
}
}
}
def get(key:String) : Future[Option[Map[String,String]]] = {
val result = redis.hGetAll(key)
result.onComplete {
case Success(content) => {
println(" --> " + content)
}
case Failure(e) => e.printStackTrace()
}
result
}
}
object Run extends App {
val redis = new Redis(host="192.168.122.2", passwordOpt = Some("privatemachine"))
import redis.dispatcher
redis.hSet("my-hash", "maker", "BMW")
redis.hGetAll("my-hash") onComplete {
case Success(content) => {
println(content)
}
case Failure(e) => e.printStackTrace()
}
val u1 = User("1", "andre")
RedisClient.save("user_1", u1)
val userResult = RedisClient.get("user_1")
userResult.map {
u =>
//val r = User.mapToUser(u.get)
val r = u.get.to[User]
println("information for " + r.name + " with id " + r.id)
}.onFailure{ case x => println("Look here : " + x )}
}
You don't have to call any of the implicit methods explicitly, nor should you use casting. Implicit conversions can be applied simply by trying to assign an instance of one type into a variable/value of another type given the matching conversion.
So in your case, using these conversions would be done as follows:
val map = Map("id" -> "1", "name" -> "2")
val u: User = map // implicit conversion into User
val m: Map[String, String] = u // implicit conversion back into Map

Creating generic update function for Slick 3.1.1

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

Converting blocking code to using scala futures

My old code looks something like below, where all db calls blocking.
I need help converting this over to using Futures.
def getUserPoints(username: String): Option[Long]
db.getUserPoints(username) match {
case Some(userPoints) => Some(userPoints.total)
case None => {
if (db.getSomething("abc").isEmpty) {
db.somethingElse("asdf") match {
case Some(pointId) => {
db.setPoints(pointId, username)
db.findPointsForUser(username)
}
case _ => None
}
} else {
db.findPointsForUser(username)
}
}
}
}
My new API is below where I am returning Futures.
db.getUserPoints(username: String): Future[Option[UserPoints]]
db.getSomething(s: String): Future[Option[Long]]
db.setPoints(pointId, username): Future[Unit]
db.findPointsForUser(username): Future[Option[Long]]
How can I go about converting the above to use my new API that uses futures.
I tried using a for-compr but started to get wierd errors like Future[Nothing].
var userPointsFut: Future[Long] = for {
userPointsOpt <- db.getUserPoints(username)
userPoints <- userPointsOpt
} yield userPoints.total
But it gets a bit tricky with all the branching and if clauses and trying to convert it over to futures.
I would argue that the first issue with this design is that the port of the blocking call to a Future should not wrap the Option type:
The blocking call:
def giveMeSomethingBlocking(for:Id): Option[T]
Should become:
def giveMeSomethingBlocking(for:Id): Future[T]
And not:
def giveMeSomethingBlocking(for:Id): Future[Option[T]]
The blocking call give either a value Some(value) or None, the non-blocking Future version gives either a Success(value) or Failure(exception) which fully preserves the Option semantics in a non-blocking fashion.
With that in mind, we can model the process in question using combinators on Future. Let's see how:
First, lets refactor the API to something we can work with:
type UserPoints = Long
object db {
def getUserPoints(username: String): Future[UserPoints] = ???
def getSomething(s: String): Future[UserPoints] = ???
def setPoints(pointId:UserPoints, username: String): Future[Unit] = ???
def findPointsForUser(username: String): Future[UserPoints] = ???
}
class PointsNotFound extends Exception("bonk")
class StuffNotFound extends Exception("sthing not found")
Then, the process would look like:
def getUserPoints(username:String): Future[UserPoints] = {
db.getUserPoints(username)
.map(userPoints => userPoints /*.total*/)
.recoverWith{
case ex:PointsNotFound =>
(for {
sthingElse <- db.getSomething("abc")
_ <- db.setPoints(sthingElse, username)
points <- db.findPointsForUser(username)
} yield (points))
.recoverWith{
case ex: StuffNotFound => db.findPointsForUser(username)
}
}
}
Which type-checks correctly.
Edit
Given that the API is set in stone, a way to deal with nested monadic types is to define a MonadTransformer. In simple words, let's make Future[Option[T]] a new monad, let's call it FutureO that can be composed with other of its kind. [1]
case class FutureO[+A](future: Future[Option[A]]) {
def flatMap[B](f: A => FutureO[B])(implicit ec: ExecutionContext): FutureO[B] = {
val newFuture = future.flatMap{
case Some(a) => f(a).future
case None => Future.successful(None)
}
FutureO(newFuture)
}
def map[B](f: A => B)(implicit ec: ExecutionContext): FutureO[B] = {
FutureO(future.map(option => option map f))
}
def recoverWith[U >: A](pf: PartialFunction[Throwable, FutureO[U]])(implicit executor: ExecutionContext): FutureO[U] = {
val futOtoFut: FutureO[U] => Future[Option[U]] = _.future
FutureO(future.recoverWith(pf andThen futOtoFut))
}
def orElse[U >: A](other: => FutureO[U])(implicit executor: ExecutionContext): FutureO[U] = {
FutureO(future.flatMap{
case None => other.future
case _ => this.future
})
}
}
And now we can re-write our process preserving the same structure as the future-based composition.
type UserPoints = Long
object db {
def getUserPoints(username: String): Future[Option[UserPoints]] = ???
def getSomething(s: String): Future[Option[Long]] = ???
def setPoints(pointId: UserPoints, username:String): Future[Unit] = ???
def findPointsForUser(username: String): Future[Option[Long]] = ???
}
class PointsNotFound extends Exception("bonk")
class StuffNotFound extends Exception("sthing not found")
def getUserPoints2(username:String): Future[Option[UserPoints]] = {
val futureOpt = FutureO(db.getUserPoints(username))
.map(userPoints => userPoints /*.total*/)
.orElse{
(for {
sthingElse <- FutureO(db.getSomething("abc"))
_ <- FutureO(db.setPoints(sthingElse, username).map(_ => Some(())))
points <- FutureO(db.findPointsForUser(username))
} yield (points))
.orElse{
FutureO(db.findPointsForUser(username))
}
}
futureOpt.future
}
[1] with acknowledgements to http://loicdescotte.github.io/posts/scala-compose-option-future/

scala chaining Trys with managed resource that needs finally/close()

For practice, I've gotten some straightforward JDBC stuff from like 100 lines down to this, but it won't typecheck. Any ideas? Better approaches?
def withResultSet[T](sql: String, f: ResultSet => T)(implicit info: ConnectionInfo): Try[T] = {
for {
conn <- Try(connect(info))
stmt <- Try(conn.createStatement()) orElse { case err: SQLException => {conn.close(); err} }
results <- Try(stmt.executeQuery(sql)) orElse { case err: SQLException => { conn.close(); stmt.close(); err }}
} yield f(results)
}
and my error is
missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: scala.util.Try[?]
stmt <- Try(conn.createStatement()) orElse { case err: SQLException => {conn.close(); err} }
^
I don't know that Try is the right tool to dispose of resources once they are no longer needed. At least I cannot see an obvious way to do it. You may want to look at https://github.com/jsuereth/scala-arm. Then your code may look like this:
def withResultSet[T](sql: String, f: ResultSet => T)(
implicit info: ConnectionInfo): ManagedResource[T] = for {
conn <- managed(connect(info))
stmt <- managed(conn.createStatement())
results <- managed(stmt.executeQuery(sql))
} yield f(results)
You can keep using map or flatMap (or for comprehension) to work with possibly other resources and at the end you can get an Option or an Either out of it and at the same time close everything that needs to be closed.
You can split your function into different parts, one for each resource type, and use it together with a separate resource manager trait.
The generalized resource manager (can be used with files etc. as well):
trait ResourceManager {
def withResource[T <: {def close()}, R](resource: T)(code: (T) => R): R = {
try {
code(resource)
} finally {
import scala.language.reflectiveCalls
resource.close()
}
}
is mixed into something like this
class Db extends ResourceManager {
private def getConnection = ...
def withConnection[T](f: (Connection) => T): T = {
withResource(getConnection) {
conn =>
f(conn)
}
}
def withStatement[T](f: (Statement) => T): T = {
withConnection {
conn =>
withResource(conn.createStatement()) {
stmnt =>
f(stmnt)
}
}
}
def withResultSet[T](selectSqlCmd: String)(f: (ResultSet) => T): T = {
withStatement {
stmnt => {
withResource(stmnt.executeQuery(selectSqlCmd)) {
rs =>
f(rs)
}
}
}
}
}
which stacks the resources, and gives entry points at each level.
On top of this another level
def mapResultSet[T, C <: Iterable[T]](selectSqlCmd: String)
(f: (ResultSet) => T)
(implicit cbf: CanBuildFrom[Nothing, T, C]): C = {
withResultSet(selectSqlCmd) {
rs =>
val builder = cbf()
while (rs.next()) {
builder += f(rs)
}
builder.result()
}
}
Each method goes only one step further down, the for comprehension is decomposed into separate functions, and the Try does not interfere.
Use it like this:
val db = new Db()
val result = db.mapResultSet("select * from pg_user")(rs => rs.getString(1))
to read from the database in a single line.
recoverWith is what i needed; you don't actually need to recover you can just rethrow.
def withResultSet[T](sql: String, f: ResultSet => T)(implicit info: ConnectionInfo): Try[T] = {
for {
conn <- Try(connect(info))
stmt <- Try(conn.createStatement()) recoverWith { case err => {conn.close(); Failure(err)} }
results <- Try(stmt.executeQuery(sql)) recoverWith { case err => { conn.close(); stmt.close(); Failure(err) }}
} yield f(results)
}