Scala: Cats, OptionT[Future, T] and ApplicativeError - scala

Some time ago I've started using Cats and found OptionT very useful to work with Future[Option[T]] in most cases. But I faced with one drawback, to use AplicativeError I need to define type alias type FutureOption[T] = OptionT[Future, X] to matching F[_] required by AplicativeError and explicitly specify the type of my expression as FutureOption[T].
type FutureOption[T] = OptionT[Future, T] // definition to match F[_] kind
val x = OptionT.liftF(Future.failed(new Exception("error"))) : FutureOption[String] // need to specify type explicitly
x.recover {
case NonFatal(e) => "fixed"
}
If I remove type definition and explicit type specification of my expression the recover will not be available because OptionT[Future, T] don't match F[_], so it can't be converted implicitly to AplicativeErrorOps.
Unfortunately, the example below won't work because there is no recover method.
val x = OptionT.liftF(Future.failed(new Exception("error")))
x.recover {
case NonFatal(e) => "fixed"
}
Is there any way to avoid such kind of boilerplate code? At least I want to avoid specifying expression types as FutureOption[T] explicitly.

In addition to the other answer, I would like to suggest that you make sure you have -Ypartial-unification enabled for your build.
This is a fix for partial unification of type constructors. You can find a more detailed explanation about the fix here.
With partial unification enabled the code you provided in your question compiles fine. Please note that if you're using an IDE (e.g. Intellij) you might get "false negatives" (the code is underlined as incorrect and code completion doesn't work), but the scalac/sbt/gradle will compile it just fine.

Yes, there are at least two ways to cope with the type ascription.
using type lambdas (this can be intimidating):
val a: { type λ[A] = OptionT[Future, A] }#λ
using a compiler plugin like kind-projector, example usage:
val a: Lambda[A => OptionT[Future, A]]
But if you wanted to call Future's recover, you can always do:
val x = OptionT.liftF(Future.failed(new Exception("error")))
x.value.recover ...

Related

In scala, how to make type class working for Aux pattern?

