Mysterious GADT skolem: What type is trying to escape its scope? - scala

Scala 11.2 is giving me this error:
error: type mismatch;
found : Seq[Some[V]]
required: Seq[Option[?V8]] where type ?V8 <: V (this is a GADT skolem)
val output = f(ivs.map(iv => Some(iv.get._1)))
^
First off, this seems like a strange error message: Doesn't Seq[Some[V]] conform to Seq[Option[V]]?
Here are the parts of the surrounding code that seem relevant:
def evalDependencyTree[V]
(linkRelevance: LinkInfo => Option[LinkStrength])
(dtree: DependencyTree[V, LinkInfo], strengthSoFar: LinkStrength = 1.0)
: Option[(V, LinkStrength)] = dtree match {
. . .
case DFunction(f, inputs) => {
val ivs = inputs.map { input =>
evalDependencyTree(linkRelevance)(input, strengthSoFar) // <-- Recursive call
}
val output = f(ivs.map(iv => Some(iv.get._1))) // <-- The line with the error
. . .
}
}
trait DependencyTree[+V, +L]
case class DFunction[V, L](
f: Seq[Option[V]] => Option[V], inputs: Seq[DependencyTree[V, L]])
extends DependencyTree[V, L]
My (very limited) understanding of GADT skolems is that they're types defined by the compiler during type inference, which copy an existing type argument in order to prevent that type from "escaping" its scope, as in a recursive call—that is, to prevent its being referred to from a wider scope that has no access to the type.
I don't see how V could refer to different types in different scopes here. The recursive call to evalDependencyTree has the same type argument, V, as the current call to evalDependencyTree. I tried explicitly writing evalDependencyTree[V] for the recursive call, but the compiler returned the same error message. This code did work when evalDependencyTree did not have a type argument; in that version, dtree was hard-coded to DependencyTree[Int, LinkInfo].
What type is trying to escape? Or rather, what am I doing wrong?

I found a workaround myself: explicitly spell out the full type of f in the pattern-match, like this:
case DFunction(f: Seq[Option[V]] => Option[V], inputs) => . . .
This works, but I'm not accepting it as an answer because I don't have an explanation of why it's necessary. I still don't know when to expect this kind of error or what causes it. If you know, please post an answer!
Also, I would have thought that most of the type explicitly provided for f would have been lost by type erasure. So, there are two important things I can't explain about this workaround.

Related

In Scala, how to circumvent 'inferred type arguments do not conform' error?

