- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Abstract type list.
Tue, 2012-01-24, 09:31
Dear all,I have the following use case in scala, which is an extension of one of the classical examples about abstract types.
Let's say we have an abstract animal, the simple case is that this animal can eat a single type of food
abstract class Animal { type foodType def eat(food:footType)
}
However, what if we want an animal to be able to eat multiple types of food, but we can't say how many different type of food at the abstract class level? I would like to write something like the following
class Dog extends Animal { type foodType = PackagedFood, Meat def eat(food:Meat) = println "The dog is happy" def eat(food: PackagedFood ) = println "The dog is sad"
}
How does one solve this problem?
Best RegardsEdmondo
Let's say we have an abstract animal, the simple case is that this animal can eat a single type of food
abstract class Animal { type foodType def eat(food:footType)
}
However, what if we want an animal to be able to eat multiple types of food, but we can't say how many different type of food at the abstract class level? I would like to write something like the following
class Dog extends Animal { type foodType = PackagedFood, Meat def eat(food:Meat) = println "The dog is happy" def eat(food: PackagedFood ) = println "The dog is sad"
}
How does one solve this problem?
Best RegardsEdmondo
Tue, 2012-01-24, 10:11
#2
Re: Re: Abstract type list.
you need either a dummy trait for marking classes as food or a dummy method to do the same using a structural types
type Food = {def eatable():Unit}
def eat(f:Food) = {
f match ....
}
scala cannot group types, afaik
-------- Original-Nachricht --------
> Datum: Tue, 24 Jan 2012 00:35:48 -0800 (PST)
> Von: David Christiansen
> An: scala-user
> Betreff: [scala-user] Re: Abstract type list.
> Hi Edmondo,
>
> > I would like to write something like the following
> >
> > class Dog extends Animal {
> > type foodType = PackagedFood, Meat
> > def eat(food:Meat) = println "The dog is happy"
> > def eat(food: PackagedFood ) = println "The dog is sad"
> >
> > }
> >
> > How does one solve this problem?
>
> In this particular case, can't you use an Either type? For example,
>
> class Dog extends Animal {
> type foodType = Either[PackagedFood, Meat]
> def eat(food: foodType) = food match {
> case Left(_) => println("The dog is happy")
> case Right(_) => println("The dog is sad")
> }
> }
>
> Nested Eithers can also build a list structure, but it's ugly.
>
> /David
Tue, 2012-01-24, 10:51
#3
Re: Abstract type list.
If you are really brave, than you could use "unboxed union types", as
introduced in [1].
I generalized them to an arbitrary number of parameters. It is
implemented in scalaz-seven [2] (example usage: [3]).
In your case, it would boil down to saying
abstract class Animal {
type FoodType <: Disj
def eat[T](food: T)(implicit ev: Contains[T, FoodType]): Unit
}
class Dog extends Animal {
type FoodType = t[PackagedFood]#t[Meat]
def eat[T](food: T)(implicit ev: Contains[T, FoodType]) = food match {
case p: PackagedFood => println("packaged")
case m: Meat => println("meat")
}
}
scala> new Dog().eat(new PackagedFood)
packaged
scala> new Dog().eat(new Meat)
meat
scala> new Dog().eat("food?")
:15: error: Cannot prove that java.lang.String => Nothing =>
Nothing <:< PackagedFood => Nothing with Meat => Nothing => Nothing.
new Dog().eat("food?")
[1]
[2]
[3]
Tue, 2012-01-24, 11:21
#4
Re: Re: Abstract type list.
On Tue, Jan 24, 2012 at 8:53 AM, Dennis Haupt wrote:
> you need either a dummy trait for marking classes as food or a dummy method to do the same using a structural types
>
> type Food = {def eatable():Unit}
>
> def eat(f:Food) = {
> f match ....
> }
>
> scala cannot group types, afaik
Oh but it can ...
class Bunnies
class Bones
class Kibble
class Carrots
class Onions
class Chocolate
abstract class Animal {
type FoodType[_]
def eat[F : FoodType](food : F)
}
case class DogFood[F](result : String)
implicit def bunnies = new DogFood[Bunnies]("Yay!")
implicit def bones = new DogFood[Bones]("Crunch!")
implicit def kibble = new DogFood[Kibble]("Chomp!")
implicit def carrots = new DogFood[Carrots]("Munh!")
// No instances for Onions or Chocolate
class Dog extends Animal {
type FoodType[F] = DogFood[F]
def eat[F : FoodType](food : F) = {
implicitly[FoodType[F]].result
}
}
val tigger = new Dog
tigger.eat(new Bunnies)
tigger.eat(new Bones)
tigger.eat(new Kibble)
tigger.eat(new Carrots)
tigger.eat(new Onions) // Does not compile
tigger.eat(new Chocolate) // Does not compile
Cheers,
Miles
Tue, 2012-01-24, 11:31
#5
Re: Re: Abstract type list.
On Tue, Jan 24, 2012 at 10:19 AM, Miles Sabin wrote:
> Oh but it can ...
I missed out the punchline ... the point is that we can still do
useful work via the base type,
// Function expressed only in terms of Animal
def feed[A <: Animal, F](a : A, f : F)(implicit ev : a.FoodType[F]) {
a.eat(f)
}
feed(tigger, new Bunnies)
feed(tigger, new Onions) // Does not compile
Note the essential use of the dependent type a.FoodType[F].
Cheers,
Miles
Tue, 2012-01-24, 11:41
#6
Re: Re: Abstract type list.
Interesting point, I can't find much about the implicitly keyword... Can you please explain it?
Best Regards
2012/1/24 Miles Sabin <miles@milessabin.com>
Best Regards
2012/1/24 Miles Sabin <miles@milessabin.com>
On Tue, Jan 24, 2012 at 8:53 AM, Dennis Haupt <h-star@gmx.de> wrote:
> you need either a dummy trait for marking classes as food or a dummy method to do the same using a structural types
>
> type Food = {def eatable():Unit}
>
> def eat(f:Food) = {
> f match ....
> }
>
> scala cannot group types, afaik
Oh but it can ...
class Bunnies
class Bones
class Kibble
class Carrots
class Onions
class Chocolate
abstract class Animal {
type FoodType[_]
def eat[F : FoodType](food : F)
}
case class DogFood[F](result : String)
implicit def bunnies = new DogFood[Bunnies]("Yay!")
implicit def bones = new DogFood[Bones]("Crunch!")
implicit def kibble = new DogFood[Kibble]("Chomp!")
implicit def carrots = new DogFood[Carrots]("Munh!")
// No instances for Onions or Chocolate
class Dog extends Animal {
type FoodType[F] = DogFood[F]
def eat[F : FoodType](food : F) = {
implicitly[FoodType[F]].result
}
}
val tigger = new Dog
tigger.eat(new Bunnies)
tigger.eat(new Bones)
tigger.eat(new Kibble)
tigger.eat(new Carrots)
tigger.eat(new Onions) // Does not compile
tigger.eat(new Chocolate) // Does not compile
Cheers,
Miles
--
Miles Sabin
tel: +44 7813 944 528
gtalk: miles@milessabin.com
skype: milessabin
g+: http://www.milessabin.com
http://twitter.com/milessabin
http://www.chuusai.com/
Tue, 2012-01-24, 11:41
#7
Re: Re: Abstract type list.
On 24/01/2012 11:19, Miles Sabin wrote:
> [...]
>
> val tigger = new Dog
> tigger.eat(new Bunnies)
> tigger.eat(new Bones)
> tigger.eat(new Kibble)
> tigger.eat(new Carrots)
> tigger.eat(new Onions) // Does not compile
> tigger.eat(new Chocolate) // Does not compile
Well, there is a clear error in that code. Actually, a tigger is not a
dog, and calling a dog tigger is not logical at all. So I just don't get
how that code could work.
Appart that really important point, that really cool, thanks Miles !
Tue, 2012-01-24, 11:51
#8
Re: Re: Abstract type list.
Hi Edomondo,
> Interesting point, I can't find much about the implicitly keyword... Can you
> please explain it?
implicitly is actually not a keyword - it's just a method defined on
Predef, whose members are automatically in scope.
Here it is:
def implicitly[T](implicit e: T) = e
It's a convenient way of asking the compiler to find
implicitly-defined values for you.
/David
Tue, 2012-01-24, 12:01
#9
Re: Re: Abstract type list.
On Tue, Jan 24, 2012 at 10:33 AM, Francois wrote:
> Well, there is a clear error in that code. Actually, a tigger is not a dog,
> and calling a dog tigger is not logical at all. So I just don't get how that
> code could work.
Tigger is a bouncy, completely illogical Springer Spaniel ... the code
wouldn't work if it was consistent ;-)
Cheers,
Miles
Tue, 2012-01-24, 12:21
#10
Re: Re: Abstract type list.
One pattern I commonly use with type classes like this is a "custom" variant on implicitly (such as is done with manifest, classManifest, etc.)
So given a type class, DogFood:
case class DogFood[F](result : String)
I'd create a same-named lookup method:
def dogFood[F](implicit f: DogFood[F]] = f
Or if I slot it into Mile's example, using FoodType as an alias for DogFood, you get:
class Dog extends Animal {
type FoodType[F] = DogFood[F] def foodType[F](implicit f: FoodType[F]] = f
def eat[F : FoodType](food : F) = {
foodType[F].result
}
}
Much cleaner :)
p.s. For code that's particularly heavy in implicits, type classes and context bounds - I've also been known to use "?[T]" as a slightly less boilerplatey alias for "implicitly[T]".
On 24 January 2012 10:27, David Christiansen <david@davidchristiansen.dk> wrote:
So given a type class, DogFood:
case class DogFood[F](result : String)
I'd create a same-named lookup method:
def dogFood[F](implicit f: DogFood[F]] = f
Or if I slot it into Mile's example, using FoodType as an alias for DogFood, you get:
class Dog extends Animal {
type FoodType[F] = DogFood[F] def foodType[F](implicit f: FoodType[F]] = f
def eat[F : FoodType](food : F) = {
foodType[F].result
}
}
Much cleaner :)
p.s. For code that's particularly heavy in implicits, type classes and context bounds - I've also been known to use "?[T]" as a slightly less boilerplatey alias for "implicitly[T]".
On 24 January 2012 10:27, David Christiansen <david@davidchristiansen.dk> wrote:
Hi Edomondo,
> Interesting point, I can't find much about the implicitly keyword... Can you
> please explain it?
implicitly is actually not a keyword - it's just a method defined on
Predef, whose members are automatically in scope.
Here it is:
def implicitly[T](implicit e: T) = e
It's a convenient way of asking the compiler to find
implicitly-defined values for you.
/David
Hi Edmondo,
> I would like to write something like the following
>
> class Dog extends Animal {
> type foodType = PackagedFood, Meat
> def eat(food:Meat) = println "The dog is happy"
> def eat(food: PackagedFood ) = println "The dog is sad"
>
> }
>
> How does one solve this problem?
In this particular case, can't you use an Either type? For example,
class Dog extends Animal {
type foodType = Either[PackagedFood, Meat]
def eat(food: foodType) = food match {
case Left(_) => println("The dog is happy")
case Right(_) => println("The dog is sad")
}
}
Nested Eithers can also build a list structure, but it's ugly.
/David