Here is a simple example:
trait Base {
type Out
def v: Out
}
object Base {
type Aux[T] = Base { type Out = T }
class ForH() extends Base {
type Out = HNil
override def v: Out = HNil
}
object ForH extends ForH
}
class TypeClass[B]
trait TypeClassLevel1 {
def summon[B](b: B)(implicit ev: TypeClass[B]): TypeClass[B] = ev
}
object TypeClass extends TypeClassLevel1 {
implicit def t1: TypeClass[Base.Aux[HNil]] = new TypeClass[Base.Aux[HNil]]
implicit def t2: TypeClass[Int] = new TypeClass[Int]
}
it("No Aux") {
val v = 2
TypeClass.summon(v) // works
}
it("Aux") {
val v = new Base.ForH()
TypeClass.summon(v) // oops
TypeClass.summon(Base.ForH) // oops
val v2 = new Base.ForH(): Base.Aux[HNil]
TypeClass.summon(v2) // works!
}
The object Base/ForH clearly has a stable path, this eliminate the possibility of the compiler not being able to resolve type ForH.Out.
What bothers me is not how incapable the compiler is to figure out the fact that ForH <:< Aux[HNil], but how easy it is to patch it up, just by a simple type upcast (last 2 lines). IMHO both features (type lambda & type classes) are important aspect of functional programming, why they can't work together at the same time?
If you are familiar with the compiler design I'll have an extra question: what does it take to improve the type class search algorithm to make it happen? Thanks a lot for your opinion.
UPDATE 1: a specific fix has been proposed but I have another problem trying to generalise it, please see In scala, how to make type class working for Aux pattern? - Part 2 for detail
So the compiler is able to infer ForH <:< Aux[HNil], but (tbh I don't know exactly why) when resolving an implicit when the return type uses a type lambda, it gets confused if you don't use a type bound.
Anyway that's probably not a great explanation, but at least I can make your code compile. Just change t1 to:
implicit def t1[T <: Base.Aux[HNil] ]: TypeClass[T] = new TypeClass[T]
this works for me in scastie using Scala 2.13.4.
What bothers me is not how incapable the compiler is to figure out the fact that ForH <:< Aux[HNil]
Surely the compiler does see that Base.ForH <:< Base.Aux[HNil]. You can check that
implicitly[Base.ForH <:< Base.Aux[HNil]]
compiles.
IMHO both features (type lambda & type classes) are important aspect of functional programming, why they can't work together at the same time?
Why are you talking about type lambdas? I can't see type lambdas in your question.
By the way, type lambdas is not a part of Scala 2 and ({ type λ[X] = ...F[X]... })#λ for a type lambda is more or less a hack. Actual type lambdas are added to Scala 3.
val v = new Base.ForH() has type Base.ForH (not Base.Aux[HNil] without upcasting via type ascription val v = new Base.ForH(): Base.Aux[HNil] or manual type specification val v: Base.Aux[HNil] = new Base.ForH()). TypeClass.summon(v) shouldn't compile since there is no implicit TypeClass[Base.ForH]. What implicit would you consider as a candidate? TypeClass.t1? But it's not a candidate, you can check that explicitly resolved
TypeClass.summon(v)(TypeClass.t1)
can't compile.
what does it take to improve the type class search algorithm to make it happen?
Implicit search algorithm shouldn't be improved in this specific place. It works properly, as intended.
You can make the type class contravariant
class TypeClass[-B]
Then TypeClass.t1 will be a candidate for TypeClass[Base.ForH] and TypeClass.summon(v) will compile.
In scala 2.13, how to use implicitly[value singleton type]?

Scala Implicit syntax in polymorphic methods

I am a Scala noob reading through a parsing library, and have reached some syntax I do not understand:
def parseA[_: P] = P("a")
val Parsed.Success(value, successIndex) = parse("a", parseA(_))
I want to be able to combine these lines into one, ie
val Parsed.Success(value, successIndex) = parse("a", P("a"))
but this gives a compile error:
Error:(8, 61) overloaded method value P with alternatives:
[T](t: fastparse.P[T])(implicit name: sourcecode.Name, implicit ctx: fastparse.P[_])fastparse.P[T] <and>
=> fastparse.ParsingRun.type
cannot be applied to (String)
Error occurred in an application involving default arguments.
val Parsed.Success(value, successIndex) = parse(source, P("a"))
How should this line be written? And can you name the syntax concepts involved to maximise my learning?
_: P is the same as (implicit ctx: P[_]), that means that method is asking for an implicit parameter of type P[_] (the underscore means that it does not care for the inner type. See What are all the uses of an underscore in Scala?).
P("a") is calling this method, which requires such implicit in scope, and that is why in your second example it fails to compile, because it did not find the implicit parameter.
The features sued here are implicits, existential types & macros...
All of them are very advanced techniques. If you are just starting, I would suggest to leave them for latter.
Implicits are very important and useful, I would start from there, but first make sure you feel comfortable with "normal" Scala (whatever that means).
For the second question, I think this should work.
def program[_: P] = parse("a", P("a"))
val Parsed.Success(value, successIndex) = program

Scala - pattern matching traits that have "self type" as type argument gives unchecked warning

I'm trying to define "immutable setter traits", and generic functions for those.
I have a working implementation, but i'm bit disturbed about the "unchecked" warnings from the pattern matching. I'm not really sure what i can do about it.
type Point = (Double, Double)
trait Sizable[A] {
this: A =>
def size: Point
/* immutable object value setter, returns a new of the same object*/
def size(point: Point): A with Sizable[A]
}
def partitionToSizable[T](elements: List[T]):
(List[T], List[T with Sizable[T]]) =
elements.foldLeft((List[T](), List[T with Sizable[T]]()))((p, c) =>
c match {
case a: T with Sizable[T] => (p._1, p._2 ++ List(a))
case a => (p._1 ++ List(a), p._2)
})
The code above demonstrates the problem.
I'm not even sure how big of an issue is that T being unchecked, since all the elements in the list will have a type of T, and the point of the pattern matching is not to determine if it's type is T since we already know that.
In theory Sizable will always have type of T because of the signature of it's enclosing function.
If it's there is no other solution i'd at least like to suppress the warning. #unchecked annotations does not seem to suppress the warning.
If i modify case a: T with Sizable[T] to case a: Sizable[_] it will not compile, since the result type will obviously not confirm to T.
ClassTags or TypeTags might solve the warning, but i suspect they are not necessary really. (also that might have a performance overhead and TypeTags don't work with Scala.js)
I think just case a: Sizable[T #unchecked] => a.size((15,20)) should work.
The main problem here is in partitionToSizable. It's taking a List[T] and then trying to partition it into a List[T] and a List[T with Sizable[T]]. You are taking a list of a single type and then saying that one type T is actually two different types T and T with Sizable[T]. This indicates an issue with your type design. I would recommend solving that first.
One solution might be to recognise that things that are sizable and things that are not sizable should be represented as two different types. Then you can use the following signature:
def partitionToSizable[T1, T2 <: Sizable[T2]](
elements: List[Either[T1, T2]]): (List[T1], List[T2])
Then you don't need to cast; you can pattern match or fold on the Either.

Scala: Typecast without explicitly known type parameter

Consider the following example:
case class C[T](x:T) {
def f(t:T) = println(t)
type ValueType = T
}
val list = List(1 -> C(2), "hello" -> C("goodbye"))
for ((a,b) <- list) {
b.f(a)
}
In this example, I know (runtime guarantee) that the type of a will be some T, and b will have type C[T] with the same T. Of course, the compiler cannot know that, hence we get a typing error in b.f(a).
To tell the compiler that this invocation is OK, we need to do a typecast à la b.f(a.asInstanceOf[T]). Unfortunately, T is not known here. So my question is: How do I rewrite b.f(a) in order to make this code compile?
I am looking for a solution that does not involve complex constructions (to keep the code readable), and that is "clean" in the sense that we should not rely on code erasure to make it work (see the first approach below).
I have some working approaches, but I find them unsatisfactory for various reasons.
Approaches I tried:
b.asInstanceOf[C[Any]].f(a)
This works, and is reasonably readable, but it is based on a "lie". b is not of type C[Any], and the only reason we do not get a runtime error is because we rely on the limitations of the JVM (type erasure). I think it is good style only to use x.asInstanceOf[X] when we know that x is really of type X.
b.f(a.asInstanceOf[b.ValueType])
This should work according to my understanding of the type system. I have added the member ValueType to the class C in order to be able to explicitly refer to the type parameter T. However, in this approach we get a mysterious error message:
Error:(9, 22) type mismatch;
found : b.ValueType
(which expands to) _1
required: _1
b.f(a.asInstanceOf[b.ValueType])
^
Why? It seems to complain that we expect type _1 but got type _1! (But even if this approach works, it is limited to the cases where we have the possibility to add a member ValueType to C. If C is some existing library class, we cannot do that either.)
for ((a,b) <- list.asInstanceOf[List[(T,C[T]) forSome {type T}]]) {
b.f(a)
}
This one works, and is semantically correct (i.e., we do not "lie" when invoking asInstanceOf). The limitation is that this is somewhat unreadable. Also, it is somewhat specific to the present situation: if a,b do not come from the same iterator, then where can we apply this type cast? (This code also has the side effect of being too complex for Intelli/J IDEA 2016.2 which highlights it as an error in the editor.)
val (a2,b2) = (a,b).asInstanceOf[(T,C[T]) forSome {type T}]
b2.f(a2)
I would have expected this one to work since a2,b2 now should have types T and C[T] for the same existential T. But we get a compile error:
Error:(10, 9) type mismatch;
found : a2.type (with underlying type Any)
required: T
b2.f(a2)
^
Why? (Besides that, the approach has the disadvantage of incurring runtime costs (I think) because of the creation and destruction of a pair.)
b match {
case b : C[t] => b.f(a.asInstanceOf[t])
}
This works. But enclosing the code with a match makes the code much less readable. (And it also is too complicated for Intelli/J.)
The cleanest solution is, IMO, the one you found with the type-capture pattern match. You can make it concise, and hopefully readable, by integrating the pattern directly inside your for comprehension, as follows:
for ((a, b: C[t]) <- list) {
b.f(a.asInstanceOf[t])
}
Fiddle: http://www.scala-js-fiddle.com/gist/b9030033133ee94e8c18ad772f3461a0
If you are not in a for comprehension already, unfortunately the corresponding pattern assignment does not work:
val (c, d: C[t]) = (a, b)
d.f(c.asInstanceOf[t])
That's because t is not in scope anymore on the second line. In that case, you would have to use the full pattern matching.
Maybe I'm confused about what you are trying to achieve, but this compiles:
case class C[T](x:T) {
def f(t:T) = println(t)
type ValueType = T
}
type CP[T] = (T, C[T])
val list = List[CP[T forSome {type T}]](1 -> C(2), "hello" -> C("goodbye"))
for ((a,b) <- list) {
b.f(a)
}
Edit
If the type of the list itself is out of your control, you can still cast it to this "correct" type.
case class C[T](x:T) {
def f(t:T) = println(t)
type ValueType = T
}
val list = List(1 -> C(2), "hello" -> C("goodbye"))
type CP[T] = (T, C[T])
for ((a,b) <- list.asInstanceOf[List[CP[T forSome { type T }]]]) {
b.f(a)
}
Great question! Lots to learn here about Scala.
Other answers and comments have already addressed most of the issues here, but I'd like to address a few additional points.
You asked why this variant doesn't work:
val (a2,b2) = (a,b).asInstanceOf[(T,C[T]) forSome {type T}]
b2.f(a2)
You aren't the only person who's been surprised by this; see e.g. this recent very similar issue report: SI-9899.
As I wrote there:
I think this is working as designed as per SLS 6.1: "The following skolemization rule is applied universally for every expression: If the type of an expression would be an existential type T, then the type of the expression is assumed instead to be a skolemization of T."
Basically, every time you write a value-level expression that the compiler determines to have an existential type, the existential type is instantiated. b2.f(a2) has two subexpressions with existential type, namely b2 and a2, so the existential gets two different instantiations.
As for why the pattern-matching variant works, there isn't explicit language in SLS 8 (Pattern Matching) covering the behavior of existential types, but 6.1 doesn't apply because a pattern isn't technically an expression, it's a pattern. The pattern is analyzed as a whole and any existential types inside only get instantiated (skolemized) once.
As a postscript, note that yes, when you play in this area, the error messages you get are often confusing or misleading and ought to be improved. See for example https://github.com/scala/scala-dev/issues/205
A wild guess, but is it possible that you need something like this:
case class C[+T](x:T) {
def f[A >: T](t: A) = println(t)
}
val list = List(1 -> C(2), "hello" -> C("goodbye"))
for ((a,b) <- list) {
b.f(a)
}
?
It will type check.
I'm not quite sure what "runtime guarantee" means here, usually it means that you are trying to fool type system (e.g. with asInstanceOf), but then all bets are off and you shouldn't expect type system to be of any help.
UPDATE
Just for the illustration why type casting is an evil:
case class C[T <: Int](x:T) {
def f(t: T) = println(t + 1)
}
val list = List("hello" -> C(2), 2 -> C(3))
for ((a, b: C[t]) <- list) {
b.f(a.asInstanceOf[t])
}
It compiles and fails at runtime (not surprisingly).
UPDATE2
Here's what generated code looks like for the last snippet (with C[t]):
...
val a: Object = x1._1();
val b: Test$C = x1._2().$asInstanceOf[Test$C]();
if (b.ne(null))
{
<synthetic> val x2: Test$C = b;
matchEnd4({
x2.f(scala.Int.unbox(a));
scala.runtime.BoxedUnit.UNIT
})
}
...
Type t simply vanished (as it should have been) and Scala is trying to convert a to an upper bound of T in C, i.e. Int. If there is no upper bound it's going to be Any (but then method f is nearly useless unless you cast again or use something like println which takes Any).

Type dependent implicits

Two implicits are needed in the function, but I can't have them in the
same parameter list, because I get dependent method type. So I
considered currying once more, but that gives me a syntax error.
What's the correct way to do this?
def add[A](newAnnotations: Seq[A])
(implicit maybeAdd: MaybeAdd[L, Seq[A]])
(implicit mod: Modifier[maybeAdd.Out, Seq[A], Seq[A]]):
Slab[Content, maybeAdd.Out] = {
val l = maybeAdd(annotations, Seq[A]())
l.updateWith(_ ++ newAnnotations)
}
I edited MaybeAdd to have an Aux type, as suggested by #milessabin.
def add[A, Out0](newAnnotations: Seq[A])(implicit maybeAdd: MaybeAdd.Aux[L, Seq[A], Out0], mod: Modifier[Out0, Seq[A], Seq[A]]): Slab[Content, mod.Out] = {
val l = maybeAdd(annotations, Seq[A]())
new Slab(content, mod(l, _ ++ newAnnotations))
}
Note: the syntax error is likely to be:
illegal dependent method type:
parameter appears in the type of another parameter in the same section or an earlier one
Meaning one cannot use the dependent type in the same section, only in the next parameters block or as a return type only.
See "A short introduction to the Aux pattern" byLuigi.
type Aux[A0, B0] = Foo[A0] { type B = B0 }
basically Aux is just a way to extract the result of a type level computation
As noted in "Why is the Aux technique required for type-level computations?"
the Aux type aliases are entirely a syntactic convenience.
The Aux version has a couple of advantages over writing out the type refinements in this way: it's less noisy, and it doesn't require us to remember the name of the type member.
These are purely ergonomic issues, though—the Aux aliases make our code a little easier to read and write, but they don't change what we can or can't do with the code in any meaningful way.