I am trying to implement a cats Monad instance for a type that has multiple type parameters. I looked at the cats Either instance to see how it was done there. Part of the Either Monad instance code from cats is copied below:
import cats.Monad
object EitherMonad {
implicit def instance[A]: Monad[Either[A, ?]] =
new Monad[Either[A, ?]] {
def pure[B](b: B): Either[A, B] = Right(b)
def flatMap[B, C](fa: Either[A, B])(f: B => Either[A, C]): Either[A, C] =
fa.right.flatMap(f)
}
}
It fails to compile with the error: error: not found: type ?
What is the ? type and how can I use it when creating instances for my own types?
It is special syntax for so-called type lambdas that is added by the kind projector plugin.
Either[A, ?]
is a shortcut for
({type L[X] = Either[A, X]})#L
The whole code desugars to
import cats.Monad
object EitherMonad {
implicit def instance[A]: Monad[({type L[X] = Either[A, X]})#L] = new Monad[({type L[X] = Either[A, X]})#L] {
def pure[B](b: B): Either[A, B] = Right(b)
def flatMap[B, C](fa: Either[A, B])(f: B => Either[A, C]): Either[A, C] =
fa.right.flatMap(f)
}
}
Type lambdas look frightening, but they are essentially a very simple concept. You have a thing that takes two type parameters, like Either[A, B]. You want to provide a monad instance for Either, but trait Monad[F[_]] takes only one type parameter. But in principle that's OK, since your monad instance is only concerned with the second (the "right") type argument anyway. A type lambda is just a way to "fix" the first type argument so you have the right shape.
If you would do the same thing at value level, you wouldn't even think about it twice. You got a function of two arguments
val f: (Int, Int) => Int = ...
And something you want to pass f to, which only takes 1 argument
def foo(x: Int => Int) = ...
The only way to make things fit is to fix one of the arguments
foo(x => f(1, x))
And that's exactly what a type lambda does at type level.
Related
this is a direct translation of my scala2 code to scala3
trait Narrow[F[_], A, B <: A: ClassTag]:
def apply(fa: F[A]): F[B]
extension [F[_], A] (fa: F[A]):
def narrow[B: ClassTag] (using op: Narrow[F, A, B]): F[B] = op(fa)
I need to specify the type of the narrow operation at the call site, however extension methods do not allow that syntax. what are there the best workarounds for this limitation?
the purpose of this is to be able to narrow the type in a collection / try / whatever. the narrow type class will flatmap whatever is inside, compare the runtime type, if it matches wrap that B in an F, or otherwise return an empty F
trait A
trait B extends A
object A extends A
object B extends B
val bb: List[B] = List(A, A, B, B, A)
.narrow[B]
assert(bb == List(B, B))
You could use a polymorphic function, if you can handle the ugliness:
extension [F[_], A] (fa: F[A]):
def narrow() = [B <: A] => (using op: Narrow[F, A, B]) => op(fa)
You can then call it with foo.narrow()[String]. Here it is in Scastie.
The narrow() is necessary, because without it, the type argument would go to the extension and not the polymorphic function.
In the future, Scala 3 may allow type arguments directly to methods inside extensions, but right now, you can keep using your Scala 2 implicit class and change it after the next release:
implicit class NarrowOps[F[_], A](fa: F[A]):
def narrow[B <: A](using op: Narrow[F, A, B]) = op(fa)
Scastie
Side note: You don't need B: ClassTag again in your extension, although I believe you do need to use the bound B <: A.
I wasnt able to live with the () on the call site. I decided trying a implicit conversion to a type with an apply method with just a type parameter.
trait NarrowTypeClass[F[_], A, B <: A: ClassTag]:
def apply(fa: F[A]): F[B]
given [F[_], A] as Conversion[F[A], Narrowable[F, A]] = Narrowable(_)
sealed class Narrowable [F[_], A] (fa: F[A]):
def narrow[B <: A: ClassTag] (using op: NarrowTypeClass[F, A, B]): F[B] = op(fa)
this seems to do the trick
I have been reviewing Scala knowledge and have been going circles about the variance/lower bound.
In the 'functional programming in scala' book, the Either type, it has below signature/exercise (Implement versions of flatMap, orElse on Either that operate on the Right value).
sealed trait Either[+E,+A] {
def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = ???
def orElse[EE >: E, B >: A](b: => Either[EE, B]): Either[EE, B] = ???
}
and the note of the book says
when mapping over the right side, we must promote the left type parameter to some supertype, to satisfy the +E variance annotation. similarly for 'orElse'
My question are:
why do not we have to say B >: A in the flatMap function? we do not need to satisfy +A?
why does orElse signature requires B >: A?
I understand method parameters count as contravariant positions, so we could not possibly have A or E in the method's parameter. i.e. the 'return type' of the f or b could not have E or A in it.
Maybe I am missing something, in relation to the fundamental knowledge of subtyping/lower bound/function as parameter.
Please help me to understand it with maybe some concrete examples.
p.s. Most articles, about variance or upper/lower bound, I found have only 1 type parameter in the class/trait.
why do not we have to say B >: A in the flatMap function? we do not
need to satisfy +A?
flatMap does not put any constraint on the type produced by f: A => Either[EE, B]. This means, for example, that we can have a Either[Throwable, String] and use flatMap to convert it to an Either[Throwable, Int]. Note that the only relationship between String and Int is through Any.
why does orElse signature requires B >: A
When we say: "Give me the left hand side, or else give me the right hand side" we usually want both types to align such that our "fallback", via orElse, will provide a meaningful fallback.
For example, let's use the above example and say we want to take an Either[Throwable, String] and convert it to a Either[Throwable, Int] using flatMap:
val either: Either[Throwable, String] = Right("42")
val res: Either[Throwable, Int] = either.flatMap(str => Try(str.toInt).toEither)
This will work when our String is 42, but if it's not a valid Int, we'll get a Left[Throwable] back. Now let's decide that in case parsing fails, we always want to return -1 as a default value (of course there are better ways to model this, but stick with me). We can leverage orElse for this:
val either: Either[Throwable, String] = Right("42")
val res: Either[Throwable, Int] = either.flatMap(str => Try(str.toInt).toEither).orElse(Right(-1))
This way, the relationship between the LHS and the RHS is preserved, and we receive a sensible value as the result. If B was not constrained to A at all, we would usually get a supertype far up in the type hierarchy, such as AnyRef or Any.
One additional thing about the EE >: E constraint. Since E is covariant, if we tried to use it as a type parameter for the flatMap function:
sealed trait Either[+E, +A] {
def flatMap[B](f: A => Either[E, B]): Either[E, B] = ???
}
The compiler would yell at us:
Error:(7, 20) covariant type E occurs in contravariant position in
type A => Either[E,B] of value f
def flatMap[B](f: A => Either[E, B]): Either[E, B] = ???
That's because covariant types cannot "go in" to the method, they can only be used in the return type, contrary to contravariant type parameters which "go in", but cannot be used in the result type.
If Either were invariant, signatures would be
sealed trait Either[E,A] {
def flatMap[B](f: A => Either[E, B]): Either[E, B] = ???
def orElse(b: => Either[E, A]): Either[E, A] = ???
}
There is no connection between A and B here.
Now if we make Either covariant with respect to E we have to add EE >: E
sealed trait Either[+E,A] {
def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B] = ???
def orElse[EE >: E](b: => Either[EE, A]): Either[EE, A] = ???
}
Otherwise if we make Either covariant with respect to A we have to add AA >: A
sealed trait Either[E,+A] {
def flatMap[B](f: A => Either[E, B]): Either[E, B] = ???
def orElse[AA >: A](b: => Either[E, AA]): Either[E, AA] = ???
}
Just AA is denoted as B.
In actual case Either is covariant with respect to both type parameters so this is the combination of above.
I guess now it's clear that B in flatMap and B in orElse are different.
In scala, I have a container type with an error case and held type:
case class Extract[E, A](runView: View => EitherT[Future, E, (A, View)]) {...}
Which I intend to use as a sort of asynchronous state monad. I have implemented all of the map, flatMap utility methods for this.
I now want to mark this type as being a member of the Monad type class in scalaz, and I'm struggling to work out the syntax for specifying that the type parameter I want the type monadic over is the A type, since Monad takes a type with a single type parameter. Below is an incorrect attempt I made.
implicit def extractInterface[E] = new Monad[Extract[E, A]] {
def point[A](a: => A): Extract[E, A] = {...}
def bind[A, B](fa: Extract[E, A])(f: (A) => Extract[E, B]): Extract[E, B] = fa.flatMap(f)
}
I have tried looking at the scalaz instances for E \/ A, but they use a ? type parameter, for which I cannot find a definition.
Is there a syntactic trick to do what I want?
You should use type lambda
implicit def extractInterface[E] = new Monad[({ type λ[A] = Extract[E, A] })#λ] {
...
}
Alternatively you can use kind-projector plugin.
implicit def extractInterface[E] = new Monad[Extract[E, ?]] {
...
}
What is the ? type?
I am trying to write a Functor for Either for academic purposes in Scala. With help of higher-kinded types and type-projections, I managed to write an implementation for Either.
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
object Functor {
implicit def eitherFunctor[A] = new Functor[({type λ[α] = Either[A, α]})#λ] {
override def map[B, C](fa: Either[A, B])(f: B => C) = fa.map(f)
}
}
def mapAll[F[_], A, B](fa: F[A])(f: A => B)(implicit fe: Functor[F]): F[B] = fe.map(fa)(f)
val right: Either[String, Int] = Right(2)
mapAll(right)(_ + 2)
Now, the code above does not compile. I am not sure of the reason but the compilation error that I am getting is given below -
Error:(19, 16) type mismatch;
found : Either[String,Int]
required: ?F[?A]
Note that implicit conversions are not applicable because they are ambiguous:
both method ArrowAssoc in object Predef of type [A](self: A)ArrowAssoc[A]
and method Ensuring in object Predef of type [A](self: A)Ensuring[A]
are possible conversion functions from Either[String,Int] to ?F[?A]
mapAll(right)(_ + 2)
Can someone point what I am not doing right in the code above?
PS: Please do not suggest me to use kind-projector.
You've just been bitten by SI-2712. If you're using Scala >= 2.12.2 just add this line to your build.sbt:
scalacOptions += "-Ypartial-unification"
For other Scala versions you can use this plugin.
Either[+A, +B] is expecting two type parameters(as #Ziyang Liu said), so for your example actually need BiFunctor not Functor, BiFunctor accept two functors and bound the two types.
there is a Bifunctor from Scalaz
trait Bifunctor[F[_, _]] { self =>
////
/** `map` over both type parameters. */
def bimap[A, B, C, D](fab: F[A, B])(f: A => C, g: B => D): F[C, D]
So you can use this Bifunctor like:
Bifunctor[Either].bimap(Right(1): Either[String, Int])(_.toUpperCase, _ + 1).println
Hope it's helpful for you.
As others said, what the compiler is trying to tell you is that the shapes of your types don't match. When you require an F[_], you're requiring a type constructor with a single type parameter, which Either[A, B] doesn't satisfy.
What we need to do is apply a type lambda when applying mapAll, same as we did when we created the instance of the Either functor:
val right: Either[String, Int] = Right(2)
mapAll[({type λ[α]=Either[String, α]})#λ, Int, Int](right)(_ + 2)
We've now squeezed in String and fixed it as the first argument, allowing the type projected type to only need to satisfy our alpha, and now the shapes match.
Of course, we can also use a type alias which would free us from specifying any additional type information when applying mapAll:
type MyEither[A] = Either[String, A]
val right: MyEither[Int] = Right(2)
mapAll(right)(_ + 2)
I am dipping my toes in higher kinded types, exploring a very basic Scala example:
trait Mappable[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
object Mappable {
implicit object MappableOption extends Mappable[Option] {
def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa.map(f)
}
implicit object MappableSeq extends Mappable[Seq] {
def map[A, B](fa: Seq[A])(f: A => B): Seq[B] = fa.map(f)
}
}
def bananaTuple[F[_], T](f: F[T])(implicit F: Mappable[F]): F[(String, T)] =
F.map(f)(("banana", _))
This works:
bananaTuple(Option(42)) // Some((banana,42))
bananaTuple(Seq(42)) // List((banana,42))
But this does not compile:
bananaTuple(Some(42))
bananaTuple(List(42))
The compile errors I get:
could not find implicit value for parameter F: ch.netzwerg.hkt.HigherKindedTypes.Mappable[Some] bananaTuple(Some(42))
not enough arguments for method bananaTuple: (implicit F: ch.netzwerg.hkt.HigherKindedTypes.Mappable[Some])Some[(String, Int)]. Unspecified value parameter F. bananaTuple(Some(42))
How can I bring variance into the game?
We can make this work with a little more parameteric polymorphism:
object MappableExample {
trait Mappable[F[_]] {
type Res[_]
def map[A, B](f: A => B)(c: F[A]): Res[B]
}
implicit def seqMappable[C[X] <: Seq[X]] = new Mappable[C] {
type Res[X] = Seq[X]
override def map[A, B](f:A => B)(c: C[A]): Seq[B] = c.map(f)
}
implicit def optionMappable[C[X] <: Option[X]]: Mappable[C] = new Mappable[C] {
type Res[X] = Option[X]
override def map[A, B](f: A => B)(c: C[A]): Option[B] = c.map(f)
}
def map[A, B, C[_]](xs: C[A])(f: A => B)(implicit mappable: Mappable[C]): mappable.Res[B] = {
mappable.map(f)(xs)
}
def main(args: Array[String]): Unit = {
println(map(List(1,2,3))(("banana", _)))
println(map(Some(1))(("banana", _)))
}
}
Yields:
List((banana,1), (banana,2), (banana,3))
Some((banana,1))
The compiler now infers Some as Mappable[Some]#Res[Int] and Mappable[List]#Res[Int] which is quite ugly. One would expect the compiler to actually be able to infer the right type without needing for any co/contravariance on the Mappable trait, which we can't do since we're using it in an invariant position.
Subtype polymorphism allows us to pass values of a certain type or any of its subtypes to a method. If a method takes a value of type Fruit, we can also pass an Apple inside (an apple is a fruit after all). So if you want to be able to pass a Mappable.MappableOption to your bananaTuple method, you have to make that MappableOption a subtype of MappableSome (since the type of your first parameter of bananaTuple dictates the implicit one). This means that you want your Mappable contravariant (if Some <: Option, then Mappable[Some] >: Mappable[Option]).
But you cannot have Mappable[F[_]] contravariant in F because F appears in covariant position of map (as a function parameter). Note that F also appears in contravariant position of map (as a return value).
If you manage to make Mappable[F[_]] contravariant in F, it should work, but I'm not sure if making it contravariant makes sense. That is, if you want a subtype relationship such as e.g. Apple <: Fruit to result in Mappable[Apple] >: Mappable[Fruit] (this would not compile since Apple and Fruit are not type constructors, but I'm just using simple types to make a point here).
Making a type contravariant in its type and solving the problem of contravariant type appearing in covariant position is a common problem and perhaps it's better if you search for it elsewhere (here is one example). I still think that it's better to provide an implicit object for every type you want to use, that is, to provide separate implicit objects for e.g. Seq and List.