- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Dwarf problem
Sun, 2009-02-22, 16:39
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.....
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Mon, 2009-02-23, 00:27
#2
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>
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
Mon, 2009-02-23, 18:47
#3
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
Mon, 2009-02-23, 18:57
#4
Re: Dwarf problem
"S
Mon, 2009-02-23, 19:27
#5
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))
Mon, 2009-02-23, 21:07
#6
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.....
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tue, 2009-02-24, 07:37
#7
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.....
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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