I'm trying to combine Playframework with Cats Effect 3 Tagless Final style.
I'm stuck on the transformation to the Future. Play's Action requires either just value or Future, which I want to reach, for async processing.
def method = authed { _ =>
val program: EitherT[IO, Throwable, Int] = ???
program
}
def authed[F[_]: Async](fa: Request => F[Result]): Action = {
???
}
In cats effect 2 it was possible via _.toIO.unsafeToFuture but now it's changed.
According to the doc I must use Dispatcher. Found the initial idea on Github but a new signature is F[Future[A]]
def beforeF[F[_]: Effect, A](fa: F[A]): Future[A] = fa.ioIO.unsafeToFuture()
// Note: Using a `Dispatcher` resource is cheap - don't worry about it
def preferredAfterF[F[_]: Async, A](fa: F[A]): F[Future[A]] = Dispatcher[F].use(_.unsafeToFuture(fa))
Does anyone have success?
Contruct your routes so that they have access to a Disptacher object as a dependency, then the lifecycle will be appropriate
class MyRoute[F[_]: MonadThrow](disp: Dispatcher[F]) {
def method = authed { _ =>
val program: EitherT[IO, Throwable, Int] = ???
disp.unsafeToFuture(program.value)
}
}
(this might require some fiddling using allocated and the IORuntime's unsafeRunSync on a Dispatcher[IO] resource, depending on how your application is wired)
Related
I created two versions of my service. First one uses Futures, other one uses ZIO as an effect.
I have a simple method which use Future as a result effect:
def get(id: String)(implicit executionContext: ExecutionContext): Future[Data]
I also has some other version which uses ZIO[SomeEnv, SomeError, Data]:
def get(id: String): ZIO[SomeEnv, SomeError, Data]
Now, I need to create some kind of adapter which will return data from one version or another one:
def returnDataFromServiceVersion(version: Int) : ??? = {
if(version == 1) futureService.get(...)
else zioService.get(...)
}
The problem here is with returned type. I do not know how to convert ZIO into future or Future into ZIO to have common return type. I tried use ZIO.fromFuture{...} or toFuture() but it did not help.
My question is - how to create this returnDataFromServiceVersion method to use it with both services? I need to have common return type here.
Or maybe there is another way to solve this problem?
You have to decide whether your function returns Future or ZIO and this cannot depend on a runtime value like version in your snippet (unless of course you define the return type as AnyRef - not very useful).
If the rest of your program is based on Futures, but you would like to start introducing ZIO in some services, you can do that by executing your effects yourself using runtime.unsafeRun(effect):
val runtime = Runtime.default
def returnDataFromServiceVersion(version: Int): Future[Data] = {
runtime.unsafeRunToFuture(
zioService.get(...)
)
}
For more details refer to Running Effects in the official ZIO docs.
If I understand correctly what you want is common ZIO type which would look like this ZIO[SomeEnv, SomeError, Data]
That can be achieved using ZIO.fromFuture and mapping Throwable (which is Future failure type) to your SomeError:
case class Data(data: String)
case class SomeEnv(env: String)
case class SomeError(err: String)
object SomeError {
def fromThrowable(throwable: Throwable) = SomeError(throwable.toString)
}
def getFuture(id: String)(implicit executionContext: ExecutionContext): Future[Data] = ???
def getZIO(id: String): ZIO[SomeEnv, SomeError, Data] = ???
def returnDataFromServiceVersion(version: Int): ZIO[SomeEnv, SomeError, Data] =
if (version == 1)
ZIO
.fromFuture(executionContext => getFuture("id")(executionContext))
.mapError(SomeError.fromThrowable)
else
getZIO("id")
I am not yet super strong with the concept of effects, so some of my assumptions might be completely wrong. Please correct me whenever you see such occurrences.
I am building an application (not from scratch, but rather developing the skeleton) with scala-cats and cats-effects. The main class extends IOApp and starts a web server:
object Main extends IOApp {
override def run(args: List[String]): IO[ExitCode] =
new Application[IO]
.stream
.compile
.drain
.as(ExitCode.Success)
}
class Application[F[_]: ConcurrentEffect: Timer] {
def stream: Stream[F, Unit] =
for {
// ...
} yield ()
}
This is the first encounter with the F[_] type. The : ConcurrentEffect: Timer context-bound says that there are two instances declared somewhere: ConcurrentEffect[F[_]] and Timer[F[_]] if I understand that correctly.
Skipping the HTTP layer of application, route handler uses the service I am trying to implement with two different variations - DummyService and LiveService - Dummy is supposed to always return the constant (dummy) data, whilst Live one sends a REST request and parses the JSON response to internal domain models:
trait CurrencyConverterAlgebra[F[_]] {
def get(currency: Currency): F[Error Either ExchangeRate]
}
class DummyCurrencyConverter[F[_]: Applicative] extends CurrencyConverterAlgebra[F] {
override def get(currency: Currency): F[Error Either ExchangeRate] =
ExchangeRate(BigDecimal(100)).asRight[Error].pure[F]
}
object DummyCurrencyConverter {
// factory method
def apply[F[_]: Applicative]: CurrencyConverterAlgebra[F] = new DummyCurrencyConverter[F]()
}
So far so good. The only bit of mystery to me is why we have to have that Applicative implicit.
But now I try to implement the Live service which will also make use of the Cache (to throttle the requests):
trait Cache[F[_], K, V] {
def get(key: K): F[Option[V]]
def put(key: K, value: V): F[Unit]
}
private class SelfRefreshingCache[F[_]: Monad, K, V]
(state: Ref[F, Map[K, V]], refresher: Map[K, V] => F[Map[K, V]], timeout: FiniteDuration) extends Cache[F, K, V] {
override def get(key: K): F[Option[V]] =
state.get.map(_.get(key))
override def put(key: K, value: V): F[Unit] =
state.update(_.updated(key, value))
}
object SelfRefreshingCache {
def create[F[_]: Monad: Sync, K, V]
(refresher: Map[K, V] => F[Map[K, V]], timeout: FiniteDuration)
(implicit timer: Timer[F]): F[Cache[F, K, V]] = {
def refreshRoutine(state: Ref[F, Map[K, V]]): F[Unit] = {
val process = state.get.flatMap(refresher).map(state.set)
timer.sleep(timeout) >> process >> refreshRoutine(state)
}
Ref.of[F, Map[K, V]](Map.empty)
.flatTap(refreshRoutine)
.map(ref => new SelfRefreshingCache[F, K, V](ref, refresher, timeout))
}
}
Here, SelfRefreshingCache requires Sync instance to be present - otherwise I am getting an error saying it is not defined when trying to construct a Ref instance. Also, in order to be able to use the state.get.map(_.get(key)) statement in the SelfRefreshingCache class, I have to use the Monad constraint, presumingly to tell Scala that my F[_] type inside Cache can be flatMap-ped.
In my Live service I am trying to use this service as follows:
class LiveCurrencyConverter[F[_]: Monad](cache: F[Cache[F, Currency, ExchangeRate]]) extends Algebra[F] {
override def get(currency: Currency): F[Error Either ExchangeRate] =
cache.flatMap(_.get(currency))
.map(_.toRight(CanNotRetrieveFromCache()))
}
object LiveCurrencyConverter {
def apply[F[_]: Timer: ConcurrentEffect]: Algebra[F] = {
val timeout = Duration(30, TimeUnit.MINUTES)
val cache = SelfRefreshingCache.create[F, Currency, ExchangeRate](refreshExchangeRatesCache, timeout)
// ---> could not find implicit value for evidence parameter of type cats.Monad[Nothing]
new LiveCurrencyConverter(cache)
}
private def refreshExchangeRatesCache[F[_]: Monad: ConcurrentEffect](existingRates: Map[Currency, ExchangeRate]): F[Map[Currency, ExchangeRate]] = ???
}
Currently, I am stuck at getting compilation error saying I don't have an instance of Monad[Nothing]. And this is where my whole story Main turns around: if I understand the whole concept behind type constraints (requiring implicits to be defined in the scope of a method call), then the F[_] type should be propagated from the very Main level down to my Live service and should be something like IO. And IO has both map and flatMap methods defined. On a Live service level, the refreshExchangeRatesCache makes a REST call (using the http4s, but that should not matter) and is supposed to run on something like IO as well.
First of all, are my assumptions about context boundaries and F[_] propagation from the Main class correct? Can I then hide the IO type on the Live service level? Or how do I provide the required Monad implicit instance?
This is the first encounter with the F[] type. The :
ConcurrentEffect: Timer context-bound says that there are two
instances declared somewhere: ConcurrentEffect[F[]] and Timer[F[_]]
if I understand that correctly.
To be specific, it has to be declared inside the implicit scope.
The only bit of mystery to me is why we have to have that Applicative
implicit.
You need evidence of an Applicative[F] because your method uses pure[F] to lift
ExchangeRate onto F, where pure is defined in the Applicative typeclass:
ExchangeRate(BigDecimal(100)).asRight[Error].pure[F]
Also, in order to be able to use the state.get.map(_.get(key))
statement in the SelfRefreshingCache class, I have to use the Monad
constraint
Since you're using .map and not .flatMap, it will suffice to require an instance of Functor and not Monad for the class definition of SelfRefreshingCache. For the companion object, you'll need a Monad in order to flatMap.
First of all, are my assumptions about context boundaries and F[_]
propagation from the Main class correct?
Yes, they are. When you build your entire program in Main, and "fill in" IO where F[_] is required, the compiler will search for the existence of all the implicit evidence required from IO in scope, given that you've captured the requirements from each method invocation using context bounds or plain implicit parameters.
Can I then hide the IO type on the Live service level?
The IO is hidden in your approach, as Live only knows the "shape" of the type, i.e. F[_]. Requiring the immediate solution to your problem, the previous answer has stated you need to add F to your method call in order for the compiler to infer which type you meant to fill in refreshExchangeRatesCache.
Add the type information, in this case F, to the line
val cache = SelfRefreshingCache.create[F, Currency, ExchangeRate](refreshExchangeRatesCache[F], timeout)
I have the following classes in Scala:
class A {
def doSomething() = ???
def doOtherThing() = ???
}
class B {
val a: A
// need to enhance the class with both two functions doSomething() and doOtherThing() that delegates to A
// def doSomething() = a.toDomething()
// def doOtherThing() = a.doOtherThing()
}
I need a way to enhance at compile time class B with the same function signatures as A that simply delegate to A when invoked on B.
Is there a nice way to do this in Scala?
Thank you.
In Dotty (and in future Scala 3), it's now available simply as
class B {
val a: A
export a
}
Or export a.{doSomething, doOtherThing}.
For Scala 2, there is unfortunately no built-in solution. As Tim says, you can make one, but you need to decide how much effort you are willing to spend and what exactly to support.
You can avoid repeating the function signatures by making an alias for each function:
val doSomething = a.doSomething _
val doOtherthing = a.doOtherThing _
However these are now function values rather than methods, which may or may not be relevant depending on usage.
It might be possible to use a trait or a macro-based solution, but that depends on the details of why delegation is being used.
Implicit conversion could be used for delegation like so
object Hello extends App {
class A {
def doSomething() = "A.doSomething"
def doOtherThing() = "A.doOtherThing"
}
class B {
val a: A = new A
}
implicit def delegateToA(b: B): A = b.a
val b = new B
b.doSomething() // A.doSomething
}
There is this macro delegate-macro which might just be what you are looking for. Its objective is to automatically implement the delegate/proxy pattern, so in your example your class B must extend class A.
It is cross compiled against 2.11, 2.12, and 2.13. For 2.11 and 2.12 you have to use the macro paradise compile plugin to make it work. For 2.13, you need to use flag -Ymacro-annotations instead.
Use it like this:
trait Connection {
def method1(a: String): String
def method2(a: String): String
// 96 other abstract methods
def method100(a: String): String
}
#Delegate
class MyConnection(delegatee: Connection) extends Connection {
def method10(a: String): String = "Only method I want to implement manually"
}
// The source code above would be equivalent, after the macro expansion, to the code below
class MyConnection(delegatee: Connection) extends Connection {
def method1(a: String): String = delegatee.method1(a)
def method2(a: String): String = delegatee.method2(a)
def method10(a: String): String = "Only method I need to implement manually"
// 96 other methods that are proxied to the dependency delegatee
def method100(a: String): String = delegatee.method100(a)
}
It should work in most scenarios, including when type parameters and multiple argument lists are involved.
Disclaimer: I am the creator of the macro.
I'm designing sort of Services and faced with a design issue. Here is what I currently have:
trait Service {
def isFailed(): Boolean
def start(): Unit
def stop(): Unit
}
And in order to group Services related to each other in a group (in order to restart/recover the group, not other services) I created the following package object:
package object app {
type FaultTolerantServiceGroup = Seq[Service]
object FaultTolerantServiceGroup{
def apply(svcs: Service*): FaultTolerantServiceGroup = Seq(svcs: _*)
}
class FaultTolerantServiceGroupOps(val F: FaultTolerantServiceGroup){
def hasFailed: Boolean = F.forall(_.failed())
}
trait FaultTolerantServiceGroupSyntax{
implicit def serviceGroup2Ops(F: FaultTolerantServiceGroup) = new FaultTolerantServiceGroupOps(F)
}
}
So I added the method hasFailed to FaultTolerantServiceGroup. But I'm not sure about this decision.
Would it be better to define a typeclass, say
trait Watchable[T]{
def hasFailed(t: T): Boolean
}
And implicitly provide an instance of Watchable[FaultTolerantServiceGroup]?
In my humble opinion implicit functions become much harder to read afterwards. Even when reading my old code it can sometimes be confusing when objects have methods that appear out of the blue.
I have yet to see an instance where implicits are easier to reason about than declarative functions:
val failedGroup : FaultTolerantServiceGroup => Boolean = _.forall(_.failed())
The resulting code doesn't seem any better, or worse, than implicits but at least it's obvious where functionality is coming from:
val group : FaultTolerantServiceGroup = ???
//no implicit
val failed = failedGroup(group)
//with implicits : how does a Seq have a hasFailed method?
val failed = group.hasFailed
Explicit functions also make Iterable functions easier to read:
val groups : Iterable[FaultTolerantServiceGroup] = ???
val failedGroups = groups filter failedGroup
Scala functions has following methods for chaining:
fn1.andThen(fn2)
fn1.compose(fn2)
But how can be written this case:
I have function cleanUp() which has to be called always as last step.
And I have a bunch of other functions, like that:
class Helper {
private[this] val umsHelper = new UmsHelper()
private[this] val user = umsHelper.createUser()
def cleanUp = ... // delete user/ and other entities
def prepareModel(model: TestModel) = {
// create model on behalf of the user
}
def commitModel() = {
// commit model on behalf of the user
}
}
And some external code can use code something like this:
val help = new Helper()
help.prepareModel()
help.commitModel()
// last step should be called implicitly cleanUp
How this can be written in a functional way, that chaining will always
call cleanUp function implicitly as last step?
Note: I see it as analogue of destructor in C++. Some chaining (doesn't matter how this chain is done) fn1 andLater fn2 andLater fn3 have to call as last step cleanUp (fn1 andLater fn2 andLater fn3 andLater cleanUp). Wrong with directly writing cleanUp method is there is a big chance someone will miss this step and user will be leaked (will be stayed in database)
This is a more advanced alternative:
When you hear "context" and "steps", there's a functional pattern that directly comes to mind: Monads. Rolling up your own monad instance can simplify the user-side of putting valid steps together, while providing warranties that the context will be cleaned up after them.
Here, we are going to develop a "CleanableContext" construction that follows that pattern.
We base our construct on the most simple monad, one whose only function is to hold a value. We're going to call that Context
trait Context[A] { self =>
def flatMap[B](f:A => Context[B]): Context[B] = f(value)
def map[B](f:A => B): Context[B] = flatMap(f andThen ((b:B) => Context(b)))
def value: A
}
object Context {
def apply[T](x:T): Context[T] = new Context[T] { val value = x }
}
Then we have a CleanableContext, which is capable of "cleaning up after itself" provided some 'cleanup' function:
trait CleanableContext[A] extends Context[A] {
override def flatMap[B](f:A => Context[B]): Context[B] = {
val res = super.flatMap(f)
cleanup
res
}
def cleanup: Unit
}
And now, we have an object that's able to produce a cleanable UserContext that will take care of managing the creation and destruction of users.
object UserContext {
def apply(x:UserManager): CleanableContext[User] = new CleanableContext[User] {
val value = x.createUser
def cleanup = x.deleteUser(value)
}
}
Let's say that we have also our model and business functions already defined:
trait Model
trait TestModel extends Model
trait ValidatedModel extends Model
trait OpResult
object Ops {
def prepareModel(user: User, model: TestModel): Model = new Model {}
def validateModel(model: Model): ValidatedModel = new ValidatedModel {}
def commitModel(user: User, vmodel: ValidatedModel): OpResult = new OpResult {}
}
Usage
With that reusable machinery in place, our users can express our process in a succinct way:
import Ops._
val ctxResult = for {
user <- UserContext(new UserManager{})
validatedModel <- Context(Ops.prepareModel(user, testModel)).map(Ops.validateModel)
commitResult <- Context(commitModel(user, validatedModel))
} yield commitResult
The result of the process is still encapsulated, and can be taken "out" from the Context with the value method:
val result = ctxResult.value
Notice that we need to encapsulate the business operations into a Context to be used in this monadic composition. Note as well that we don't need to manually create nor cleanup the user used for the operations. That's taken care of for us.
Furthermore, if we needed more than one kind of managed resource, this method could be used to take care of managing additional resources by composing different contexts together.
With this, I just want to provide another angle to the problem. The plumbing is more complex, but it creates a solid ground for users to create safe processes through composition.
I think that the core of the question is "how to keep a resource within a managed context". i.e. provide users with a way to use the resource and prevent it to 'leak' outside its context.
One possible approach is to provide a functional access to the managed resource, where the API requires functions to operate over the resource in question. Let me illustrate this with an example:
First, we define the domain of our model: (I've added some subtypes of Model to make the example more clear)
trait User
trait Model
trait TestModel extends Model
trait ValidatedModel extends Model
trait OpResult
// Some external resource provider
trait Ums {
def createUser: User
def deleteUser(user: User)
}
Then we create a class to hold our specific context.
class Context {
private val ums = new Ums{
def createUser = new User{}
def deleteUser(user: User) = ???
}
def withUserDo[T](ops: User => T):T = {
val user = ums.createUser
val result = ops(user)
ums.deleteUser(user)
result
}
}
The companion object provides (some) operations on the managed resource. Users can provide their own functions as well.
object Context {
def prepareModel(model: TestModel): User => Model = ???
val validateModel: Model => ValidatedModel = ???
val commitModel: ValidatedModel => OpResult = ???
}
We can instantiate our context and declare operations on it, using a classic declaration, like:
val ctx = new Context
val testModel = new TestModel{}
val result = ctx.withUserDo{ user =>
val preparedModel = prepareModel(testModel)(user)
val validatedModel = validateModel(preparedModel)
commitModel(validatedModel)
}
Or, given the desire in the question to use functional composition, we could rewrite this as:
val result = ctx.withUserDo{
prepareModel(testModel) andThen validateModel andThen commitModel
}
Use autoClean this will automatically call cleanUp at the end.
create a HelperStuff trait which contains all the necessary functions.
Inside the Helper object create a private implementation of the HelperStuff and then have a method method called autoClean which does the work keeping the Helper instance private and safe way from the rouge users.
Helper.autoClean { helperStuff =>
//write all your code here. clean up will happen automatically
helper.foo()
helper.commitModel()
}
Here is the autoClean function for you
trait HelperStuff {
def foo(): Unit
def commitModel: Unit
def cleanUp(): Unit
}
object Helper {
private class Helper extends HelperStuff {
def foo(): Unit = println("foo")
def cleanUp(): Unit = println("cleaning done")
}
private val helper = new Helper()
def autoClean[T](code: HelperStuff => T): T = {
val result = code(helper)
helper.cleanUp()
result
}
}