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

Abstract type list.

10 replies
edmondo1984
Joined: 2011-09-14,
User offline. Last seen 28 weeks 3 days ago.
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        





David Christiansen
Joined: 2010-11-22,
User offline. Last seen 42 years 45 weeks ago.
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

H-star Development
Joined: 2010-04-14,
User offline. Last seen 2 years 26 weeks ago.
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

Lars Hupel
Joined: 2010-06-23,
User offline. Last seen 44 weeks 3 days ago.
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]

milessabin
Joined: 2008-08-11,
User offline. Last seen 33 weeks 3 days ago.
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

milessabin
Joined: 2008-08-11,
User offline. Last seen 33 weeks 3 days ago.
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

edmondo1984
Joined: 2011-09-14,
User offline. Last seen 28 weeks 3 days ago.
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>
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/

fanf
Joined: 2009-03-17,
User offline. Last seen 2 years 30 weeks ago.
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 !

David Christiansen
Joined: 2010-11-22,
User offline. Last seen 42 years 45 weeks ago.
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

milessabin
Joined: 2008-08-11,
User offline. Last seen 33 weeks 3 days ago.
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

Kevin Wright 2
Joined: 2010-05-30,
User offline. Last seen 26 weeks 4 days ago.
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:
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



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