I'm reading about higher kinded types in Scala and I can't wrap my head around the use of the underscore.
For example:
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B) : F[B]
}
val listFunctor = new Functor[List] {
override def map[A, B](fa: List[A])(f: A => B) = fa.map(f)
}
listFunctor.map(List(1,2,3))(_ + 3)
If I use a parametrised type A in the trait, I get the same result:
trait Functor2[F[A]] {
def map[A, B](fa: F[A])(f: A => B) : F[B]
}
val listFunctor2 = new Functor2[List] {
override def map[A, B](fa: List[A])(f: A => B) = fa.map(f)
}
listFunctor2.map(List(1,2,3))(_ + 3)
Is there a significant difference between the two examples?
There's no difference between those two ways to express the fact that Functor2 is parametrized over a type constructor. If you notice, in the latter case, A is just a placeholder, as it hasn't been declared elsewhere.
I generally prefer to use the F[_] syntax as it clarifies we want a type constructor and we don't have accidental overlapping in naming between the placeholder and types as referred within the class: in the latter case, even though they happen to refer to the same type parameter, there is no enforced constraint between the A in the constructor signature and the A in the map method signature, and this can also lead to some confusion regarding the nature of the two A and how they are related to each other.
Related
In Scala 2.13 and Cats, the following works fine:
import cats.implicits._
Traverse[Option]
However the following fails:
import cats.implicits._
Traverse[Some]
I would like for the latter to work, not only for Traverse of Option subclasses, but for any type that has a parent that there exists a given typeclass for.
I have tried creating a method with implicit proof <:< but can't quite get it to work.
If you really understand what you do you can define necessary instance manually (when it exists).
Some is isomorphic to Id.
implicit val someTraverse: Traverse[Some] = new Traverse[Some] {
override def traverse[G[_]: Applicative, A, B](fa: Some[A])(f: A => G[B]): G[Some[B]] = f(fa.value).map(Some(_))
override def foldLeft[A, B](fa: Some[A], b: B)(f: (B, A) => B): B = f(b, fa.value)
override def foldRight[A, B](fa: Some[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = f(fa.value, lb)
}
Generally this is not possible. If F is a functor and G[T] <: F[T] for all T then G is not necessarily a functor.
Calling generic function with Functor using subclass (cats/scalaz)
Also sometimes you can derive type classes using kittens.
Why can find `Functor` instance for Tree but not for Branch or Leaf?
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'm learning category theory.
I understand the concept of reader monad, it's even pretty easy to implement:
case class Reader[DEP, A](g: DEP => A) {
def apply(dep: DEP): A = g(dep)
def map[B](f: A => B): Reader[DEP, B] = Reader(dep => f(apply(dep)))
def flatMap[B](f: A => Reader[DEP, B]): Reader[DEP, B] = Reader(dep => f(apply(dep)) apply dep)
}
However, I have problems implementing it with constraint to some generic Monad interface, i.e
trait Monad[A] {
def pure(a: A): Monad[A]
def map[B](f: A => B): Monad[B]
def flatMap[B](f: A => Monad[B]): Monad[B]
}
let's forgot for a second that there's an applicative or functor and let's just put these 3 methods here.
Now, having this interface I have problems implementing ReaderMonad.
map method is pretty straighforward, but what about pure and flatMap?
What does it even mean to have pure on Reader?
To implement flatMap, I need to have a function from A to Reader[DEP, B], but I have A => Monad[B], thus I'm not possible to access apply.
case class Reader[DEP, A](g: DEP => A) extends Monad[A] {
def apply(dep: DEP): A = g(dep)
override def pure(a: A): Reader[DEP, A] = Reader(_ => a) // what does it even mean in case of Reader
override def map[B](f: (A) => B): Reader[DEP, B] = Reader(dep => f(apply(dep)))
override def flatMap[B](f: (A) => Monad[B]): Reader[DEP, B] = ??? // to implement it, I need f to be (A) => Reader[DEP, B], not (A) => Monad[B]
}
Is it possible to implement it this way in scala? I tried to play around with self bound types, but it didn't work either.
I know libraries like scalaz or cats uses typeclasses to implement these types, but this is just for educational purpose.
As you've discovered when trying to implement flatMap, the problem with declaring the Monad trait as you have is you lose the particular monad type you're defining when chaining operations. The usual way of defining the Monad trait is to parameterise it by the type constructor the monad instance is being defined for e.g.
trait Monad[M[_]] {
def pure[A](a: A): M[A]
def map[A, B](f: A => B, m: M[A]): M[B]
def flatMap[A, B](f: A => M[B], m : M[A]): M[B]
}
So M is a unary type constructor such as List or Option. You can think of a Reader[DEP, A] as being a computation which depends on some environment type DEP which returns a value of type A. Since this has two type parameters you need to fix the environment parameter type when defining the monad instance:
case class Reader[DEP, A](g: DEP => A)
class ReaderMonad[DEP]() extends Monad[({type t[X] = Reader[DEP, X]})#t] {
def pure[A](a: A) = Reader[DEP, A](_ => a)
def map[A, B](f: A => B,m: Reader[DEP,A]): Reader[DEP,B] = Reader(env => f(m.g(env)))
def flatMap[A, B](f: A => Reader[DEP,B],m: Reader[DEP,A]): Reader[DEP,B] = Reader(env => f(m.g(env)).g(env))
}
({type t[X] = Reader[DEP, X]})#t is a type lambda used to partially apply one of the two parameters for Reader[DEP, A].
Now pure returns a Reader which ignores the environment and returns the given value directly.
flatMap constructs a Reader which when run will run the inner computation, use the result to construct the next computation and run it with the same environment.
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.
I had a query about what is the difference between parameterising the class vs parameterising the function.
I have provided implementation of a Functor as follows:
trait Functor[F[_],A,B] {
def map(fa: F[A]) (f: A => B) : F[B]
}
and the other where the function is parameterised as follows:
trait Functor[F[_]] {
def map[A,B](fa: F[A]) (f: A => B) : F[B]
}
Where are the cases where we should use one over another?
Another follow up question:
Why do we pass the argument to functor as F[_] and not as F[A] or F[B]. What cases arise when we use either F[A] or F[B]?
Always prefer the second one. With the first one you can implement instances as nonsensical as:
trait WrongFunctor[F[_],A,B] {
def map(fa: F[A])(f: A => B) : F[B]
}
case class notEvenRemotelyAFunctor[A]() extends WrongFunctor[List,A,Int] {
def map(fa: List[A])(f: A => Int) : List[Int] =
if(f(fa.head) < 4) List(3) else List(4)
}
type Id[X] = X
case object ILikeThree extends WrongFunctor[Id, Int, Int] {
def map(fa: Int)(f: Int => Int): Int = if(fa == 3) 3 else f(fa)
}
Even if you do things right, you'd need for a fixed functor implementation one object per different types at which you want to use fmap. But the important point is that the second one at least makes it harder to write that kind of wrong "functors"; less non-functors will slip by:
trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A => B) : F[B]
}
case object ILikeThreeAgain extends Functor[Id] {
def map[A,B](fa: A)(f: A => B) : B =
??? // how do I write the above here?
}
The keywords here are parametricity and parametric polymorphism. The intuition is that if something is defined generically, you can derive properties it will satisfy just from the types involved. See for example Bartosz Milewski blog - Parametricity: Money for Nothing and Theorems for Free for a good explanation, or the canonical Theorems for free paper.
Follow-up question
Another follow up question: Why do we pass the argument to functor as F[_] and not as F[A] or F[B]. What cases arise when we use either F[A] or F[B]?
Because that's part of what a functor is; it is a "constructor":
for each input type A it gives you as output another type F[A]
and for each function f: A => B, another function fmap(f): F[A] => F[B] satisfying fmap(id[A]) == id[F[A]] and fmap(f andThen g) == fmap(f) andThen fmap(g)
So for 1. you need a way of representing functions on types; and that's what F[_] is.
Note that having a map method like in your signature is in this case equivalent to fmap:
trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A => B) : F[B]
def fmap[A,B](f: A => B): F[A] => F[B] =
{ fa => map(fa)(f) }
def mapAgain[A,B](fa: F[A])(f: A => B) : F[B] =
fmap(f)(fa)
}
Now for how this links with real category theory:
Instances of your Functor[F[_]] trait above are meant to represent Scala-enriched functors
F: Scala → Scala
Let's unpack this.
There's a (usually implicitly defined) category Scala with objects types, and morphisms functions f: A ⇒ B. This category is cartesian closed, where the internal hom is the type A ⇒ B, and the product (A,B). We can work then with Scala-enriched categories and functors. What's a Scala-enriched category? basically one that you can define using Scala the language: you have
a set of objects (which you'd need to represent as types)
for each A,B a type C[A,B] with identities id[X]: C[X,Y] and composition andThen[X,Y,Z]: (C[X,Y], C[Y,Z]) => C[X,Z] satisfying the category axioms
Enriched functors F: C → D are then
a function from the objects of C to those of D, A -> F[A]
for each pair of objects A,B: C a morphism in Scala i.e. a function fmap: C[A,B] => C[F[A], F[B]] satisfying the functor laws fmap(id[A]) == id[F[A]] and fmap(f andThen g) == fmap(f) andThen fmap(g)
Scala is naturally enriched in itself, with Scala[X,Y] = X => Y, and enriched functors F: Scala → Scala are what instances of your Functor[F[_]] trait are meant to represent.
Of course this needs all sort of qualifications about how Scala breaks this and that, morphism equality etc. But the moral of the story is: your base language L (like Scala in this case) is likely trying to be a cartesian-closed (or at least symmetric monoidal-closed) category, and functors definable through it correspond to L-enriched functors.