Being writing a completely async library to access a remote service (using Play2.0), I'm using Promise
and Validation
to create non-blocking call, which has a type presenting fail and valid result at once.
Promise
comes from Play2-scala, where Validation
comes from scalaz.
So here is the type of examples of such functions
- f ::
A => Promise[Validation[E, B]]
- g ::
B => Promise[Validation[E, C]]
So far, so good, now if I want to compose them, I can simple use the fact that Promise
present a flatMap
, so I can do it with a for-comprehension
for (
x <- f(a);
y <- g(b)
) yield y
Ok, I took a shortcut to my problem here because I didn't reused the Validation
results within the for-comprehension. So if I want to reuse x
in g
, here is how I could do
for (
x <- f(a); // x is a Validation
y <- x.fold(
fail => Promise.pure(x),
ok => g(ok)
)
) yield y
Fair enough, but this kind of boilerplate will go to pollute my code over and over again. The problem here is that I've a kind of two-levels Monadic structure like M[N[_]]
.
At this stage, is there any structure in f° programming that enables working with such structure by skipping easily the secong level:
for (
x <- f(a); //x is a B
y <- g(b)
) yield y
Now, below is how I achieved something similar.
I created kind of Monadic structure that wraps the two level in one, let say ValidationPromised
which pimped the Promise
type with two methods:
def /~> [EE >: E, B](f: Validation[E, A] => ValidationPromised[EE, B]): ValidationPromised[EE, B] =
promised flatMap { valid =>
f(valid).promised
}
def /~~>[EE >: E, B](f: A => ValidationPromised[EE, B]): ValidationPromised[EE, B] =
promised flatMap { valid =>
valid.fold (
bad => Promise.pure(KO(bad)),
good => f(good).promised
)
}
This allows me to do such things
endPoint.service /~~> //get the service
(svc => //the service
svc.start /~~> (st => //get the starting elt
svc.create(None) /~~> //svc creates a new elt
(newE => //the created one
newEntry.link(st, newE) /~~> //link start and the new
(lnk => Promise.pure(OK((st, lnk, newE)))) //returns a triple => hackish
)
)
)
As we can see /~~>
is pretty similar to flatMap
but skips one level. The problem is the verbosity (that's why "for-comprehension" exists in Scala and "do" in Haskell).
Another point, I've the /~>
that stands like a map
also but works on the second level (instead of the Valid type -- third level)
So my second question is corollary to the former... Am I approching a sustainable solution with this construction ?
sorry to be that long