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

Dwarf problem

7 replies
Franco Lombardo
Joined: 2009-02-03,
User offline. Last seen 42 years 45 weeks ago.

I have a problem with implicit conversions. I'm trying to write a tale in
Scala, but feeding dwarfs is a problem:

abstract class Dwarf {
def name: String
def food: String
}

case object Grumpy extends Dwarf {
def name = "Grumpy"
def food = "Apples"
}

case object Sleepy extends Dwarf {
def name = "Sleepy"
def food = "Mushrooms"
}

//And so on...

case class DwarfMeal [T <: Dwarf](dwarf: T, qty: Int) {
def + (otherMeal: DwarfMeal[T]) = DwarfMeal(dwarf, qty + otherMeal.qty)
}

object DwarfMealRunner {
implicit def multiplierConverter(n: Int) = new {
def * (meal: DwarfMeal[Dwarf]) = {
DwarfMeal(meal.dwarf, n * meal.qty )
}
}

def main(args : Array[String]) : Unit = {
//OK: it prints DwarfMeal(Sleepy,9)
println(DwarfMeal(Sleepy, 5) + DwarfMeal(Sleepy, 4))

//This does not compile: it's OK since each dwarf wants his own food
println(DwarfMeal(Grumpy, 5) + DwarfMeal(Sleepy, 4))

//This does not compile: why?
println(2 * DwarfMeal(Grumpy, 3))
}
}

I don't understand why, when I try to multiply a DwarfMeal, the compiler
doesn't apply the implicit conversion I defined. The dwarf are starving, so,
please, help me!

Bye

Franco

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
http://www.francolombardo.net
Scala, Java, As400.....
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Johannes Rudolph
Joined: 2008-12-17,
User offline. Last seen 29 weeks 20 hours ago.
Dwarf problem

Hi!

