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.
Related
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.
I'm trying to understand what monads are (not just in scala, but by example using scala). Let's consider the most (in my opinion) simple example of a monad:
scala.Some
As some articles state, every monad in its classic sense should preserve some rules for the flatMap and unit functions.
Here is the definition from scala.Some
#inline final def flatMap[B](f: A => Option[B]): Option[B]
So, understand it better I want to understand it from the category theory standpoint. So, we're considering a monad and it's supposed to be a functor (but between what?).
Here we have to category Option[A] and Option[B] and the flatMap along with the f: A => Option[B] passed into it is supposed to define a Functor between them. But in the tranditional category definition it's a functor from a category to itself.
The category is the category of scala types, where the objects are types and the arrows are functions between values of those types. Option is an endofunctor on this category. For each object (i.e. type) in the Scala category, the Option type constructor maps each type A into a type Option[A].
In addition it maps each arrow f: A => B into an arrow fo: Option[A] => Option[B] which is what Option.map does.
A Monad is a Functor M along with two operations, unit: A => M[A] and join: M[M[A]] => M[A]. For Option, unit(x: A) = Some(x) and join can be defined as:
def join[A](o: Option[Option[A]]): Option[A] = o match {
case None => None
case Some(i) => i
}
flatMap can then be defined as, flatMap(f, m) = join(map(f, m)). Alternatively the monad can be defined using unit and flatMap and join defined as join(m) = flatMap(id, m).
I have already understood how to create a Functor on my own case class when its parameters are of the same type A.
case class MyCaseClass[A](a: A, b: A)
val local = MyCaseClass[String]("One", "Two")
implicit val myCaseClassFunctor = new Functor[MyCaseClass]{
def map[A, B](fa: MyCaseClass[A])(f: A => B) = MyCaseClass(f(fa.a), f(fa.b))
}
val F = Functor[MyCaseClass]
val res = F.map(local)(_ + ".")
println(res)
In real life problems, a case class isn't going to be formed by parameters of the same type but different ones.
If the case clase gets to be something as simple as
case class MyCaseClass[A, B](a: A, b: B)
Is it possible to build a Functor for it?
My guess is I can't ever since the definition of map is quite clear with repect to its types parameters
def map[A, B]
But I have to ask ever since this is my first time programming a Functor.
For a class with two parameters there are two Functors possible: one that maps the first field and another one that maps the second field. While this is the solution, I would recommend to define a Bifunctor because it is more general:
implicit val bifunctor = new Bifunctor[MyCaseClass] {
/** `map` over both type parameters. */
def bimap[A, B, C, D](fab: MyCaseClass[A, B])(f: A => C, g: B => D): MyCaseClass[A, B] =
MaCaseClass(f(fab.a), g(fab.b))
}
Having a Bifunctor instance you can easily get a Functor for either the left of the right parameter:
implicit def functor[X] = bifunctor.leftFunctor[X]
// or
implicit def functor[X] = bifunctor.rightFunctor[X]
Functor requires types with a single type parameter, yes, but you can use so-called type lambdas:
implicit def myCaseClassFunctor[C] = new Functor[({type f[X] = MyCaseClass[C, X]})#f]{
def map[A,B](fa: MyCaseClass[C,A])(f:A=>B) = MyCaseClass(fa.a, f(fa.b))
}
I'm working on this Functional Programming in Scala exercise:
// But what if our list has an element type that doesn't have a Monoid instance?
// Well, we can always map over the list to turn it into a type that does.
As I understand this exercise, it means that, if we have a Monoid of type B, but our input List is of type A, then we need to convert the List[A] to List[B], and then call foldLeft.
def foldMap[A, B](as: List[A], m: Monoid[B])(f: A => B): B = {
val bs = as.map(f)
bs.foldLeft(m.zero)((s, i) => m.op(s, i))
}
Does this understanding and code look right?
First I'd simplify the syntax of the body a bit:
def foldMap[A, B](as: List[A], m: Monoid[B])(f: A => B): B =
as.map(f).foldLeft(m.zero)(m.ops)
Then I'd move the monoid instance into its own implicit parameter list:
def foldMap[A, B](as: List[A])(f: A => B)(implicit m: Monoid[B]): B =
as.map(f).foldLeft(m.zero)(m.ops)
See the original "Type Classes as Objects and Implicits" paper for more detail about how Scala implements type classes using implicit parameter resolution, or this answer by Rex Kerr that I've also linked above.
Next I'd switch the order of the other two parameter lists:
def foldMap[A, B](f: A => B)(as: List[A])(implicit m: Monoid[B]): B =
as.map(f).foldLeft(m.zero)(m.ops)
In general you want to place the parameter lists containing parameters that change the least often first, in order to make partial application more useful. In this case there may only be one possible useful value of A => B for any A and B, but there are lots of values of List[A].
For example, switching the order allows us to write the following (which assumes a monoid instance for Bar):
val fooSum: List[Foo] => Bar = foldMap(fooToBar)
Finally, as a performance optimization (mentioned by stew above), you could avoid creating an intermediate list by moving the application of f into the fold:
def foldMap[A, B](f: A => B)(as: List[A])(implicit m: Monoid[B]): B =
as.foldLeft(m.zero) {
case (acc, a) => m.op(acc, f(a))
}
This is equivalent and more efficient, but to my eye much less clear, so I'd suggest treating it like any optimization—if you need it, use it, but think twice about whether the gains are really worth the loss of clarity.
Is there any quick way to use as a concrete function (of type, say, (A) => B) as a PartialFunction[A, B]? The most concise syntax I know of is:
(a: A) => a match { case obj => func(obj) }
Is there an implicit conversion anywhere, something like:
implicit def funcAsPartial[A, B](func: A => B) = new PartialFunction[A, B] {
def isDefinedAt(a: A) = true
def apply(a: A) = func(a)
}
I guess I just wrote what I was looking for, but does this already exist in the Scala libraries?
Doing this with an implicit conversion is dangerous, for the same reason that (A) => B should not inherit from PartialFunction[A, B]. That is, the contract of PartialFunction guarantees that you can safely* call apply wherever isDefinedAt returns true. Function1's contract provides no such guarantee.
Your implicit conversion will result in a PartialFunction that violates its contract if you apply it to a function that is not defined everywhere. Instead, use a pimp to make the conversion explicit:
implicit def funcAsPartial[A, B](f: A => B) = new {
/** only use if `f` is defined everywhere */
def asPartial(): PartialFunction[A, B] = {
case a => f(a)
}
def asPartial(isDefinedAt: A => Boolean): PartialFunction[A, B] = {
case a if isDefinedAt(a) => f(a)
}
}
// now you can write
val f = (i: Int) => i * i
val p = f.asPartial // defined on all integers
val p2 = f.asPartial(_ > 0) // defined only on positive integers
* As discussed in the comments, it may not be entirely clear what "safety" means here. The way I think about it is that a PartialFunction explicitly declares its domain in the following precise sense: if isDefinedAt returns true for a value x, then apply(x) can be evaluated in a way that is consistent with the intent of the function's author. That does not imply that apply(x) will not throw an exception, but merely that the exception was part of the design of the function (and should be documented).
No, I tried to find one a few months ago and ended up writing my own that's essentially the same as yours.