Scala version: 2.11.8
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import play.api.mvc._
object foooo {
type ActionFilter[R] = R => Future[Either[Int, Unit]]
case class FooRequest[A](request: Request[A]) extends WrappedRequest(request)
implicit class ActionFilterOps[R](val f: ActionFilter[R]) {
def * (f1: ActionFilter[R]): ActionFilter[R] = { (r: R) =>
f(r).flatMap {
case Left(r) => Future.successful(Left(r))
case Right(_) => f1(r)
}
}
}
def test[A]() = {
val f0: ActionFilter[FooRequest[A]] = { (r: FooRequest[A]) =>
Future.successful(Left(1))
}
val f1 = f0
ActionFilterOps(f1) * f0 // Ok
f1 * f0 // Won't compile
}
}
As you can see, implicit class not work. I have to new the ActionFilterOps
As a side note: it helps if you have a self contained example that does not depend on external dependencies (such as Play) if that is possible.
Now onto the answer, or at least fix of the error... If you change the type alias ActionFilter to a class that wraps a function it works.
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
case class ActionFilter[R](f: R => Future[Either[Int, Unit]]) { def apply(r: R) = f(r) }
type Request[A] = Option[A]
case class FooRequest[A](request: Request[A])
implicit class ActionFilterOps[R](val f: ActionFilter[R]) {
def * (f1: ActionFilter[R]): ActionFilter[R] = ActionFilter{ (r: R) =>
f(r).flatMap {
case Left(r) => Future.successful(Left(r))
case Right(_) => f1(r)
}
}
}
def test[A]() = {
val f0: ActionFilter[FooRequest[A]] = ActionFilter{ (r: FooRequest[A]) =>
Future.successful(Left(1))
}
val f1 = f0
ActionFilterOps(f1) * f0 // Ok
f1 * f0 // Ok
}
It also works if you use the raw function type instead of the type alias.
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
type Request[A] = Option[A]
case class FooRequest[A](request: Request[A])
implicit class ActionFilterOps[R](val f: R => Future[Either[Int, Unit]]) {
def * (f1: R => Future[Either[Int, Unit]]): R => Future[Either[Int, Unit]] = { (r: R) =>
f(r).flatMap {
case Left(r) => Future.successful(Left(r))
case Right(_) => f1(r)
}
}
}
def test[A]() = {
val f0: FooRequest[A] => Future[Either[Int, Unit]] = { (r: FooRequest[A]) =>
Future.successful(Left(1))
}
val f1 = f0
ActionFilterOps(f1) * f0 // Ok
f1 * f0 // Ok
}
Especially that second "solution" makes me think that this is a bug.
Related
Cannot compile even if a function that handles function arguments is passed to macro.
A sample is shown below.
trait Generated[Z] {
def deserialize[A](t: A): Z
}
def from[A, Z](apl: A => Z): Generated[Z] = macro GeneratorMacro.from[A, Z]
class GeneratorMacro(val c: blackbox.Context) {
import c.universe._
def from[A: c.WeakTypeTag, Z: c.WeakTypeTag](apl: c.Expr[A => Z]): c.Expr[Generated[Z]] = {
reify {
new Generated[Z] {
def deserialize[A](t: A): Z = {
apl.splice.apply(t)
}
}
}
}
}
object Generation {
def apply(input: String): Generated[Int] = {
Generator.from[Option[String], Int] {
case Some(i) => input.toInt + i.toInt // compilation failed
case None => 0
}
}
}
An error occurs at this time.
Error: scalac: Error while emitting Generation.scala
value input
Isn't the class recompiled with the macro expanded?
If recompiled with inline expansion, no compilation error should occur.
object Generation {
def apply(input: String): Generated[Int] = {
new Generated[Int] {
def deserialize(t: String): Int = {
{
case Some(i) => input.toInt + i.toInt // input should be visible
case None => 0
}.apply(t)
}
}
}
}
What is going on and how to avoid it.
This seems to be impossible, AFAICS. The compiler creates an anonymous class for your code, but it will not capture the lexical context of the macro call inside it.
The code looks a bit like this after the lambdalift phase:
def apply(input: String): Generated[Int] = ({
new <$anon: Generated>()
}: Generated);
...
final class $anon extends Object with Generated {
def deserialize(t: Option): Int = {
... // <- your code is here !!
};
}
Of course, the code has no access to the input variable at this place.
This might be a bug in the Scala compiler...
The first error I have is
Warning:scalac: {
final class $anon extends App.this.Generated[Int] {
def <init>() = {
super.<init>();
()
};
def deserialize(t: Option[String]): Int = ((x0$1: Option[String]) => x0$1 match {
case (value: String)Some[String]((i # _)) => scala.Predef.augmentString(input).toInt.+(scala.Predef.augmentString(i).toInt)
case scala.None => 0
}).apply(t)
};
new $anon()
}
Error:(6, 43) object creation impossible. Missing implementation for:
def deserialize[A](t: A): Int // inherited from trait Generated
Generator.from[Option[String], Int] {
Obviously this is because you define
reify {
new Generated[Z] {
def deserialize(t: A): Z = {
...
instead of def deserialize[A](t: A): Z (#OlegPyzhcov pointed this out in the comments).
Regarding your error Error while emitting ... the thing is that
{
case Some(i: String) => input.toInt + i.toInt
case None => 0
}
has type not ... => ... i.e. Function1[..., ...] but actually PartialFunction[..., ...].
Try either
object Generator {
def from[Z](apl: PartialFunction[Any, Z]): Generated[Z] = macro GeneratorMacro.from[Z]
}
class GeneratorMacro(val c: blackbox.Context) {
import c.universe._
def from[Z: c.WeakTypeTag](apl: c.Expr[Any => Z]): c.Expr[Generated[Z]] = {
reify {
new Generated[Z] {
def deserialize[A](t: A): Z = {
apl.splice.apply(t)
}
}
}
}
}
Generator.from[Int]({
case Some(i: String) => input.toInt + i.toInt
case None => 0
})
or
object Generator {
def from[Z](apl: Any => Z): Generated[Z] = macro GeneratorMacro.from[Z]
}
class GeneratorMacro(val c: blackbox.Context) {
import c.universe._
def from[Z: c.WeakTypeTag](apl: c.Expr[Any => Z]): c.Expr[Generated[Z]] = {
reify {
new Generated[Z] {
def deserialize[A](t: A): Z = {
apl.splice.apply(t)
}
}
}
}
}
Generator.from[Int](new (Any => Int) {
override def apply(x: Any): Int = x match {
case Some(i: String) => input.toInt + i.toInt
case None => 0
}
})
Can I generate Scala code from a template (of sorts)?
I know how to do this in Racket/Scheme/Lisp, but not in Scala. Is this something Scala macros can do?
I want to have a code template where X varies. If I had this code template:
def funcX(a: ArgsX): Try[Seq[RowX]] =
w.getThing() match {
case Some(t: Thing) => w.wrap(t){Detail.funcX(t, a)}
case _ => Failure(new MissingThingException)
}
and tokens Apple and Orange, a macro would take my template, replace the Xs, and produce:
def funcApple(a: ArgsApple): Try[Seq[RowApple]] =
w.getThing() match {
case Some(t: Thing) => w.wrap(t){Detail.funcApple(t, a)}
case _ => Failure(new MissingThingException)
}
def funcOrange(a: ArgsOrange): Try[Seq[RowOrange]] =
w.getThing() match {
case Some(t: Thing) => w.wrap(t){Detail.funcOrange(t, a)}
case _ => Failure(new MissingThingException)
}
Try macro annotation with tree transformer
#compileTimeOnly("enable macro paradise")
class generate extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro GenerateMacro.impl
}
object GenerateMacro {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
val trees = List("Apple", "Orange").map { s =>
val transformer = new Transformer {
override def transform(tree: Tree): Tree = tree match {
case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" if tname.toString.contains("X") =>
val tname1 = TermName(tname.toString.replace("X", s))
val tparams1 = tparams.map(super.transform(_))
val paramss1 = paramss.map(_.map(super.transform(_)))
val tpt1 = super.transform(tpt)
val expr1 = super.transform(expr)
q"$mods def $tname1[..$tparams1](...$paramss1): $tpt1 = $expr1"
case q"${tname: TermName} " if tname.toString.contains("X") =>
val tname1 = TermName(tname.toString.replace("X", s))
q"$tname1"
case tq"${tpname: TypeName} " if tpname.toString.contains("X") =>
val tpname1 = TypeName(tpname.toString.replace("X", s))
tq"$tpname1"
case q"$expr.$tname " if tname.toString.contains("X") =>
val expr1 = super.transform(expr)
val tname1 = TermName(tname.toString.replace("X", s))
q"$expr1.$tname1"
case tq"$ref.$tpname " if tpname.toString.contains("X") =>
val ref1 = super.transform(ref)
val tpname1 = TypeName(tpname.toString.replace("X", s))
tq"$ref1.$tpname1"
case t => super.transform(t)
}
}
transformer.transform(annottees.head)
}
q"..$trees"
}
}
#generate
def funcX(a: ArgsX): Try[Seq[RowX]] =
w.getThing() match {
case Some(t: Thing) => w.wrap(t){Detail.funcX(t, a)}
case _ => Failure(new MissingThingException)
}
//Warning:scalac: {
// def funcApple(a: ArgsApple): Try[Seq[RowApple]] = w.getThing() match {
// case Some((t # (_: Thing))) => w.wrap(t)(Detail.funcApple(t, a))
// case _ => Failure(new MissingThingException())
// };
// def funcOrange(a: ArgsOrange): Try[Seq[RowOrange]] = w.getThing() match {
// case Some((t # (_: Thing))) => w.wrap(t)(Detail.funcOrange(t, a))
// case _ => Failure(new MissingThingException())
// };
// ()
//}
Also you can try approach with type class
def func[A <: Args](a: A)(implicit ar: ArgsRows[A]): Try[Seq[ar.R]] =
w.getThing() match {
case Some(t: Thing) => w.wrap(t){Detail.func(t, a)}
case _ => Failure(new MissingThingException)
}
trait ArgsRows[A <: Args] {
type R <: Row
}
object ArgsRows {
type Aux[A <: Args, R0 <: Row] = ArgsRows[A] { type R = R0 }
implicit val apple: Aux[ArgsApple, RowApple] = null
implicit val orange: Aux[ArgsOrange, RowOrange] = null
}
sealed trait Args
trait ArgsApple extends Args
trait ArgsOrange extends Args
trait Thing
sealed trait Row
trait RowApple extends Row
trait RowOrange extends Row
object Detail {
def func[A <: Args](t: Thing, a: A)(implicit ar: ArgsRows[A]): ar.R = ???
}
class MissingThingException extends Throwable
trait W {
def wrap[R <: Row](t: Thing)(r: R): Try[Seq[R]] = ???
def getThing(): Option[Thing] = ???
}
val w: W = ???
In my opinion, it looks like you could pass your funcX function as a higher-order function. You could also combine it with currying to make a "function factory":
def funcX[A](f: (Thing, A) => RowX)(a: A): Try[Seq[RowX]] =
w.getThing() match {
case Some(t: Thing) => w.wrap(t){f(t,a)}
case _ => Failure(new MissingThingException)
}
Then you could use it to create instances of funcApple or funcOrange:
val funcApple: ArgsApple => Try[Seq[RowX]] = funcX(Detail.funcApple)
val funcOrange: ArgsOrange => Try[Seq[RowX]] = funcX(Detail.funcOrange)
funcApple(argsApple)
funcOrange(argsOrange)
I assumed the signature of Detail.funcApple and Detail.funcOrange is similar to (Thing, X) => RowX, but of course you could use different.
You may not actually need macros to achieve this, you can use a pattern match a generic type like this:
import scala.util.Try
def funcX[A](input :A) :Try[Seq[String]] = input match {
case x :String => Success(List(s"Input is a string: $input, call Detail.funcApple"))
case x :Int => Success(List(s"Input is an int, call Detail.funcOrange"))
}
scala> funcX("apple")
res3: scala.util.Try[Seq[String]] = Success(List(Input is a string: apple, call Detail.funcApple))
scala> funcX(11)
res4: scala.util.Try[Seq[String]] = Success(List(Input is an int, call Detail.funcOrange))
Inspired by travisbrown, I'm trying to use a macro to create some "smart constructors".
Given
package mypkg
sealed trait Hello[A]
case class Ohayo[A,B](a: (A,B)) extends Hello[A]
and
val smartConstructors = FreeMacros.liftConstructors[Hello]
The macro should find all the subclasses of Hello, look at their constructors, and extract a few elements to populate this tree for the "smart constructor":
q"""
def $methodName[..$typeParams](...$paramLists): $baseType =
$companionSymbol[..$typeArgs](...$argLists)
"""
I hoped to get:
val smartConstructors = new {
def ohayo[A, B](a: (A, B)): Hello[A] = Ohayo[A, B](a)
}
but instead get:
error: type mismatch;
found : (A(in class Ohayo), B(in class Ohayo))
required: ((some other)A(in class Ohayo), (some other)B(in class Ohayo))
val liftedConstructors = FreeMacros.liftConstructors[Hello]
At a glance, the tree looks ok to me:
scala> q" new { ..$wellTyped }"
res1: u.Tree =
{
final class $anon extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
def ohayo[A, B](a: (A, B)): net.arya.constructors.Hello[A] = Ohayo[A, B](a)
};
new $anon()
}
but I guess it invisibly isn't. If I naively try to freshen up the typeParams with info.typeParams.map(p => TypeName(p.name.toString)), I get "can't splice A as type parameter" when I do the quasiquoting.
Where am I going wrong? Thanks for taking a look.
-Arya
import scala.language.experimental.macros
import scala.reflect.api.Universe
import scala.reflect.macros.whitebox
class FreeMacros(val c: whitebox.Context) {
import c.universe._
import FreeMacros._
def liftedImpl[F[_]](implicit t: c.WeakTypeTag[F[_]]): Tree = {
val atc = t.tpe
val childSymbols: Set[ClassSymbol] = subCaseClassSymbols(c.universe)(atc.typeSymbol.asClass)
val wellTyped = childSymbols.map(ctorsForSymbol(c.universe)(atc)).unzip
q"new { ..${wellTyped} }"
}
}
object FreeMacros {
def liftConstructors[F[_]]: Any = macro FreeMacros.liftedImpl[F]
def smartName(name: String): String = (
name.toList match {
case h :: t => h.toLower :: t
case Nil => Nil
}
).mkString
def subCaseClassSymbols(u: Universe)(root: u.ClassSymbol): Set[u.ClassSymbol] = {
val subclasses = root.knownDirectSubclasses
val cast = subclasses.map(_.asInstanceOf[u.ClassSymbol])
val partitioned = mapped.partition(_.isCaseClass)
partitioned match {
case (caseClasses, regularClasses) => caseClasses ++ regularClasses.flatMap(r => subCaseClassSymbols(u)(r))
}
}
def ctorsForSymbol(u: Universe)(atc: u.Type)(caseClass: u.ClassSymbol): (u.DefDef, u.DefDef) = {
import u._
import internal._
// these didn't help
// def clearTypeSymbol(s: Symbol): TypeSymbol = internal.newTypeSymbol(NoSymbol, s.name.toTypeName, s.pos, if(s.isImplicit)Flag.IMPLICIT else NoFlags)
// def clearTypeSymbol2(s: Symbol): TypeSymbol = internal.newTypeSymbol(NoSymbol, s.name.toTypeName, NoPosition, if(s.isImplicit)Flag.IMPLICIT else NoFlags)
// def clearTypeDef(d: TypeDef): TypeDef = internal.typeDef(clearTypeSymbol(d.symbol))
val companionSymbol: Symbol = caseClass.companion
val info: Type = caseClass.info
val primaryCtor: Symbol = caseClass.primaryConstructor
val method = primaryCtor.asMethod
val typeParams = info.typeParams.map(internal.typeDef(_))
// val typeParams = info.typeParams.map(s => typeDef(newTypeSymbol(NoSymbol, s.name.toTypeName, NoPosition, NoFlags)))
// val typeParams = info.typeParams.map(s => internal.typeDef(clearTypeSymbol2(s)))
val typeArgs = info.typeParams.map(_.name)
val paramLists = method.paramLists.map(_.map(internal.valDef(_)))
val argLists = method.paramLists.map(_.map(_.asTerm.name))
val baseType = info.baseType(atc.typeSymbol)
val List(returnType) = baseType.typeArgs
val methodName = TermName(smartName(caseClass.name.toString))
val wellTyped =
q"""
def $methodName[..$typeParams](...$paramLists): $baseType =
$companionSymbol[..$typeArgs](...$argLists)
"""
wellTyped
}
}
P.S. I have been experimenting with toolbox.untypecheck / typecheck per this article but haven't found a working combination.
you need using
clas.typeArgs.map(_.toString).map(name => {
TypeDef(Modifiers(Flag.PARAM),TypeName(name), List(),TypeBoundsTree(EmptyTree, EmptyTree))
}
replace
info.typeParams.map(p => TypeName(p.name.toString))
it si my code
object GetSealedSubClass {
def ol3[T]: Any = macro GetSealedSubClassImpl.ol3[T]
}
class GetSealedSubClassImpl(val c: Context) {
import c.universe._
def showInfo(s: String) =
c.info(c.enclosingPosition, s.split("\n").mkString("\n |---macro info---\n |", "\n |", ""), true)
def ol3[T: c.WeakTypeTag]: c.universe.Tree = {
//get all sub class
val subClass = c.weakTypeOf[T]
.typeSymbol.asClass.knownDirectSubclasses
.map(e => e.asClass.toType)
//check type params must ia s sealed class
if (subClass.size < 1)
c.abort(c.enclosingPosition, s"${c.weakTypeOf[T]} is not a sealed class")
// get sub class constructor params
val subConstructorParams = subClass.map { e =>
//get constructor
e.members.filter(_.isConstructor)
//if the class has many Constructor then you need filter the main Constructor
.head.map(s => s.asMethod)
//get function param list
}.map(_.asMethod.paramLists.head)
.map(_.map(e => q"""${e.name.toTermName}:${e.info} """))
val outfunc = subClass zip subConstructorParams map {
case (clas, parm) =>
q"def smartConstructors[..${
clas.typeArgs.map(_.toString).map(name => {
TypeDef(Modifiers(Flag.PARAM), TypeName(name), List(), TypeBoundsTree(EmptyTree, EmptyTree))
})
}](..${parm})=${clas.typeSymbol.name.toTermName} (..${parm})"
}
val outClass =
q"""
object Term{
..${outfunc}
}
"""
showInfo(show(outClass))
q"""{
$outClass
Term
}
"""
}
}
using like this
sealed trait Hello[A]
case class Ohayo[A, B](a: (A, B)) extends Hello[A]
object GetSealed extends App {
val a = GetSealedSubClass.ol3[Hello[_]]
val b=a.asInstanceOf[ {def smartConstructors[A, B](a: (A, B)): Ohayo[A, B]}].smartConstructors(1, 2).a
println(b)
}
I have a function get: T => scala.concurrent.Future[T]
I want to iterates it like :
val futs: Iterator[Future[T]] = Iterator.iterate(get(init)){
_.flatMap(prev => get(prev))
}
But the type of Iterator is Future[T], it is not easy to process this iterator.
How could I transfer that to Process[?, T]
(Maybe T => Future[T] as context type F).
Not super nice solution, but works
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Future => SFuture}
import scala.language.implicitConversions
import scalaz.concurrent.Task
import scalaz.stream._
implicit class Transformer[+T](fut: => SFuture[T]) {
def toTask(implicit ec: scala.concurrent.ExecutionContext): Task[T] = {
import scala.util.{Success, Failure}
import scalaz.syntax.either._
Task.async {
register =>
fut.onComplete {
case Success(v) => register(v.right)
case Failure(ex) => register(ex.left)
}
}
}
}
val init: Int = 0
def f(i: Int): SFuture[Int] = SFuture(i + 1)
val p = Process.repeatEval[Task, Int] {
var prev = init
f(prev).toTask.map(next => {prev = next; next})
}
println(p.take(10).runLog.run)
Assuming you know how to convert Future -> Task (either via implicit or via Process.transform) this shall work:
def get(t:T): Task[T] = ???
val initial : T = ???
val signal = scalaz.stream.async.signal[T]
// emit initial value, and follow by any change of `T` within the signal
val source:Process[Task,T] = eval_(signal.set(t)) fby signal.discrete
// sink to update `T` within the signal
val signalSink:Sink[Task,T] = constant((t:T) => signal.set(t))
// result, that esentially converts T => Task[T] into Process[Task,T]
val result: Process[Task,T] = source.observe(signalSink)
Finally I got what Pavel Chlupacek wanted to say. Signal looks cool, but a little bit cryptic for beginner.
import scala.concurrent.{Future => SFuture}
import scala.language.implicitConversions
import scalaz.concurrent.Task
import scalaz.stream._
import scala.concurrent.ExecutionContext.Implicits.global
implicit class Transformer[+T](fut: => SFuture[T]) {
def toTask(implicit ec: scala.concurrent.ExecutionContext): Task[T] = {
import scala.util.{Failure, Success}
import scalaz.syntax.either._
Task.async {
register =>
fut.onComplete {
case Success(v) => register(v.right)
case Failure(ex) => register(ex.left)
}
}
}
}
val init: Int = 0
def f(i: Int): SFuture[Int] = SFuture(i + 1)
val signal = scalaz.stream.async.signal[Int]
// Observe value and push them to signal
val signalSink: Process[Task, Int => Task[Unit]] = // =:= Sink[Task, Int]
Process.constant((input: Int) => signal.set(input))
// Start from init and then consume from signal
val result = (Process.eval(f(init).toTask) ++ signal.discrete.evalMap(i => f(i).toTask)) observe signalSink
println(result.take(10).runLog.run)
I made another solution
def iterate[F[_],A](init: A)(f: A => F[A]): Process[F, A] = {
Process.emit(init) ++ Process.await(f(init)) { next => iterate(next)(f)}
}
This is already an feature of scalaz-stream 0.6, see this pr for detail
Inorder to use scala.concurrent.Future as context type F
We need import scalaz.std.scalaFuture._ and an Catchable instance
implicit def futureCatchable(implicit ctx: ExecCtx): Catchable[Future] = {
new Catchable[Future] {
def attempt[A](f: Future[A]) = f.map(\/-(_)).recover { case e => -\/(e)}
def fail[A](err: Throwable) = Future.failed(err)
}
}
Finally I got this:
package stream
import scala.concurrent._
import scalaz._
import scalaz.stream._
package object future {
type ExecCtx = ExecutionContext
def iterate[F[_],A](init: A)(f: A => F[A]): Process[F, A] = {
Process.emit(init) ++ Process.await(f(init)) { next => iterate(next)(f)}
}
implicit def futureCatchable(implicit ctx: ExecCtx): Catchable[Future] = {
new Catchable[Future] {
def attempt[A](f: Future[A]) = f.map(\/-(_)).recover { case e => -\/(e)}
def fail[A](err: Throwable) = Future.failed(err)
}
}
}
object futureApp extends App {
import scalaz.Scalaz._
import future._
import scala.concurrent.ExecutionContext.Implicits.global
def get(i: Int) = Future {
println(i + 1)
i + 1
}
iterate(0)(get).takeWhile(_ < 100000).run
}
I want to define a method, which will return a Future. And in this method, it will call another service which returns also a Future.
We have defined a BusinessResult to represent Success and Fail:
object validation {
trait BusinessResult[+V] {
def flatMap[T](f: V => BusinessResult[T]):BusinessResult[T]
def map[T](f: V => T): BusinessResult[T]
}
sealed case class Success[V](t:V) extends BusinessResult[V] {
def flatMap[T](f: V => BusinessResult[T]):BusinessResult[T] = {
f(t)
}
def map[T](f: V => T): BusinessResult[T] = {
Success(f(t))
}
}
sealed case class Fail(e:String) extends BusinessResult[Nothing] {
def flatMap[T](f: Nothing => BusinessResult[T]):BusinessResult[T] = this
def map[T](f: Nothing => T): BusinessResult[T] = this
}
}
And define the method:
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import validation._
def name: BusinessResult[String] = Success("my name")
def externalService(name:String):Future[String] = future(name)
def myservice:Future[Int] = {
for {
n <- name
res <- externalService(n)
} yield res match {
case "ok" => 1
case _ => 0
}
}
But which is not compilable. The code in myservice can't return a Future[Int] type.
I also tried to wrap the name with Future:
def myservice:Future[Int] = {
for {
nn <- Future.successful(name)
n <- nn
res <- externalService(n)
} yield res match {
case "ok" => 1
case _ => 0
}
}
Which is also not compilable.
I know there must be a lot of issues in this code. How can I adjust them to make it compilable?
If you change the n with some hardcoded string it works, the problem is that in the for comprehension the variable n has type BusinessResult[String], as you probably already know for comprehension desugarize to map, flatMap and filter so the first part n <- name desugarize to a map on name:
val test: BusinessResult[String] = name.map(x => x)
Intellij thinks n is a String but the scala compiler disagree:
type mismatch;
[error] found : validation.BusinessResult[Nothing]
[error] required: scala.concurrent.Future[Int]
[error] n <- name
[error] ^
Easy solution could be to add a getter method to get back the string and do something like Option does:
object validation {
trait BusinessResult[+V] {
def flatMap[T](f: V => BusinessResult[T]):BusinessResult[T]
def map[T](f: V => T): BusinessResult[T]
def getVal: V
}
sealed case class Success[V](t:V) extends BusinessResult[V] {
def flatMap[T](f: V => BusinessResult[T]):BusinessResult[T] = {
f(t)
}
def map[T](f: V => T): BusinessResult[T] = {
Success(f(t))
}
def getVal: V = t
}
sealed case class Fail(e:String) extends BusinessResult[Nothing] {
def flatMap[T](f: Nothing => BusinessResult[T]):BusinessResult[T] = this
def map[T](f: Nothing => T): BusinessResult[T] = this
def getVal = throw new Exception("some message")
}
}
def myservice: Future[Int] = {
val value = name.getVal
for {
res <- externalService(value)
} yield res match {
case "ok" => 1
case _ => 0
}
}
Note that you can't extract the name in the for comprehension since map on String return Chars