On Sun, Feb 22, 2009 at 4:39 PM, Franco Lombardo wrote:
> object DwarfMealRunner {
> implicit def multiplierConverter(n: Int) = new {
> def * (meal: DwarfMeal[Dwarf]) = {
> DwarfMeal(meal.dwarf, n * meal.qty )
> }
> }
> //This does not compile: why?
> println(2 * DwarfMeal(Grumpy, 3))
I guess, the actual compiler error message would be helpful...

> I don't understand why, when I try to multiply a DwarfMeal, the compiler
> doesn't apply the implicit conversion I defined. The dwarf are starving, so,
> please, help me!
To debug issues with implicits, make the implicit application explicit:

println(multiplierConverter(2).*(DwarfMeal(Grumpy, 3)))

I would guess the implicit does not apply, because T of DwarfMeal is
defined as invariant and * accepts a DwarfMeal[Dwarf]. Invariant means
that

DwarfMeal[Grumpy.type] <: DwarfMeal[Dwarf]

does NOT hold. You could try to declare T as covariant
(DwarfMeal[+T<:Dwarf]) or use a type parameter in the multiplication
function.

BTW: Using structural subtyping (new {def meetCinderella}) in
implicits is convenient but has had some issues in the past since
structural subtyping is implemented through Java reflection at
runtime. This is somewhat bad performance-wise and may cause a
class-file size explosion when massively used. Good to know, so no one
dies by suffocation...

Hope that helps in this case of emergency.

Johannes

Sebastien Bocq
Joined: 2008-12-18,
User offline. Last seen 42 years 45 weeks ago.
Re: Dwarf problem
From what I understood of implicits, you must at least give the compiler a unambiguous clue about the expected type.

This works for example:

class DwarfMealMultipler(n:Int) {
   def *(meal: DwarfMeal[Dwarf]) = {
     DwarfMeal(meal.dwarf, n * meal.qty )
   }
 }

object DwarfMealRunner {
 implicit def multiplierConverter(n: Int) = new DwarfMealMultipler(n)
}

scala> import DwarfMealRunner._

scala> (2:DwarfMealMultipler) * DwarfMeal(Grumpy, 3)
res2: DwarfMeal[Dwarf] = DwarfMeal(Grumpy,6)

Sebastien

2009/2/22 Johannes Rudolph <johannes.rudolph@googlemail.com>
Hi!

On Sun, Feb 22, 2009 at 4:39 PM, Franco Lombardo <f_lombardo@hotmail.com> wrote:
> object DwarfMealRunner {
>  implicit def multiplierConverter(n: Int) = new {
>    def * (meal: DwarfMeal[Dwarf]) = {
>      DwarfMeal(meal.dwarf, n * meal.qty )
>    }
>  }
>    //This does not compile: why?
>    println(2 * DwarfMeal(Grumpy, 3))
I guess, the actual compiler error message would be helpful...

> I don't understand why, when I try to multiply a DwarfMeal, the compiler
> doesn't apply the implicit conversion I defined. The dwarf are starving, so,
> please, help me!
To debug issues with implicits, make the implicit application explicit:

println(multiplierConverter(2).*(DwarfMeal(Grumpy, 3)))

I would guess the implicit does not apply, because T of DwarfMeal is
defined as invariant and * accepts a DwarfMeal[Dwarf]. Invariant means
that

DwarfMeal[Grumpy.type] <: DwarfMeal[Dwarf]

does NOT hold. You could try to declare T as covariant
(DwarfMeal[+T<:Dwarf]) or use a type parameter in the multiplication
function.

BTW: Using structural subtyping (new {def meetCinderella}) in
implicits is convenient but has had some issues in the past since
structural subtyping is implemented through Java reflection at
runtime. This is somewhat bad performance-wise and may cause a
class-file size explosion when massively used. Good to know, so no one
dies by suffocation...

Hope that helps in this case of emergency.

Johannes

Franco Lombardo
Joined: 2009-02-03,
User offline. Last seen 42 years 45 weeks ago.
Re: Dwarf problem

"Johannes Rudolph" wrote

>> println(2 * DwarfMeal(Grumpy, 3))
> I guess, the actual compiler error message would be helpful...

Here is the message:

overloaded method value * with alternatives (Double)Double
(Float)Float (Long)Long (Int)Int (Char)Int
(Short)Int (Byte)Int cannot be applied to
(net.francolombardo.DwarfMeal[object net.francolombardo.Grumpy])

> To debug issues with implicits, make the implicit application explicit:
>
> println(multiplierConverter(2).*(DwarfMeal(Grumpy, 3)))

This actually compiles!!! (I bet on it :-)

>
> I would guess the implicit does not apply, because T of DwarfMeal is
> defined as invariant and * accepts a DwarfMeal[Dwarf]. Invariant means
> that
>
> DwarfMeal[Grumpy.type] <: DwarfMeal[Dwarf]
>
> does NOT hold. You could try to declare T as covariant
> (DwarfMeal[+T<:Dwarf])

I tried

case class DwarfMeal [+T <: Dwarf](dwarf: T, qty: Int) {
def + (otherMeal: DwarfMeal[T]) = DwarfMeal(dwarf, qty + otherMeal.qty)
}

but the compiler does not accept the definition of + with the message
"covariant type T occurs in contravariant position in type
net.francolombardo.DwarfMeal[T] of value otherMeal

> or use a type parameter in the multiplication
> function.

I did it too, but the implicit conversion still did not apply :-(

> BTW: Using structural subtyping (new {def meetCinderella}) in
> implicits is convenient but has had some issues in the past since
> structural subtyping is implemented through Java reflection at
> runtime. This is somewhat bad performance-wise and may cause a
> class-file size explosion when massively used. Good to know, so no one
> dies by suffocation...

So you suggest to use:

new CindirellaMeet(dwarf: Dwarf)

where CindirellaMeet is a class with the method meetCinderella?

> Hope that helps in this case of emergency.

I thank you for your valuable help, but the dwarfs are still in troubles!

;-)

Bye

Franco

Franco Lombardo
Joined: 2009-02-03,
User offline. Last seen 42 years 45 weeks ago.
Re: Dwarf problem

"S

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: Dwarf problem

On Mon, Feb 23, 2009 at 06:32:10PM +0100, Franco Lombardo wrote:
> overloaded method value * with alternatives (Double)Double
> (Float)Float (Long)Long (Int)Int (Char)Int
> (Short)Int (Byte)Int cannot be applied to
> (net.francolombardo.DwarfMeal[object net.francolombardo.Grumpy])

What he told you is correctly - the implicit doesn't apply because T was not declared covariant.

Here's a diff to your original that lets the last line compile while still preventing grumpy and sleepy meals from being addable.
There might be a cleaner way to do this - the addition is ugly because singleton types are not inferred, which apparently won't
be fixed for reasons you can read about here:

https://lampsvn.epfl.ch/trac/scala/ticket/1208

Anyway, the diff:

< case class DwarfMeal [T <: Dwarf](dwarf: T, qty: Int) {
< def + (otherMeal: DwarfMeal[T]) = DwarfMeal(dwarf, qty + otherMeal.qty)
---
> case class DwarfMeal [+T <: Dwarf](dwarf: T, qty: Int) {
> def + (otherMeal: DwarfMeal[dwarf.type]) = DwarfMeal(dwarf, qty + otherMeal.qty)
31c31
< println(DwarfMeal(Sleepy, 5) + DwarfMeal(Sleepy, 4))
---
> println(DwarfMeal[Sleepy.type](Sleepy, 5) + DwarfMeal[Sleepy.type](Sleepy, 4))

Sebastien Bocq
Joined: 2008-12-18,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Dwarf problem


2009/2/23 Franco Lombardo <f_lombardo@hotmail.com>

"Sébastien Bocq" wrote

>scala> (2:DwarfMealMultipler) * DwarfMeal(Grumpy, 3)

Oh, it works! Grumpy thanks you very much, but, as he has a bad temperament,
you know, he pointed me that this is not such a good syntax (he's a bit
rude).
What he means is that there is no great advantage of implicit conversions
here if I need to "explicit" call it. What do you think?

Indeed, I thought it could not work without giving a hint about the type but apparently it can work. I learn a bit more everyday!

By the way, could you give me some pointers about this syntax (object:type)?

It just permits to specify the type of a value like in:

scala> val a = 2:Float
a: Float = 2.0

There are parentheses in the previous example because ':' keyword follows the operator precendence rules and has lower precedence than '*'.
 

Thanks a lot.
- Afficher le texte des messages précédents -
Bye

Franco


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
http://www.francolombardo.net
Scala, Java, As400.....
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~




Franco Lombardo
Joined: 2009-02-03,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Dwarf problem

> Anyway, the diff:
>
> < case class DwarfMeal [T <: Dwarf](dwarf: T, qty: Int) {
> < def + (otherMeal: DwarfMeal[T]) = DwarfMeal(dwarf, qty +
> otherMeal.qty)
> ---
>> case class DwarfMeal [+T <: Dwarf](dwarf: T, qty: Int) {
>> def + (otherMeal: DwarfMeal[dwarf.type]) = DwarfMeal(dwarf, qty +
>> otherMeal.qty)
> 31c31
> < println(DwarfMeal(Sleepy, 5) + DwarfMeal(Sleepy, 4))
> ---
>> println(DwarfMeal[Sleepy.type](Sleepy, 5) +
>> DwarfMeal[Sleepy.type](Sleepy, 4))

It works! But,as you know, Grumpy could say: "yeah, but this syntax is a bit
too awkward!".
You know, that guy is a little rough around the edges!

>There might be a cleaner way to do this - the addition is ugly because
>singleton types are not inferred,

Can you give me some hints? I tried with:

case class Grumpy() extends Dwarf

but I can't find a way to compile the code without the [Sleepy.type] type
parameter, wich is not very nice.

Thanks a lot!

Bye

Franco

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
http://www.francolombardo.net
Scala, Java, As400.....
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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