- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Type inferencer not inferencing quite enough?
Wed, 2011-12-14, 15:00
The following compiles OK:
----------
abstract class DirDecorator
abstract class DirDecoratorFactory {
def canDecorate(dir: File): Boolean
def newInstance: DirDecorator1
}
object DirDecorator {
def add(dirDecFac: DirDecoratorFactory) {
decorators.append(dirDecFac)
}
def get(dir: File): Option[DirDecorator] = {
decorators.find(_.canDecorate(dir)) match {
case Some(ddf) => Some(ddf.newInstance)
case None => None
}
}
private val decorators = new ListBuffer[DirDecoratorFactory]
}
----------
But if I add type bounds as shown below the 'case Some(...' line fails
to compile with:
type mismatch;
found : _0 where type +_0
required: Option[com.bleaklow.browser.dirdecorator.DirDecorator]
case Some(ddf) => ddf.newInstance
----------
abstract class DirDecorator
abstract class DirDecoratorFactory[DD >: DirDecorator] {
def canDecorate(dir: File): Boolean
def newInstance: DD
}
object DirDecorator {
def add(dirDecFac: DirDecoratorFactory[_]) {
decorators.append(dirDecFac)
}
def get(dir: File): Option[DirDecorator] = {
decorators.find(_.canDecorate(dir)) match {
case Some(ddf) => Some(ddf.newInstance)
case None => None
}
}
private val decorators = new ListBuffer[DirDecoratorFactory[_]]
}
----------
I can work around that by explicitly matching against the type returned
by the find():
case Some(ddf: DirDecoratorFactory[_]) => Some(ddf.newInstance)
But I don't understand why that's necessary. The type returned by the
find() is always going to be a DirDecoratorFactory, why does the type
inferencer get it right in the first case and not in the second? I
found a bug that seems related
(https://issues.scala-lang.org/browse/SI-2635) but that was closed in
2.8. Am I missing something obvious or, more likely, doing something dumb?
Thanks,
Wed, 2011-12-14, 22:01
#2
Re: Type inferencer not inferencing quite enough?
Am 14.12.2011 14:58, schrieb Alan Burlison:
> But I don't understand why that's necessary. The type returned by the
> find() is always going to be a DirDecoratorFactory, why does the type
> inferencer get it right in the first case and not in the second?
It gets it right in the second case, it just tells you in
a slightly opaque way that your type annotation specifies
that you are not guaranteed to get a DirDecorator back and
so cannot use Some(ddf.newInstance) at a point that
expects an Option[DirDecorator].
- Florian.
Proof of assertion by counterexample:
scala> :paste
// Entering paste mode (ctrl-D to finish)
import java.io.File
trait DirDecorator
abstract class DirDecoratorFactory[DD >: DirDecorator] {
def canDecorate(dir: File): Boolean
def newInstance: DD
}
// Exiting paste mode, now interpreting.
import java.io.File
defined trait DirDecorator
defined class DirDecoratorFactory
scala> :paste
// Entering paste mode (ctrl-D to finish)
class PsycoFactory extends DirDecoratorFactory[Any] {
def canDecorate(f: File) = true
def newInstance = 42
}
// Exiting paste mode, now interpreting.
defined class PsycoFactory
scala> (new PsycoFactory).newInstance
res0: Int = 42
Wed, 2011-12-14, 22:21
#3
Re: Type inferencer not inferencing quite enough?
On 14/12/2011 20:54, Florian Hars wrote:
> It gets it right in the second case, it just tells you in
> a slightly opaque way that your type annotation specifies
> that you are not guaranteed to get a DirDecorator back and
> so cannot use Some(ddf.newInstance) at a point that
> expects an Option[DirDecorator].
Ah OK, that makes sense - thanks for the explanation. The followup is
obvious - how would I modify that so it *would* know it is getting a
DirDecorator back?
Thu, 2011-12-15, 10:21
#4
Re: Type inferencer not inferencing quite enough?
On Wed, Dec 14, 2011 at 22:10, Alan Burlison wrote:
> Ah OK, that makes sense - thanks for the explanation. The followup is
> obvious - how would I modify that so it *would* know it is getting a
> DirDecorator back?
Use <: instead of >: ?
Thu, 2011-12-15, 11:11
#5
Re: Type inferencer not inferencing quite enough?
On 15/12/2011 09:13, Harald Meland wrote:
> Use<: instead of>: ?
Still doesn't work:
found : Any
required: com.oracle.psarc.dirdecorator.DirDecorator
case Some(ddf) => ddf.newInstance(path)
Thu, 2011-12-15, 15:01
#6
Re: Type inferencer not inferencing quite enough?
As aside, please note that
{
case None => None
case Some(x) => Some(x.f)
}
is better written:
.map(_.f)
The following compiles OK:
----------
abstract class DirDecorator
abstract class DirDecoratorFactory {
def canDecorate(dir: File): Boolean
def newInstance: DirDecorator1
}
object DirDecorator {
def add(dirDecFac: DirDecoratorFactory) {
decorators.append(dirDecFac)
}
def get(dir: File): Option[DirDecorator] = {
decorators.find(_.canDecorate(dir)) match {
case Some(ddf) => Some(ddf.newInstance)
case None => None
}
}
private val decorators = new ListBuffer[DirDecoratorFactory]
}
----------
But if I add type bounds as shown below the 'case Some(...' line fails to compile with:
type mismatch;
found : _0 where type +_0
required: Option[com.bleaklow.browser.dirdecorator.DirDecorator]
case Some(ddf) => ddf.newInstance
----------
abstract class DirDecorator
abstract class DirDecoratorFactory[DD >: DirDecorator] {
def canDecorate(dir: File): Boolean
def newInstance: DD
}
object DirDecorator {
def add(dirDecFac: DirDecoratorFactory[_]) {
decorators.append(dirDecFac)
}
def get(dir: File): Option[DirDecorator] = {
decorators.find(_.canDecorate(dir)) match {
case Some(ddf) => Some(ddf.newInstance)
case None => None
}
}
private val decorators = new ListBuffer[DirDecoratorFactory[_]]
}
----------
I can work around that by explicitly matching against the type returned by the find():
case Some(ddf: DirDecoratorFactory[_]) => Some(ddf.newInstance)
But I don't understand why that's necessary. The type returned by the find() is always going to be a DirDecoratorFactory, why does the type inferencer get it right in the first case and not in the second? I found a bug that seems related (https://issues.scala-lang.org/browse/SI-2635) but that was closed in 2.8. Am I missing something obvious or, more likely, doing something dumb?
Thanks,
Thu, 2011-12-15, 23:21
#7
Re: Type inferencer not inferencing quite enough?
On Thu, Dec 15, 2011 at 11:00, Alan Burlison wrote:
> On 15/12/2011 09:13, Harald Meland wrote:
>
>> Use<: instead of>: ?
>
>
> Still doesn't work:
... because you've specified 'decorators' to have type
ListBuffer[DirDecoratorFactory[_]], i.e. with an unbounded existential
type, which makes scala think the elements in the ListBuffer have type
DirDecoratorFactory[Any] -- even though Any doesn't satisfy the <:
DirDecorator type bound you've defined on the DirDecoratorFactory
class.
You can fix this by adding the same bounds to the existential:
private val decorators = new ListBuffer[DirDecoratorFactory[_ <:
DirDecorator]]
(which you then would also need to do in the argument to add(), so
maybe something like
type Factory = DirDecoratorFactory[_ <: DirDecorator]
private val decorators = new ListBuffer[Factory]
etc. would be a good way to go about it)
...or avoiding using an existential at all, e.g. by adding an
(abstract?) type inside DirDecoratorFactory instead of the class type
parameter.
Fri, 2011-12-16, 08:01
#8
Re: Type inferencer not inferencing quite enough?
On Thu, Dec 15, 2011 at 8:44 AM, Tony Morris <tmorris@tmorris.net> wrote:
As aside, please note that
{
case None => None
case Some(x) => Some(x.f)
}is better written:
.map(_.f)
Except when you're trying to reduce the number of classes your library weighs (as a certain very heavily used scala library is), or when it breaks tail recursion.
Fri, 2011-12-16, 15:51
#9
Re: Type inferencer not inferencing quite enough?
On Fri, Dec 16, 2011 at 1:55 AM, Naftoli Gugenheim <naftoligug@gmail.com> wrote:
On Thu, Dec 15, 2011 at 8:44 AM, Tony Morris <tmorris@tmorris.net> wrote:
As aside, please note that
{
case None => None
case Some(x) => Some(x.f)
}is better written:
.map(_.f)
Except when you're trying to reduce the number of classes your library weighs (as a certain very heavily used scala library is), or when it breaks tail recursion.
I can only assume you mean the Scala standard library here... or perhaps the compiler?
Fri, 2011-12-16, 23:21
#10
Re: Type inferencer not inferencing quite enough?
On 15/12/2011 22:19, Harald Meland wrote:
> You can fix this by adding the same bounds to the existential:
>
> private val decorators = new ListBuffer[DirDecoratorFactory[_<:
> DirDecorator]]
>
> (which you then would also need to do in the argument to add(), so
> maybe something like
> type Factory = DirDecoratorFactory[_<: DirDecorator]
> private val decorators = new ListBuffer[Factory]
> etc. would be a good way to go about it)
That sorted it, thanks very much for the explanation, very helpful :-)
Sun, 2011-12-18, 09:21
#11
Re: Type inferencer not inferencing quite enough?
I can't find the link at the moment but I was referring to sbt.
In any case I perhaps wrongly assumed that Tony was making a general statement. My counterpoints were certainly not relevant to the OP's use case; I was simply showing that there are other times when people prefer pattern matching. Also I didn't say anything about tail call optimization but about tail recursion.
On Fri, Dec 16, 2011 at 9:42 AM, Josh Suereth <joshua.suereth@gmail.com> wrote:
In any case I perhaps wrongly assumed that Tony was making a general statement. My counterpoints were certainly not relevant to the OP's use case; I was simply showing that there are other times when people prefer pattern matching. Also I didn't say anything about tail call optimization but about tail recursion.
On Fri, Dec 16, 2011 at 9:42 AM, Josh Suereth <joshua.suereth@gmail.com> wrote:
On Fri, Dec 16, 2011 at 1:55 AM, Naftoli Gugenheim <naftoligug@gmail.com> wrote:
On Thu, Dec 15, 2011 at 8:44 AM, Tony Morris <tmorris@tmorris.net> wrote:
As aside, please note that
{
case None => None
case Some(x) => Some(x.f)
}is better written:
.map(_.f)
Except when you're trying to reduce the number of classes your library weighs (as a certain very heavily used scala library is), or when it breaks tail recursion.
I can only assume you mean the Scala standard library here... or perhaps the compiler?
The following compiles OK:
----------
abstract class DirDecorator
abstract class DirDecoratorFactory {
def canDecorate(dir: File): Boolean
def newInstance: DirDecorator1
}
object DirDecorator {
def add(dirDecFac: DirDecoratorFactory) {
decorators.append(dirDecFac)
}
def get(dir: File): Option[DirDecorator] = {
decorators.find(_.canDecorate(dir)) match {
case Some(ddf) => Some(ddf.newInstance)
case None => None
}
}
private val decorators = new ListBuffer[DirDecoratorFactory]
}
----------
But if I add type bounds as shown below the 'case Some(...' line fails
to compile with:
type mismatch;
found : _0 where type +_0
required: Option[com.bleaklow.browser.dirdecorator.DirDecorator]
case Some(ddf) => ddf.newInstance
----------
abstract class DirDecorator
abstract class DirDecoratorFactory[DD >: DirDecorator] {
def canDecorate(dir: File): Boolean
def newInstance: DD
}
object DirDecorator {
def add(dirDecFac: DirDecoratorFactory[_]) {
decorators.append(dirDecFac)
}
def get(dir: File): Option[DirDecorator] = {
decorators.find(_.canDecorate(dir)) match {
case Some(ddf) => Some(ddf.newInstance)
case None => None
}
}
private val decorators = new ListBuffer[DirDecoratorFactory[_]]
}
----------
I can work around that by explicitly matching against the type returned
by the find():
case Some(ddf: DirDecoratorFactory[_]) => Some(ddf.newInstance)
But I don't understand why that's necessary. The type returned by the
find() is always going to be a DirDecoratorFactory, why does the type
inferencer get it right in the first case and not in the second? I
found a bug that seems related
(https://issues.scala-lang.org/browse/SI-2635) but that was closed in
2.8. Am I missing something obvious or, more likely, doing something dumb?
Thanks,