This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Type inferencer not inferencing quite enough?

11 replies
Alan Burlison
Joined: 2011-08-26,
User offline. Last seen 42 years 45 weeks ago.

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,

Alan Burlison
Joined: 2011-08-26,
User offline. Last seen 42 years 45 weeks ago.
Type inferencer not inferencing quite enough?

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,

Florian Hars 3
Joined: 2011-05-08,
User offline. Last seen 42 years 45 weeks ago.
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

Alan Burlison
Joined: 2011-08-26,
User offline. Last seen 42 years 45 weeks ago.
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?

Harald Meland
Joined: 2011-06-22,
User offline. Last seen 42 years 45 weeks ago.
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 >: ?

Alan Burlison
Joined: 2011-08-26,
User offline. Last seen 42 years 45 weeks ago.
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)

Tony Morris
Joined: 2008-12-19,
User offline. Last seen 30 weeks 4 days ago.
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)

On Dec 15, 2011 12:00 AM, "Alan Burlison" <alan.burlison@gmail.com> wrote:
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,

Harald Meland
Joined: 2011-06-22,
User offline. Last seen 42 years 45 weeks ago.
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.

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
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.
Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 5 days ago.
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?
Alan Burlison
Joined: 2011-08-26,
User offline. Last seen 42 years 45 weeks ago.
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 :-)

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
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:


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?

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland