After I solidified my understanding of why a parameter in a method is a contravariant position with SO post scala contravariant position on method
I am looking at List[+A] in scala and I see this method which is fine since the parameter position is contravariant
def contains[A1 >: A](elem: A1)
What I don't get is the :: method definition which seems like that would not compile
def ::(x: A): List[A]
What is going on with this method? oh, they hide the real signature?
def ::[B >: A](x: B): List[B]
ok, then why do they hide the real signature?
Scaladoc entries marked [use case] are presenting the most common cases in terms of type parameters first, often leading to a simpler construction for beginners. If you expand that, you get the full signature.
For a type Foo[+A], you can implement methods that take A-like arguments by introducing a type parameter B >: A, because now you cannot provide a value that is a sub-type of A which would violate the type-soundness. In most cases you will prepend to a List[A] other elements of type A, therefore the doc simplification.
Related
I wonder if anyone could explain the inferencing rule in this particular case below, and most importantly it's rational/implication ?
case class E[A, B](a: A) // class E
E(2) // E[Int,Nothing] = E(2)
Note that I could have wrote E[Int](2). What matter to me is why is the second parameter type inferred to be Nothing (i.e. Bottom type) instead of let say Any for instance ? Why is that and What's the rational/Implication ?
Just to give some context, this is related to the definition of Either and how it works for Left and Right. Both are defined according to the pattern
final case class X[+A, +B](value: A) extends Either[A, B]
Where you instantiate it let say as Right[Int](2) and the type inferred is Right[Nothing, Int] and by extension Either[Nothing, Int]
EDIT1
There is consistency here, but i still can figure out the rational. Below is the same definition with a contra-variant paramete:
case class E[A, -B](a: A)// class E
E(2) // E[Int, Any] = E(2)
Hence we do have the same thing the other way around when it is contra-variant, and that make the all behavior or inference rule, coherent. However the rational for this i am not sure ....
Why not the opposite rule i.e. infer Any when Co-Variant/Invariant and Nothing when Contra-Variant ?
EDIT2
In the light of #slouc Answer, which make good sense, i'm left with still understanding what and why the compiler is doing what it is doing. The example below illustrate my confusion
val myleft = Left("Error") // Left[String,Nothing] = Left(Error)
myleft map { (e:Int) => e * 4} // Either[String,Int] = Left(Error)
First the compiler fix the type to something that "for sure work" to reuse the conclusion of #slouc (albeit make more sense in the context of a Function) Left[String,Nothing]
Next the compile infer myleft to be of type Either[String,Int]
given map definition def map[B](f: A => B): Either[E, B], (e:Int) => e * 4 can only be supplied if myleft is actually Left[String,Int] or Either[String,Int]
So in other words, my question is, what is the point of fixing the type to Nothing if it is to change it later.
Indeed the following does not compile
val aleft: Left[String, Nothing] = Left[String, Int]("Error")
type mismatch;
found : scala.util.Left[String,Int]
required: Left[String,Nothing]
val aleft: Left[String, Nothing] = Left[String, Int]("Error")
So why would I infer to a type, that normally would block me to do anything else over variable of that type (but for sure works in term of inference), to ultimately change that type, so i can do something with a variable of that inferred type.
EDIT3
Edit2 is a bit misunderstanding and everything is clarified in #slouc answer and comments.
Covariance:
Given type F[+A] and relation A <: B, then the following holds: F[A] <: F[B]
Contravariance:
Given type F[-A] and relation A <: B, then the following holds: F[A] >: F[B]
If the compiler cannot infer the exact type, it will resolve the lowest possible type in case of covariance and highest possible type in case of contravariance.
Why?
This is a very important rule when it comes to variance in subtyping. It can be shown on the example of the following data type from Scala:
trait Function1[Input-, Output+]
Generally speaking, when a type is placed in the function/method parameters, it means it's in the so-called "contravariant position". If it's used in function/method return values, it's in the so-called "covariant position". If it's in both, then it's invariant.
Now, given the rules from the beginning of this post, we conclude that, given:
trait Food
trait Fruit extends Food
trait Apple extends Fruit
def foo(someFunction: Fruit => Fruit) = ???
we can supply
val f: Food => Apple = ???
foo(f)
Function f is a valid substitute for someFunction because:
Food is a supertype of Fruit (contravariance of input)
Apple is a subtype of Fruit (covariance of output)
We can explain this in natural language like this:
"Method foo needs a function that can take a Fruit and produce a
Fruit. This means foo will have some Fruit and will need a
function it can feed it to, and expect some Fruit back. If it gets a
function Food => Apple, everything is fine - it can still feed it
Fruit (because the function takes any food), and it can receive
Fruit (apples are fruit, so the contract is respected).
Coming back to your initial dilemma, hopefully this explains why, without any extra information, compiler will resort to lowest possible type for covariant types and highest possible type for contravariant ones. If we want to supply a function to foo, there's one that we know surely works: Any => Nothing.
Variance in general.
Variance in Scala documentation.
Article about variance in Scala (full disclosure: I wrote it).
EDIT:
I think I know what's confusing you.
When you instantiate a Left[String, Nothing], you're allowed to later map it with a function Int => Whatever, or String => Whatever, or Any => Whatever. This is precisly because of the contravariance of function input explained earlier. That's why your map works.
"what is the point of fixing the type to Nothing if it is to change it
later?"
I think it's a bit hard to wrap your head around compiler fixing the unknown type to Nothing in case of contravariance. When it fixes the unknown type to Any in case of covariance, it feels more natural (it can be "Anything"). Because of the duality of covariance and contravariance explained earlier, same reasoning applies for contravariant Nothing and covariant Any.
This is a quote from
Unification of Compile-Time and Runtime Metaprogramming in Scala
by Eugene Burmako
https://infoscience.epfl.ch/record/226166 (p. 95-96)
During type inference, the typechecker collects constraints on missing
type arguments from bounds of type parameters, from types of term
arguments, and even from results of implicit search (type inference
works together with implicit search because Scala supports an analogue
of functional dependencies). One can view these constraints as a
system of inequalities where unknown type arguments are represented as
type variables and order is imposed by the subtyping relation.
After collecting constraints, the typechecker starts a step-by-step
process that, on each step, tries to apply a certain transformation to
inequalities, creating an equivalent, yet supposedly simpler system of
inequalities. The goal of type inference is to transform the original
inequalities to equalities that represent a unique solution of the
original system.
Most of the time, type inference succeeds. In that
case, missing type arguments are inferred to the types represented by
the solution.
However, sometimes type inference fails. For example,
when a type parameter T is phantom, i.e. unused in the term parameters
of the method, its only entry in the system of inequalities will be
L <: T <: U, where L and U are its lower and upper bound respectively.
If L != U, this inequality does not have a unique solution, and that
means a failure of type inference.
When type inference fails, i.e.
when it is unable to take any more transformation steps and its
working state still contains some inequalities, the typechecker breaks
the stalemate. It takes all yet uninferred type arguments, i.e. those
whose variables are still represented by inequalities, and forcibly
minimizes them, i.e. equates them to their lower bounds. This produces
a result where some type arguments are inferred precisely, and some
are replaced with seemingly arbitrary types. For instance,
unconstrained type parameters are inferred to Nothing, which is a
common source of confusion for Scala beginners.
You can learn more about type inference in Scala:
Hubert Plociniczak Decrypting Local Type Inference https://infoscience.epfl.ch/record/214757
Guillaume Martres Scala 3, Type Inference and You! https://www.youtube.com/watch?v=lMvOykNQ4zs
Guillaume Martres Dotty and types: the story so far https://www.youtube.com/watch?v=YIQjfCKDR5A
Slides http://guillaume.martres.me/talks/
Aleksander Boruch-Gruszecki GADTs in Dotty https://www.youtube.com/watch?v=VV9lPg3fNl8
I've tried to define a Monad (scalaz) for shapeless HList through point and bind implementation. The first problem is that HList trait is not a type constructor, but that can be solved with type lambdas, point is simple, but i couldn't find right implementation for bind, i guess i need some function of type Poly1 with some Aux/Mapper tricks, but that side of shapeless is still dark to me. HList has all functions to be a Monad, like simple List, so is it possible to implement one from Scalaz?
A monoid is a set with some operations that obey particular laws. What elements are you considering as possible HListM[A]? If you declare HListM[A] = HList, i.e. any HList, then you'll quickly find that you can't map with f: A => B, except by treating all maps as identity and you've reinvented the rather uninteresting monad Id (with a few extra but inert inhabitants).
We could make a monad with the type HListM[A] = A :: ... :: A :: HNil (though even actually expressing that type in Scala is a challenge - you'd need an auxiliary trait trait CopiesOf[N <: Nat, A] {type Out <: HList}, implicits to provide instances of this, and then an existential to actually write it (CopiesOf[N, A]#Out forSome {type N <: Nat})). Writing monad operations for this is possible, though you'd need to require shapeless auxiliary classes like Prepend at the point of operation, since there's no real way to express a "forall" type in Scala - you can declare instances of your type for _0 and Succ[N], but there's no way to prove to the compiler that there is an instance for any N <: Nat, you just have to require implicit ones whenever you need to use them.
But after a lot of work you'd end up with something isomorphic to List[A]; why not just use List[A] for that case?
I'm kind of new to Scala and I have a question about the type system.
The flatten method works on nested collections, so if I have a List of Lists it will flatten it into a List. But it does not make sense to call flatten on a collection that is already flat. And sure enough the Scala type checker will flag that as an error.
List(List(1,2,3),List(4,5,6)).flatten // produces List(1,2,3,4,5,6)
List(1,2,3,4).flatten // type error
I understand that this somehow relies on an implicit parameter to flatten. But I don't know where the implicit value comes from and how it is used to assert the type of the object flatten is called on. Also, why doesn't the implicit parameter show up in the scaladocs for List.flatten?
To understand how this works, we need to have a look at the type signature of flatten:
def flatten[B](implicit asTraversable: (A) ⇒ GenTraversableOnce[B]): List[B]
A is the element type of the list and B is the type of the elements of each element. In order for flatten to work, there has to be an implicit conversion from the element types to a GenTraversableOnce[B]. That is only the case for collections or if you implement your own implicit conversion. For example, you could define one for pairs:
implicit def pairToList[A](p:(A,A)) = List(p._1, p._2)
List(1->2,2->3).flatten //compiles! List(1,2,2,3)
The trick is an implicit witness that ensures that the elements in the list are traversable. Here is the (slightly simplified) signature of flatten taken from GenericTraversableTemplate:
def flatten[B](implicit asTraversable: A => TraversableOnce[B])
: GenTraversable[B] =
In your case, the witness cannot be found for elements of type Int, and the invocation of flatten is therefore rejected by the compiler.
If you wanted to make your example compile, you could use the following implicit definition:
implicit def singleToList[A](a: A) = List(a)
(As a side-note: I'd consider such an implicit as quite dangerous, since it's applicability is very general, which can result in unpleasant surprises because the compiler might inject invocations at various places without you being aware of it.)
Heiko Seeberger wrote a great blog post on category theory here:
https://hseeberger.wordpress.com/2010/11/25/introduction-to-category-theory-in-scala/
In it, he defines a GenericFunctor like so:
trait GenericFunctor[->>[_, _], ->>>[_, _], F[_]] {
def fmap[A, B](f: A ->> B): F[A] ->>> F[B]
}
I did not have any luck finding documentation references to the ->> and ->>> symbols in documentation. Could someone please explain what they are doing?
The symbols themselves don't mean anything. They are arbitrary names Heiko picked:
> class Foo[A, B]
defined class Foo
> class Foo[M1[_], M2[_]]
defined class Foo
> class GenericFunctor[->>[_, _], ->>>[_, _], F[_]]
defined class GenericFunctor
They are parts of type parameters that they themselves are type constructors (higher-kinded types if you want to sound fancy).
Type applications can be written infix, so A ->> B is same as ->>[A, B].
As per what's going on... Heiko says
Looking at the ingredients, we find all that we need: Types A and B are mapped to types F[A] and F[B] and maps A ->> B are mapped to maps F[A] ->>> F[B].
Since we are talking category theory, we want to avoid the term function because that's implementation specific, but we want to describe something kind of like a function. Something-like-a-function in their lingo is an arrow. We need two of them since we don't want to assume the incoming and outgoing arrows to be the same. These two arrows are represented by ->> and ->>>. F[_] is a container like List and Option. I think..
So fmap (aka map method in Scala) takes an arrow of values and returns another arrow of containers. Except unlike map method, fmap returns an arrow that takes a container.
A specific application of the GenericFunctor using Function for both arrows is Functor. And specific application of Functor that uses List for the container is ListFunctor.
object ListFunctor extends Functor[List] {
def fmap[A, B](f: A => B): List[A] => List[B] = as => as map f
}
So that's taking a function from A to B, and returning a function from List[A] to List[B], calling map internally.
A clue is that they are within square brackets in the trait definition: they are just arbitrary symbols that have been picked by the blog author, just as [T] is often chosen for generic classes, traits, and methods. These here just happen to be higher-kinded types (i.e. parameters with parameters).
The arrow-like name was chosen because, as he says,
"A ->> B is just another way to write ->>[A, B], which nicely reflects
the fact that we are talking about maps here."
Answer copied from my answer to a duplicate question on request:
It's a higher-kinded type, described nicely in this introduction and in this research paper.
The reason you might find it confusing is that ->> is the name for the higher-kinded type -- it might have as well been called Arrow instead.
According to the documentation, PartiallyOrdered[A] is covariant in A, while Ordered[A] is invariant (but used to be covariant) in A.
Why was Ordered[A] ever covariant in A? Isn't this an obvious violation of the substitution principle?
Why can't Ordered[A] be contravariant in A? This would allow an Ordered[Traversible[Char]] to be typed as an Ordered[StringBuilder], for example. I don't see how this could be problematic.
I'm having trouble understanding the signature of tryCompareTo in PartiallyOrdered. It looks like the argument can be an instance of any supertype of A. Couldn't you pass in any object by calling tryCompareTo[Any](anything)? If so, how is the method signature any better than tryCompareTo(that: Any)?
Logically, ordered sets are a subclass of partially ordered sets, but the Scala classes don't seem to reflect this relationship. Is this because Ordered[A] cannot be covariant in A as PartiallyOrdered[A] can?
Looking at the previous version (2.8.1) , I didn't find anything relevant about Ordered that was covariant ?
EDIT : Looked further and find some explanation in the documentation for Ordered to not be covariant anymore.
For the case of tryCompare in PartiallyOrdered, the signature set more restriction on passed parameter :
def tryCompareTo [B >: A] (that: B)(implicit arg0: (B) ⇒ PartiallyOrdered[B]): Option[Int]
Here the implicit arg0 implies that to be seen as a PartiallyOrdered[B] instance and thus that it has access to all method defined in this trait.