I have a reflective function with implicit TypeTag parameter:
def fromOptionFn[R: TypeTag](self: Int => Option[R]): Wrapper[R] = {
println(TypeTag[R])
...
}
Which for unknown reason doesn't work (see How to make Scala type inference powerful enough to discover generic type parameter?):
> fromOptionFn2(v => Some(" + _))
> typeTag(Any)
I speculate that its caused by inferring R from Option[R], so I improve it a bit:
def fromOptionFn[R, Opt <: Option[R]: TypeTag](self: Int => Opt): Wrapper[R] = {
println(typeTag[Opt])
...
}
This time its worse, doesn't even compile, the error clearly inferred that scala is not smart enough to analyse the type:
> fromOptionFn2(v => Some(" + _))
Error: inferred type arguments [Nothing,Option[String]] do not conform to method fromOptionFn's type parameter bounds [R,Opt <: Option[R]]
So how do I temporarily circumvent this compilation problem? (Of course I can report it on Lightbend issue tracker but its too slow)
ADDENDUM: This problem itself is an attempted circumvention for How to make Scala type inference powerful enough to discover generic type parameter?, which might won't be fixed. In my case I don't mind getting the TypeTag of type R or Option[R], whatever works works.
This isn't an improvement, just the opposite, and Scala type inference simply doesn't support inferring Opt first and getting R from there: instead it infers Nothing because R isn't a part of any parameter types (and return type is unknown).
You could circumvent it by specifying the type parameters explicitly on every call: fromOptionFn2[String, Option[String]](...). Giving the expected type should also work in this specific case, I think: fromOptionFn2(...): Wrapper[String]. However, a better idea would be not to use type parameter signatures like [R, Opt <: Option[R]] in the first place.

Removing ambiguity of overloaded method definition

The compiler is getting confused by an overloaded method definiton and I cannot find a way to clarify my intention sufficiently.
I have the following code:
val part: (QueryParams) => List[ResultMap] = (partOf.find _).andThen(makeMap)
The find method is overloaded:
def find(params: QueryParams): List[U] = ...
def find(params: QueryParams, flattener: U => T): List[T] = ...
The code was working fine as long as there was a single definiton of find. Since I had to add the second find definiton with 2 params, the compiler is generating this error:
Error:(15, 31) ambiguous reference to overloaded definition,
both method find in trait DataFetcher of type (params: ...QueryParams)List[...Parser]
and method find in trait DataFetcher of type (params: ...QueryParams, flattener: ...Parser => ...ResultTuple)List[...ResultTuple]
match expected type ?
val part: Fetcher = (partOf.find _).andThen(makeMap)
^
IMHO there is no ambiguity. The type of part is defined to accept one argument of type QueryParams. There is only one method accepting a single QueryParams. U and T are different types and makeMap expects a List[U] There are no implicits, default values or varargs involved.
Is there a way to further clarify my intention to the compiler?
EDIT: one way to remove the ambiguity is to introduce an intermediary value, clarifying the expected type of the eta expansion:
val find: (QueryParams) => List[ResultTuple] = partOf.find _
val part: (QueryParams) => List[ResultMap] = find andThen makeMap
But since makeMap is only accepting List[ResultTuple] I stil dont get the reason for the supposed ambiguity and would prefer not to introduce the extra value. Any clarification?
First, it is important to understand that the trailing underscore is a deliberate design decision to prevent programmer mistakes. The only exception is when the type is explicitly declared.
Here is an example illustrating this point.
object A {
def add(a: Int)(b:Int): Int = a + b
val x: Int => Int = add(5) // compiles fine
val y = add(5) // produces a compiler error
}
The same applies to your question. Because you do not specify the intermediate type, when you write find _, should the compiler infer the type to be QueryParams => List[ResultTuple] (as you may expect) or should it be (QueryParams, U => T) => List[ResultTuple]? Note that the trailing underscore does not stand for a single argument, it just lifts the method to a function. When the type if declared, you can drop the trailing underscore and write find where you would have written find _.
I see from your edit that you found out that an intermediate value with a declared type works. Another (slightly clunky) way to clarify your intent is the following.
val part: (QueryParams) => List[ResultMap] = (x => partOf.find(x)).andThen(makeMap)

Scala ambiguity with paren-less function calls

Excuse the long set-up. This question relates to, but is not answered by, Scala: ambiguous reference to overloaded definition - best disambiguation? .
I'm pretty new to Scala, and one thing that's throwing me off is that Scala both:
Has first-class functions
Calls functions when using object-dot notation without any parenthetical argument lists (as if the function were a property)
These two language features are confusing me. Look at the below code:
class MyClass {
def something(in: String): String = {
in + "_X"
}
def something: String => String = {
case _ => "Fixed"
}
}
val my = new MyClass()
println(List("foo", "bar").map(my.something))
I would expect this to print List("foo_X", "bar_X") by calling the something prototype that matches the map's required String => ? argument. Instead, the output is List("Fixed", "Fixed") - Scala 2.11 is invoking the no-argument something() and then passing its return value to the map.
If we comment out the second no-argument prototype of something, the output changes to be the expected result, demonstrating that the other prototype is valid in context.
Adding an empty argument list to the second prototype (making it def something()) also changes the behavior.
Changing the my.something to my.something(_) wakes Scala up to the ambiguity it was silently ignoring before:
error: ambiguous reference to overloaded definition,
both method something in class MyClass of type => String => String
and method something in class MyClass of type (in: String)String
match argument types (String)
println(List("foo", "bar").map(my.something(_)))
Even using the supposedly-for-this-purpose magic trailing underscore doesn't work:
val myFun: (String) => String = my.something _
This results in:
error: type mismatch;
found : () => String => String
required: String => String
val myFun: (String) => String = my.something _
My questions:
If I have MyClass exactly as written (no changes to the prototypes, especially not adding an empty parameter list to one of the prototypes), how do I tell Scala, unambiguously, that I want the first one-argument version of something to pass as an argument to another call?
Since there are clearly two satisfying arguments that could be passed to map, why did the Scala compiler not report the ambiguity as an error?
Is there a way to disable Scala's behavior of (sometimes, not always) treating foo.bar as equivalent to foo.bar()?
I have filed a bug on the Scala issue tracker and the consensus seems to be that this behaviour is a bug. The compiler should have thrown an error about the ambiguous reference to "my.something".

type inference in argument list in combination with setter not working

Let's imagine the following items in scope:
object Thing {
var data: Box[String] = Empty
}
def perform[T](setter: Box[T] => Unit) {
// doesn't matter
}
The following fails to compile:
perform(Thing.data = _)
The error message is:
<console>:12: error: missing parameter type for expanded function ((x$1) => Thing.data = x$1)
perform(Thing.data = _)
^
<console>:12: warning: a type was inferred to be `Any`; this may indicate a programming error.
perform(Thing.data = _)
^
While the following compiles:
perform(Thing.data_=)
I have since surpassed this issue by creating a better abstraction, but my curiosity still remains.
Can anyone explain why this is?
Let's expand out what you're doing in the first example:
Thing.data = _
is shorthand for defining an anonymous function, which looks like:
def anon[T](x: Box[T]) {
Thing.data = x
}
So when you call
perform(Thing.data = _)
it's the same as
perform(anon)
The problem is anon and perform take a type parameter T and at no point are you declaring what T is. The compiler can only infer type parameters in a function call from passed arguments, not from within the function body, so it cannot infer in anon that T should be String.
Notice that if you call
perform[String](Thing.data = _)
the compiler has no issue because it now knows what T should be, and if you try to use any type besides string, you'll get a type mismatch error, but the error occurs in the body of the anonymous function, not on the call to perform.
However, when you call
perform(Thing.data_=)
you are passing the method Thing.data_=, which is explicitly defined as Box[String] => Unit, so the compiler can infer perform's type parameter because it is coming from a function argument.

Scala unexpectedly not being able to ascertain type for expanded function

Why, in Scala, given:
a = List(1, 2, 3, 4)
def f(x : String) = { x }
does
a.map(_.toString)
work, but
a.map(f(_.toString))
give the error
missing parameter type for expanded function ((x$1) => x$1.toString)
Well... f() takes a String as a parameter. The construct _.toString has type A <: Any => String. The function f() expects a type of String, so the example above does not type check. It seems that Scala is friendly in this case and gives the user another chance. The error message means: "By my type inference algorithms this does not compile. Put the types in and it might, if it's something I can't infer."
You would have to write the anonymous function longhand in this case, i.e. a.map(n => f(n.toString)). This is not a limitation of type inference, but of the wildcard symbol. Basically, when you write a.map(f(_.toString)), the _.toString gets expanded into an anonymous function inside the closest brackets it can find, otherwise this would lead to enormous ambiguity. Imagine something like f(g(_.toString)). Does this mean f(g(x => x.toString)) or f(x => g(x.toString))? Worse ambiguities would arise for multiple nested function calls. The Scala type checker therefore takes the most logical solution, as described above.
Nitpick: the first line of your code should be val a = List(1,2,3,4) :).