Quick question
With
trait SubEnv1
trait SubEnv2
While i understand (I think) why the following code would work:
def logic =
for {
s1 <- Kleisli{(e:SubEnv1) => Option("hello")}
s1 <- Kleisli{(e:Any) => Option("hello2")}
} yield (s1)
//cats.data.Kleisli[Option,SubEnv1,String]
I am confused as to why the following does not work:
def logic =
for {
s1 <- Kleisli{(e:SubEnv1) => Option("hello")}
s1 <- Kleisli{(e:Nothing) => Option("hello2")}
} yield (s1)
//type mismatch;
//found : Nothing => Option[String]
//required: A => Option[String]
In fact i do not understand the error.
The type of flatMap is
def flatMap[C, AA <: A](f: B => Kleisli[F, AA, C])(implicit F: FlatMap[F]): Kleisli[F, AA, C]
So we have AA <: A
The first work because of contravariance i think.
Where AA => Option[C] is expected we can pass can take Any => Option[C]
The second is rather strange given the requirement
AA <: A
and
implicitly[Nothing <:< SubEnv1]
// Nothing <:< SubEnv1 = generalized constraint
So what does //required: A => Option[String] that it requires a type but Nothing ?
I believe you are simply hitting this bug: Nothing does not conform to arbitrary type parameter #9453
Proof:
Kleisli { (e: SubEnv1) => Option("hello") }.flatMap[String, Nothing] { s1 =>
Kleisli[Option, Nothing, String] { (e: Nothing) => Option("boo") }
}
res34: Kleisli[Option, Nothing, String] = Kleisli(cats.data.Kleisli$$$Lambda$2297/0x0000000800c08840#1385e9e3)
(typechecks correctly)
Related
I am trying to create a function, which takes a tuple of higher-kinded types and applies a function to the types within the higher-kinded types.
In the example below, there is a trait Get[A] which is our higher-kinded type. There is also a tuple of Get's: (Get[String],Get[Int]) as well as function from (String,Int) => Person.
Scala-3 has a Match-Type called InverseMap which converts the type (Get[String], Get[Int]) into what is essentially the type (String,Int).
So the ultimate goal is to write a function which can take a tuple with any number of Get[_] types and a function whose input matches the InserveMap types and finally return a Get[_], where the wrapped type is the result of the function.
I have attempted to create a function called genericF below to show the desired behavior, though it may not be correct -- but I think it does at least show the proper intent.
case class Person(name: String, age: Int)
trait Get[A] {
def get: A
}
case class Put[A](get: A) extends Get[A]
val t: (Get[String], Get[Int]) = (Put("Bob"), Put(42))
val fPerson: (String,Int) => Person = Person.apply _
def genericF[T<:Tuple,I<:Tuple.InverseMap[T,Get],B](f: I => B, t: T): Get[B] = ???
val person: Get[Person] = genericF(fPerson, t)
I have set up a Scastie here: https://scastie.scala-lang.org/OleTraveler/QIyNHPLHQIKPv0lgsYbujA/23
Your code is almost compiling already - the only thing is that fPerson is of type (String, Int) => Person instead of ((String, Int)) => Person (taking a tuple instead of 2 separate parameters).
The solution below this one is not nice, although it is perhaps more efficient for TupleXXL's. Here's a nicer version with typeclasses (Scastie):
val fPerson: ((String, Int)) => Person = Person.apply _
opaque type Extract[GT <: Tuple, RT <: Tuple] = GT => RT
given Extract[EmptyTuple, EmptyTuple] = Predef.identity
given [A, PG <: Tuple, PR <: Tuple](using p: Extract[PG, PR])
as Extract[Get[A] *: PG, A *: PR] = {
case h *: t => h.get *: p(t)
}
def genericF[GT <: Tuple, RT <: Tuple, B](
f: RT => B,
t: GT
)(using extract: Extract[GT, RT]): Get[B] = Put(f(extract(t)))
Here's one way you could implement genericF using Tuple.InverseMap (note that I switched the two parameters to genericF:
val fPerson: ((String, Int)) => Person = Person.apply _
type ExtractG = [G] =>> G match {
case Get[a] => a
}
type AllGs[T <: Tuple] = T match {
case EmptyTuple => DummyImplicit
case Get[_] *: t => AllGs[t]
case _ => Nothing
}
def extract[T <: Tuple](t: T)(using AllGs[T]): Tuple.InverseMap[T, Get] =
t.map {
[G] => (g: G) => g.asInstanceOf[Get[_]].get.asInstanceOf[ExtractG[G]]
}.asInstanceOf[Tuple.InverseMap[T, Get]]
def genericF[B](
t: Tuple,
f: Tuple.InverseMap[t.type, Get] => B
)(using AllGs[t.type]): Get[B] = Put(f(extract(t)))
val person: Get[Person] = genericF(t, fPerson)
ExtractG is to make the PolyFunction compile, because it requires you apply a type constructor to its type parameter.
AllGs is to verify that the tuple consists only of Gets, because as pointed out by Dmytro Mitin, it isn't typesafe otherwise. If it's all Gets, the type becomes DummyImplicit, which Scala provides for us. Otherwise, it's Nothing. I guess it could conflict with other implicit/given Nothings in scope, but if you do have one already, you're screwed anyways.
Note that this will work only when you have Get and will need some modification if you also want it to work for tuples like (Put[String], GetSubclass[Int]).
Travis Stevens, the OP, has managed to get the solution above this one to work without creating AllGs, by using IsMappedBy. This is what they got (Scastie):
val fPerson: ((String, Int)) => Person = Person.apply _
type ExtractG = [G] =>> G match {
case Get[a] => a
}
def extract[T <: Tuple, I <: Tuple.InverseMap[T, Get]](
t: T
)(using Tuple.IsMappedBy[Get][T]): I =
t.map {
[G] => (g: G) => g.asInstanceOf[Get[_]].get.asInstanceOf[ExtractG[G]]
}.asInstanceOf[I]
def genericF[T <: Tuple, I <: Tuple.InverseMap[T, Get], B](
t: T,
f: I => B
)(using Tuple.IsMappedBy[Get][T]): Get[B] = Put(f(extract(t)))
And here's one using dependent types, just for fun (Scastie):
type Extract[T <: Tuple] <: Tuple = T match {
case EmptyTuple => EmptyTuple
case Get[a] *: t => a *: Extract[t]
}
type AllGs[T <: Tuple] = T match {
case EmptyTuple => DummyImplicit
case Get[_] *: t => AllGs[t]
case _ => Nothing
}
def genericF[T <: Tuple : AllGs, B](
t: T,
f: Extract[t.type] => B
): Get[B] = {
def extract[T <: Tuple](t: T): Extract[T] = t match {
case _: EmptyTuple => EmptyTuple
case (head *: tail): (Get[_] *: _) => head.get *: extract(tail)
}
Put(f(extract(t)))
}
I was hoping Extract wouldn't compile for tuples like (Put("foo"), 3), but unfortunately, AllGs is still necessary.
I have a trait that is extended by multiple subclasses
trait Sup
case class Sub[A, B](a: A, f: B => B)(implicit val ev: A =:= B) extends Sup
case class Sub2[A, B](a: A, f: B => Unit)(implicit val ev: A =:= B) extends Sup
And two functions:
def foo[A, B](a: A, f: B => B)(implicit ev: A =:= B) = f(a)
def bar[A, B](a: A, f: B => Unit)(implicit ev: A =:= B) = f(a)
Now I can do some form of dynamic dispatching and call foo if the object is a Sub and bar if the object is a Sub2.
def dispatch(obj: Sup) = {
obj match {
case Sub(a, f) => foo(a, f)
case Sub2(a, f) => bar(a, f) // type mismatch: found: Nothing => Unit. required: B => Unit
}
}
I've also tried to pass the evidence explicitly but it results in the same error:
case o # Sub2(a, f) => bar(a, f)(o.ev) // type mismatch
It is very weird that f: B => B works (I can call foo), but f: B => Unit doesn't work (I can't call bar).
Not an answer but something to think about:
case class Sub1[A, B](a: A, f: B => B)
case class Sub2[A, B](a: A, f: B => Unit)
def foo[A, B](a: A, f: B => B)(implicit ev: A =:= B) = f(a)
def bar[A, B](a: A, f: B => Unit)(implicit ev: A =:= B) = f(a)
def dispatch(obj: Any) = obj match {
case Sub1(a, f) => foo(a, f)
case Sub2(a, f) => bar(a, f) // type mismatch: found: Nothing => Unit. required: B => Unit
}
This code have the same problem as yours but Sub1 and Sub2 case classes don't even have implicit blocks.
implicit section in case class doesn't effect pattern resolution. This section is just syntax sugar for calling apply(a: A, f: B => B)(implicit val ev: A =:= B) method on Sub1/2's companion objects. Pattern matching use unapply method to match the pattern at runtime and this unapply don't even know about evidences.
But I'm still wondering why first case is compiled without having this evidence.
Edit: Adding helpful comment from #AlexeyRomanov
More type inference than type erasure. But yes, the compiler infers type Any for a and Any => Any for f and then produces and uses evidence that Any =:= Any. In the second case it infers Nothing => Unit for f, because B => Unit is contravariant in B, and fails to find Any =:= Nothing.
You can actually make it work using type variable patterns:
def dispatch(obj: Sup) = {
obj match {
case obj: Sub[a, b] => foo(obj.a, obj.f)(obj.ev)
case obj: Sub2[a, b] => bar(obj.a, obj.f)(obj.ev)
}
}
This part is an answer to the comments, because it doesn't really fit in there:
Btw, there is still one subtlety I do not get: why is B => Unit contravariant in B
what is compiler's logic for this Nothing => Unit inference staff
You need to start with function variance. X => Y is a subtype of X1 => Y1 if and only if X is a supertype of X1 and Y is a subtype of Y1. We say it's contravariant in X and covariant in Y.
So if you fix Y = Unit, what remains is just contravariant in X. Any => Unit is a subtype of String => Unit, which is a subtype of Nothing => Unit. In fact, Nothing => Unit is the most general of all B => Unit, and that's why it gets inferred in the Sub2 case.
and B => B not (since it infers Any => Any) ?
The situation with B => B is different: String => String is neither a subtype nor a supertype of Any => Any, or of Nothing => Nothing. That is, B => B is invariant. So there is no principled reason to infer any specific B, and in this case the compiler uses the upper bound for B (Any), and B => B becomes Any => Any.
Suppose I have a function-like type e.g.
trait Parser[-Context, +Out]
and I want to be able to combine multiple parsers such that the combined Context will be the most-specific type among the combined parsers' contexts. For example:
Parser[Any, Int] + Parser[String, Long] = Parser[String, (Int, Long)]
Parser[String, Int] + Parser[Any, Long] = Parser[String, (Int, Long)]
Parser[Option[Int], Foo] + Parser[Some[Int], Bar] = Parser[Some[Int], (Foo, Bar)]
Parser[String, Foo] + Parser[Int, Bar] = <should be a compile error>
To put the example in more concrete terms, suppose I have a function combiner like
def zipFuncs[A, B1, B2](f1: A => B1, f2: A => B2): A => (B1, B2) = {
a => (f1(a), f2(a))
}
and some functions like
val f1 = { a: Any => 123 }
val f2 = { a: String => 123 }
val f3 = { a: Option[Int] => 123 }
Now I can do
> zipFuncs(f1, f2)
res1: String => (Int, Int) = <function>
> zipFuncs(f1, f3)
res2: Option[Int] => (Int, Int) = <function>
> zipFuncs(f2, f3)
res3: Option[Int] with String => (Int, Int) = <function1>
But what I want is for zipFuncs(f2, f3) to not compile at all. Since String is not a subtype of Option[Int], and Option[Int] is not a subtype of String, there's no way to construct an input value for res3.
I did create a typeclass:
// this says type `T` is the most specific type between `T1` and `T2`
sealed trait MostSpecificType[T, T1, T2] extends (T => (T1, T2))
// implementation of `object MostSpecificType` omitted
def zipFuncs[A, A1, A2, B1, B2](f1: A1 => B1, f2: A2 => B2)(
implicit mst: MostSpecificType[A, A1, A2]
): A => (B1, B2) = { a: A =>
val (a1, a2) = mst(a)
f1(a1) -> f2(a2)
}
This accomplishes the goal described above, but with a really annoying problem. IntelliJ will highlight valid combinations as errors, inferring that the "most specific type (A)" is actually Nothing when it is in fact a real value. Here's the actual issue in practice.
The highlighting issue is surely a bug in IntelliJ, and google searching seems to imply that various resets/cache wipes/etc should fix it (it didn't). Regardless of the blame, I'm hoping to find an alternate approach that both satisfies my original requirement, and doesn't confuse IntelliJ.
You can achieve that using generalized type constraints:
def zipFuncs[A1, A2, B1, B2](f1: A1 => B1, f2: A2 => B2)
(implicit ev: A2 <:< A1): A2 => (B1, B2) = {
a => (f1(a), f2(a))
}
val f1 = { a: Any => 123 }
val f2 = { a: String => 123 }
val f3 = { a: Option[Int] => 123 }
zipFuncs(f1, f2) // works
zipFuncs(f1, f3) // works
zipFuncs(f2, f3) // cannot prove that Option[Int] <:< String
However, this requires the second function to use a more specific type in the input parameter than the first one. This is OK unless you also want zipFuncs(f2, f1) to work too. If you do have that requirement, I don't see any other way than doing some implicit type gymnastics similar to the ones you already do.
EDIT: See Eduardo's answer for a neat trick on achieving this.
And yes, I also had a number of situations when IntelliJ sees something as an error when in fact it is not. I know it's tedious but I don't see a way to fix the situation other than reporting an issue and waiting.
If you want this to work only when one of the types is a subtype of the other, then you can do this:
def Zip[A,X,Y](f: A => X, g: A => Y): A => (X,Y) = a => (f(a), g(a))
implicit class ZipOps[A,X](val f: A => X) extends AnyVal {
def zip[A0, Y](g: A0 => Y)(implicit ev: A0 <:< A): A0 => (X,Y) =
Zip({a: A0 => f(a)},g)
def zip[A0 >: A, Y](g: A0 => Y): A => (X,Y) =
Zip(f,g)
}
val f1: Any => Int = { a: Any => 123 }
val f2: String => Int = { a: String => 123 }
val f3: Option[Int] => Int = { a: Option[Int] => 123 }
val x1 = f1 zip f2 // works
val x1swap = f2 zip f1 // works
val x2 = f1 zip f3 // works
val x3 = f2 zip f3 // cannot prove that Option[Int] <:< String
val x3swap = f3 zip f2 // cannot prove that String <:< Option[Int]
While thinking about my previous question, I realized I ought to be able to write something like the following:
val empty: Try[B, forall types B] = Failure(new RuntimeException("empty"))
def firstSuccess[A, B](xs: Iterable[A], f: A => Try[B]): Try[B] = {
xs.foldLeft(empty)((e, a) => e.recoverWith { case _ => f(a) })
}
because a Failure is a valid Try[B] for any type B. Is there a way to achieve my "B, forall types B" in Scala?
You can use the Nothing type since everything in scala is Nothing:
val empty = Failure[Nothing](new RuntimeException("empty"))
def firstSuccess[A, B](xs: Iterable[A], f: A => Try[B]): Try[B] = {
xs.foldLeft[Try[B]](empty)((e, a) => e.recoverWith { case _ => f(a) })
}
You do have to sprinkle in a few types here and there though (added type parameter to foldLeft).
Using some structure:
object Foo {
trait Bar[B]
}
trait Foo[A, B, F <: Foo[A, B, F]] {
def baz(fun: A => Foo.Bar[B] => Unit): Unit
}
...why are existential types causing trouble:
def test[A, F <: Foo[A, _, F]](foo: F) =
foo.baz { a => b => println(b) }
The following error occurs:
<console>:38: error: type mismatch;
found : A => Foo.Bar[(some other)_$1(in type F)] => Unit
forSome { type (some other)_$1(in type F) }
required: A => (Foo.Bar[_$1(in type F)] => Unit)
foo.baz { a => b => println(b) }
^
While the following compiles:
def test[A, JamesClapperSociopath, F <: Foo[A, JamesClapperSociopath, F]](foo: F) =
foo.baz { a => b => println(b) }
It must have something to do with equivalence of existential types. The compiler probably infers b: F#_$1 and then can't figure out that the two projections are equal.
Fortunately, functions are contravariant in the parameter type, so you can just write:
def test[A, F <: Foo[A, _, F]](foo: F) =
foo.baz { a => (b: Any) => println(b) }