I've observed that, if I want to make a generic function that can accept a list of any type and return a boolean, I can use the following syntax for a function declaration:
def someFunction[A](l:List[A]):Boolean
However, I can achieve an equivalent function declaration with this syntax as well:
def someFunction(l:List[_]):Boolean
The latter syntax makes sense to me; the underscore indicates a wildcard for a List of any type. However the former is confusing; what's the semantic difference between the two types of syntax, if there is one at all? Note: I noticed I could use [B] or [c] or even [%] in place of the "[A]" in the first syntax example.
The A is a "type parameter". Just like a value parameter, such as your l passed parameter, it is the "name", or place holder, for a some type which might be different at different times (i.e. with different invocations of the method).
In your example the A is unused so, yeah, using _ makes more sense and is clearer, but if you were to return an element from the list then the method return type would be A (or whatever name you want to give that parameter). Using _ as a return type wouldn't make any sense.
List[_] is an unconstrained existential type and shorthand for List[X] forSome {type X <: Any} (which is like List<?> in Java).
In this case, I think the function types (not in Scala syntax) forall A. List[A] -> Boolean and (exists A. List[A]) -> Boolean denote the same things, since in both cases you can only inspect the "shape" of the list; probably there's an equivalence between those types.
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
Apologies, this is going to be a somewhat noob-ish question. I have an object from the slick library that has at type like this:
Query[(Rep[String], Rep[String]), (String, String), Seq]
I'm trying to write a function which accepts queries as arguments, though the sequences in them are of uncertain length - ie, it could equally well be:
Query[(Rep[String], Rep[String], Rep[String]), (String, String, String), Seq]
So the first two components have three elements rather than two. I cannot figure out how this is done. I have tried various erroneous permutations, like Query[Product[Rep[String]], Product[String], Seq], to no avail, and even what I assumed would be the nuclear option of just using Any doesn't work. My error messages are along the lines of
[error] found : Option[slick.driver.H2Driver.api.Query[(slick.driver.H2Driver.api.Rep[String], slick.driver.H2Driver.api.Rep[String]),(String, St
ring),Seq]]
[error] (which expands to) Option[slick.lifted.Query[(slick.lifted.Rep[String], slick.lifted.Rep[String]),(String, String),Seq]]
[error] required: Option[slick.driver.H2Driver.api.Rep[scala.concurrent.Future[List[String]]]]
[error] (which expands to) Option[slick.lifted.Rep[scala.concurrent.Future[List[String]]]]
[error] ReturnFunctions.completeQuery(db, query, serialize_and_send)
I think my inability to solve this may reflect some fundamental lack of understanding about scala, strongly typed languages in general and possibly also computing as a whole. Should I be resolving this Query to some more definite form before I try to even pass it into a function? I also suspect I'm not interpreting the original type correctly - what do the parantheses mean in this context? Is it that Query is expecting to receive three sets of parameters, one after the other, like when you do fn(arg1)(arg2)(arg3) = ...?
Any help with this troubling dilemma gratefully received.
I also suspect I'm not interpreting the original type correctly - what do the parentheses mean in this context?
You're looked at a reasonable advanced area, but let's try to help.
The Query type always has three type parameters. You'll see them written as Query[M, U, C].
The first parameter, M, is a tuple. That's what the parentheses mean in this context.
In your first example, M is a tuple of two elements; and in the second it's three. The same situation exists for the second parameter, U. There's a bit more detail on this in Essential Slick.
In Scala, you can have generic parameters. That means you can say something along the lines of:
def foo[M, U, C[_]](q: Query[M,U,C]) = ???
We've defined a method with:
three type parameters; and
taking an argument of a query that has those types.
We've not said anything about M, U, or much about C (other than that it's a type that takes a type as an argument). That means there's not a lot we can do with them, but you may not need to.
A post on query enrichment in Slick gives a longer (related) example which may be of use.
As Dmytro suggests, a better route would be to create a concrete example of what you'd like to achieve and work from there.
Consider the shape of Query type constructor
Query[+E, U, C[_]]
We say Query is a type constructor because it constructs a concrete type out of given type arguments E, U, and C[_], similarly to how a function constructs a concrete value out of given function arguments.
Now lets try to deconstruct the concrete type
Query[(Rep[String], Rep[String]), (String, String), Seq]
into its constituent type parameters. We have
E = (Rep[String], Rep[String])
U = (String, String)
C[_] = Seq
Note (A, B) is just syntactic sugar for Tuple2[A, B] thus
E = (Rep[String], Rep[String]) = Tuple2[Rep[String], Rep[String]]
U = (String, String) = Tuple2[String, String]
C[_] = Seq = Seq
You might be wondering about that underscore in C[_]. This specifies that the type parameter C must be a type constructor as opposed to a concrete type. For example Seq is a type constructor whilst Seq[Int] is not. Furthermore, you might be wondering about that + in +E. This specifies the inheritance relationship of parameterised types, or in other words, variance, for example, it specifies whether Seq[Dog] a subtype of Seq[Animal].
Lastly lets write the resulting concrete type in its full verbosity
Query[Tuple2[Rep[String], Rep[String]], Tuple2[String, String], Seq]
I'm a beginner of Scala who is struggling with Scala syntax.
I got the line of code from https://www.tutorialspoint.com/scala/higher_order_functions.htm.
I know (x: A) is an argument of layout function
( which means argument x of Type A)
But what is [A] between layout and (x: A)?
I've been googling scala function syntax, couldn't find it.
def layout[A](x: A) = "[" + x.toString() + "]"
It's a type parameter, meaning that the method is parameterised (some also say "generic"). Without it, compiler would think that x: A denotes a variable of some concrete type A, and when it wouldn't find any such type it would report a compile error.
This is a fairly common thing in statically typed languages; for example, Java has the same thing, only syntax is <A>.
Parameterized methods have rules where the types can occur which involve concepts of covariance and contravariance, denoted as [+A] and [-A]. Variance is definitely not in the scope of this question and is probably too much for you too handle right now, but it's an important concept so I figured I'd just mention it, at least to let you know what those plus and minus signs mean when you see them (and you will).
Also, type parameters can be upper or lower bounded, denoted as [A <: SomeType] and [A >: SomeType]. This means that generic parameter needs to be a subtype/supertype of another type, in this case a made-up type SomeType.
There are even more constructs that contribute extra information about the type (e.g. context bounds, denoted as [A : Foo], used for typeclass mechanism), but you'll learn about those later.
This means that the method is using a generic type as its parameter. Every type you pass that has the definition for .toString could be passed through layout.
For example, you could pass both int and string arguments to layout, since you could call .toString on both of them.
val i = 1
val s = "hi"
layout(i) // would give "[1]"
layout(s) // would give "[hi]"
Without the gereric parameter, for this example you would have to write two definitions for layout: one that accepts integers as param, and one that accepts string as param. Even worse: every time you need another type you'd have to write another definition that accepts it.
Take a look at this example here and you'll understand it better.
I also recomend you to take a look at generic classes here.
A is a type parameter. Rather than being a data type itself (Ex. case class A), it is generic to allow any data type to be accepted by the function. So both of these will work:
layout(123f) [Float datatype] will output: "[123]"
layout("hello world") [String datatype] will output: "[hello world]"
Hence, whichever datatype is passed, the function will allow. These type parameters can also specify rules. These are called contravariance and covariance. Read more about them here!
For example, Exception.allCatch is defined as
def allCatch[T]: Catch[T]
Why not just
val allCatch: Catch[Nothing]
when Catch is covariant in its argument?
Or, why PartialFunction object defines
def empty[A, B]: PartialFunction[A, B]
instead of just
val empty: PartialFunction[Any,Nothing]
?
Update: So far it seems that the answers miss the point. So please include a specific examples in your answer that really target the question. For example: Show a piece of code that works with def empty[A, B]: PartialFunction[A, B] but doesn't work (or is less convenient) with val empty: PartialFunction[Any,Nothing].
This saves the need for casting later and allows to treat the args type as T instead of Any, which is usually more convenient.
Here is an example:
scala> def func1[T](arg : T) : T = { arg }
func1: [T](arg : T)T
scala> def func2(arg : Any) : Any = { arg }
func2: (arg: Any)Any
scala> func1(4)
res4: Int = 4
scala> func2(4)
res7: Any = 4
Looking at the source of PartialFunction available here we can see that it in fact calls a private method on the PartialFunction object:
private[this] val empty_pf: PartialFunction[Any, Nothing]
So the return type of empty will always be PartialFunction[Any, Nothing]. As for the reasoning behind I have no idea. Maybe someone else have better insight as to why. You could try the language mailing list as well...
If you hard-code the type, like PartialFunction[Any,Nothing], you cannot restrict your function to take a more specific parameter than Any.
By using a generic type parameter, you can end up with a more flexible satisfying all cases and especially making the function safe.
Let's assume you want a function aiming to take an Animal as parameter and returning an Integer.
Let's assume that function is declared as being:
def myFunction: PartialFunction[Any,Nothing]
Firstly, PartialFunction would not be specialized to Animal at parameter side but to Any. What about if I pass a Human as parameter...., it would pass.. What about safety?
Secondly, If this function is declared as returning Nothing, you can't return from it any value but Nothing! Indeed, Nothing subclasses all classes in Scala.
This leads to the known rule that return type parameter must always be covariant in order to make a function interesting, not the case with Nothing.
In fact, Nothing is interesting only when dealing with the empty method of PartialFunction. Logic since an empty PartialFunction by definition involves nothing to return and should be forced to do it :)
You would ask: "So why don't we change the return type to Any?"
Answer: Because you'd lose all the benefit of generic erasure time making compiler to add needed casts automatically => You wouldn't retrieve directly the Integer value, but Any. annoying..
Actually, it seems that Scala standard library has some more places where generic type parameter is redundant (because of type variance). For example, see my question about foreach. My guess, based on #Peter's answer, is that these redundant generics make the interface more clear. Thanks to them, we don't have to remember which types are covariant, contravariant and invariant. Also, this makes things way simpler for people who are not familiar with variance, which is rather an advanced feature of Scala.
Code seems trivial but I'm not understanding one thing in the return value:
trait JdbcTemplate {
def query(psc: PreparedStatementCreator,
rowMapper: RowMapper): List[_]
}
What exactly does List[_] mean here? Wouldn't using List[Any] imply the same thing? Where can I read on the differences?
Any is a specific, known (though utterly all-inclusive) type. The use of the underscore as type parameter is a shorthand for a more cumbersome and more general syntax for what is called an "existential type." Existential types are non-specific: They say there's at least one type that could go here. They are the dual of universal quantification that is the interpretation of the more commonly used unbounded type parameters. E.g., def method[T](t: T) .... In this construct, T may be bound to any type whatsoever though at each place where that type is instantiated (every occurrence of a call to that method), it is bound to a specific type.
Given that _ means you don't care about the type and Any is supertype of everything, both are the same.