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

Existentials considered harmful

22 replies
Johannes Rudolph 2
Joined: 2010-02-12,
User offline. Last seen 42 years 45 weeks ago.

I've got a little puzzler up here which shows various problems with
existentials:

https://gist.github.com/1048906

Do you think this is idiomatic code? Given the compile errors how
would you implement this otherwise?

At least one those problems is clearly a bug, another one was
discussed [1] but with no final conclusion.

In practice, issues like this make the use of existentials in places,
where they would be natural and useful,  really awkward. How do you
get around those problems?

--
Johannes

[1] http://www.scala-lang.org/node/8018

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

adriaanm
Joined: 2010-02-08,
User offline. Last seen 31 weeks 4 days ago.
Re: Existentials considered harmful
Do you think this is idiomatic code?
No. Existentials mainly exist to interop with Java wildcards.  
Given the compile errors how
would you implement this otherwise?
use type members (rewritten version of your puzzler below)
they are like existentials with more intuitive rules for packing and unpacking
I would say your puzzler illustrates that these rules are somewhat awkward for existentials. More precisely, these rules govern when the compiler decides you want to "peek into" the existential package, and what the scope is for the fresh type (the skolem) that you then invent for whatever type was in there. Each time you peek (unpack), you get a type that is incompatible to everything known to man until then (it's fresh). The tricky bit is that there's no easy way of grouping multiple peeks into one coordinated session.
This is exactly where type members excel, since the path on which you select the type member gives a kind of scattered scope for the same existential: as long as you select the same type member on equal paths, you get the same type.
hthadriaan

object TypeMembersConsideredCanonical {

  class Animal(val name: String)

  object Dog extends Animal("Dog")

  object Sheep extends Animal("Sheep")


  trait Tools[A] {

    def shave(a: A): A

  }

  def tools[A](a: A): Tools[A] = null // dummy

  object TransportBox {

    def apply[T](animal_ : T, tools_ : Tools[T]) = new TransportBox { type A = T; val animal = animal_; val tools = tools_ } 

  }

  trait TransportBox {

    type A <: Animal

    val animal: A

    val tools: Tools[A]

    

    def label: String = animal.name

  }


  // 1.

  def carry(box: TransportBox): Unit = {

    println(box.animal.name+" got carried away")

  }


  val aBox =

    if (math.random < 0.5)

      TransportBox(Dog, tools(Dog))

    else

      TransportBox(Sheep, tools(Sheep))


  // 2.

  aBox.tools.shave(aBox.animal)


  abstract class BoxCarrier[R <: Animal](box: TransportBox{type A = R}) {

    def speed: Int


    def talkToAnimal: Unit = println("The carrier says hello to "+box.animal.name)

  }


  // 3.

  new BoxCarrier(aBox) {

    def speed: Int = 12

  }

}

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Existentials considered harmful


On Wed, Jul 6, 2011 at 10:28 AM, Adriaan Moors <adriaan [dot] moors [at] epfl [dot] ch> wrote:
Do you think this is idiomatic code?
No. Existentials mainly exist to interop with Java wildcards.  
Given the compile errors how
would you implement this otherwise?
use type members (rewritten version of your puzzler below)
they are like existentials with more intuitive rules for packing and unpacking
I would say your puzzler illustrates that these rules are somewhat awkward for existentials. More precisely, these rules govern when the compiler decides you want to "peek into" the existential package, and what the scope is for the fresh type (the skolem) that you then invent for whatever type was in there. Each time you peek (unpack), you get a type that is incompatible to everything known to man until then (it's fresh). The tricky bit is that there's no easy way of grouping multiple peeks into one coordinated session.
This is exactly where type members excel, since the path on which you select the type member gives a kind of scattered scope for the same existential: as long as you select the same type member on equal paths, you get the same type.
hthadriaan

object TypeMembersConsideredCanonical {

  class Animal(val name: String)

  object Dog extends Animal("Dog")

  object Sheep extends Animal("Sheep")


  trait Tools[A] {

    def shave(a: A): A

  }

  def tools[A](a: A): Tools[A] = null // dummy

  object TransportBox {

    def apply[T](animal_ : T, tools_ : Tools[T]) = new TransportBox { type A = T; val animal = animal_; val tools = tools_ } 

  }

  trait TransportBox {

    type A <: Animal

    val animal: A

    val tools: Tools[A]

    

    def label: String = animal.name

  }


  // 1.

  def carry(box: TransportBox): Unit = {

    println(box.animal.name+" got carried away")

  }


  val aBox =

    if (math.random < 0.5)

      TransportBox(Dog, tools(Dog))

    else

      TransportBox(Sheep, tools(Sheep))


  // 2.

  aBox.tools.shave(aBox.animal)


  abstract class BoxCarrier[R <: Animal](box: TransportBox{type A = R}) {

    def speed: Int


    def talkToAnimal: Unit = println("The carrier says hello to "+box.animal.name)

  }


  // 3.

  new BoxCarrier(aBox) {

    def speed: Int = 12

  }

}


+1

Existentials in Scala exist primarily for Java interop, as something that's close enough to both Java wildcard types and Java raw types to be able to model them in most cases. They do tend to be awkward to use (not because we wanted to make them awkward, but because of their nature as existentials). That's why there exist type members as a better alternative. Wildcards in Java are more awkward than type members, a little bit less awkward than existentials, but much more complicated than either (I guess there are at maybe 2 or 3 people in the world who understand them fully).

Cheers

 -- Martin

milessabin
Joined: 2008-08-11,
User offline. Last seen 33 weeks 3 days ago.
Re: Existentials considered harmful

On Wed, Jul 6, 2011 at 9:28 AM, Adriaan Moors wrote:
> > Given the compile errors how would you implement this otherwise?
>
> use type members (rewritten version of your puzzler below)

Adriaan's solution using abstract types is the way to go if that's
possible. However, if you are forced to stick to using existentials,
then the trick is to avoid opening them multiple times ... that's
something that pattern matching helps us with,

object ExistentialsConsideredHarmful {
class Animal(val name: String)
object Dog extends Animal("Dog")
object Sheep extends Animal("Sheep")

trait Tools[A] {
def shave(a: A): A
}
def tools[A](a: A): Tools[A] = null // dummy

case class TransportBox[A <: Animal](animal: A, tools: Tools[A]) {
def label: String = animal.name
}

// 1.
def carry[A <: Animal](box: TransportBox[A]): Unit = {
println(box.animal.name+" got carried away")
}

val aBox =
if (math.random < 0.5)
TransportBox(Dog, tools(Dog))
else
TransportBox(Sheep, tools(Sheep))

// 2.
//aBox.tools.shave(aBox.animal)

// Use pattern match to avoid opening the existential twice
aBox match {
case TransportBox(animal, tools) => tools.shave(animal)
}

abstract class BoxCarrier[R <: Animal](box: TransportBox[R]) {
def speed: Int

def talkToAnimal: Unit = println("The carrier says hello to
"+box.animal.name)
}

// 3.
//val bc = new BoxCarrier(aBox) {

// Use pattern match to avoid opening the existential twice
// Type annotation on bc is required ... possible compiler bug?
val bc : BoxCarrier[_ <: Animal] = aBox match {
case tb : TransportBox[a] => new BoxCarrier(tb) {
def speed: Int = 12
}
}
}

Johannes Rudolph 2
Joined: 2010-02-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Existentials considered harmful

Thanks for your useful responses. The code seems a bit less obvious
with type members, however, probably only for the library writer which
would be ok, since you could add a `type` declaration to Adriaan's
example which makes the example looking exactly the same for the user
of the type at least.

So, can we try to make a rule out of this?

For simplifying these complex matters, my rule of thumb for using type
parameters vs. type members were:
* use type parameters when the type parameter is really part of the
external signature of a type
* use a type member when the type is only needed in the
implementation/library itself

My example is probably a hybrid because you have use cases (like 1.)
where the actual type parameter instance isn't needed and you would
usually use a type member and another use case (like 3.) where you
normally would use a type parameter.

So, the rule would be to prefer type members over type parameters if
you expect common use cases like 1. and 2.

Perhaps, someone can suggest a better wording...

Thanks Miles, good to know there's a workaround if you are bound to
use existentials for some reasons.

Johannes

On Wed, Jul 6, 2011 at 10:43 AM, Miles Sabin wrote:
> On Wed, Jul 6, 2011 at 9:28 AM, Adriaan Moors wrote:
>> > Given the compile errors how would you implement this otherwise?
>>
>> use type members (rewritten version of your puzzler below)
>
> Adriaan's solution using abstract types is the way to go if that's
> possible. However, if you are forced to stick to using existentials,
> then the trick is to avoid opening them multiple times ... that's
> something that pattern matching helps us with,
>
> object ExistentialsConsideredHarmful {
>  class Animal(val name: String)
>  object Dog extends Animal("Dog")
>  object Sheep extends Animal("Sheep")
>
>  trait Tools[A] {
>    def shave(a: A): A
>  }
>  def tools[A](a: A): Tools[A] = null // dummy
>
>  case class TransportBox[A <: Animal](animal: A, tools: Tools[A]) {
>    def label: String = animal.name
>  }
>
>  // 1.
>  def carry[A <: Animal](box: TransportBox[A]): Unit = {
>    println(box.animal.name+" got carried away")
>  }
>
>  val aBox =
>    if (math.random < 0.5)
>      TransportBox(Dog, tools(Dog))
>    else
>      TransportBox(Sheep, tools(Sheep))
>
>  // 2.
>  //aBox.tools.shave(aBox.animal)
>
>  // Use pattern match to avoid opening the existential twice
>  aBox match {
>    case TransportBox(animal, tools) => tools.shave(animal)
>  }
>
>  abstract class BoxCarrier[R <: Animal](box: TransportBox[R]) {
>    def speed: Int
>
>    def talkToAnimal: Unit = println("The carrier says hello to
> "+box.animal.name)
>  }
>
>  // 3.
>  //val bc = new BoxCarrier(aBox) {
>
>  // Use pattern match to avoid opening the existential twice
>  // Type annotation on bc is required ... possible compiler bug?
>  val bc : BoxCarrier[_ <: Animal] = aBox match {
>    case tb : TransportBox[a] => new BoxCarrier(tb) {
>      def speed: Int = 12
>    }
>  }
> }
>
>
> --
> Miles Sabin
> tel: +44 7813 944 528
> gtalk: miles [at] milessabin [dot] com
> skype: milessabin
> http://www.chuusai.com/
> http://twitter.com/milessabin
>

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Existentials considered harmful

I get an error for your code, Adriaan:

:26: error: overriding type A in trait TransportBox with
bounds >: Nothing <: TypeMembersConsideredCanonical.Animal;
type A has incompatible type
def apply[T](animal_ : T, tools_ : Tools[T]) = new
TransportBox { type A = T; val animal = animal_; val tools = tools_ }

^
Nothing <: this.A?
true
this.A <: $line1.$read.$iw.$iw.TypeMembersConsideredCanonical.Animal?
<: $line1.$read.$iw.$iw.TypeMembersConsideredCanonical.Animal?
false
<: $line1.$read.$iw.$iw.TypeMembersConsideredCanonical.Animal?
false
Any <: $line1.$read.$iw.$iw.TypeMembersConsideredCanonical.Animal?
<: $line1.$read.$iw.$iw.TypeMembersConsideredCanonical.Animal?
false
false
false

I suppose T should have been bounded as well?

On Wed, Jul 6, 2011 at 05:28, Adriaan Moors wrote:
>> Do you think this is idiomatic code?
>
> No. Existentials mainly exist to interop with Java wildcards.
>
>>
>> Given the compile errors how
>> would you implement this otherwise?
>
> use type members (rewritten version of your puzzler below)
> they are like existentials with more intuitive rules for packing and
> unpacking
> I would say your puzzler illustrates that these rules are somewhat awkward
> for existentials. More precisely, these rules govern when the compiler
> decides you want to "peek into" the existential package, and what the scope
> is for the fresh type (the skolem) that you then invent for whatever type
> was in there. Each time you peek (unpack), you get a type that is
> incompatible to everything known to man until then (it's fresh).
> The tricky bit is that there's no easy way of grouping multiple peeks into
> one coordinated session.
> This is exactly where type members excel, since the path on which you select
> the type member gives a kind of scattered scope for the same existential: as
> long as you select the same type member on equal paths, you get the same
> type.
> hth
> adriaan
>
> object TypeMembersConsideredCanonical {
>
>   class Animal(val name: String)
>
>   object Dog extends Animal("Dog")
>
>   object Sheep extends Animal("Sheep")
>
>   trait Tools[A] {
>
>     def shave(a: A): A
>
>   }
>
>   def tools[A](a: A): Tools[A] = null // dummy
>
>   object TransportBox {
>
>     def apply[T](animal_ : T, tools_ : Tools[T]) = new TransportBox { type A
> = T; val animal = animal_; val tools = tools_ }
>
>   }
>
>   trait TransportBox {
>
>     type A <: Animal
>
>     val animal: A
>
>     val tools: Tools[A]
>
>
>
>     def label: String = animal.name
>
>   }
>
>   // 1.
>
>   def carry(box: TransportBox): Unit = {
>
>     println(box.animal.name+" got carried away")
>
>   }
>
>   val aBox =
>
>     if (math.random < 0.5)
>
>       TransportBox(Dog, tools(Dog))
>
>     else
>
>       TransportBox(Sheep, tools(Sheep))
>
>   // 2.
>
>   aBox.tools.shave(aBox.animal)
>
>   abstract class BoxCarrier[R <: Animal](box: TransportBox{type A = R}) {
>
>     def speed: Int
>
>     def talkToAnimal: Unit = println("The carrier says hello to
> "+box.animal.name)
>
>   }
>
>   // 3.
>
>   new BoxCarrier(aBox) {
>
>     def speed: Int = 12
>
>   }
>
> }

adriaanm
Joined: 2010-02-08,
User offline. Last seen 31 weeks 4 days ago.
Re: Existentials considered harmful


On Wed, Jul 6, 2011 at 5:05 PM, Daniel Sobral <dcsobral [at] gmail [dot] com> wrote:
I suppose T should have been bounded as well?
yep
Grey
Joined: 2009-01-03,
User offline. Last seen 42 years 45 weeks ago.
Re: Existentials considered harmful

How can when show the Non-Existential of God in the following
https://gist.github.com/1096418

On Jul 6, 11:05 am, Daniel Sobral wrote:
> I get an error for your code, Adriaan:
>
> :26: error: overriding type A in trait TransportBox with
> bounds >: Nothing <: TypeMembersConsideredCanonical.Animal;
>  type A has incompatible type
>            def apply[T](animal_ : T, tools_ : Tools[T]) = new
> TransportBox { type A = T; val animal = animal_; val tools = tools_ }
>
>            ^
> Nothing <: this.A?
> true
> this.A <: $line1.$read.$iw.$iw.TypeMembersConsideredCanonical.Animal?
>   <: $line1.$read.$iw.$iw.TypeMembersConsideredCanonical.Animal?
>   false
>   <: $line1.$read.$iw.$iw.TypeMembersConsideredCanonical.Animal?
>   false
>   Any <: $line1.$read.$iw.$iw.TypeMembersConsideredCanonical.Animal?
>     <: $line1.$read.$iw.$iw.TypeMembersConsideredCanonical.Animal?
>     false
>   false
> false
>
> I suppose T should have been bounded as well?
>
>
>
>
>
>
>
>
>
> On Wed, Jul 6, 2011 at 05:28, Adriaan Moors wrote:
> >> Do you think this is idiomatic code?
>
> > No. Existentials mainly exist to interop with Java wildcards.
>
> >> Given the compile errors how
> >> would you implement this otherwise?
>
> > use type members (rewritten version of your puzzler below)
> > they are like existentials with more intuitive rules for packing and
> > unpacking
> > I would say your puzzler illustrates that these rules are somewhat awkward
> > for existentials. More precisely, these rules govern when the compiler
> > decides you want to "peek into" the existential package, and what the scope
> > is for the fresh type (the skolem) that you then invent for whatever type
> > was in there. Each time you peek (unpack), you get a type that is
> > incompatible to everything known to man until then (it's fresh).
> > The tricky bit is that there's no easy way of grouping multiple peeks into
> > one coordinated session.
> > This is exactly where type members excel, since the path on which you select
> > the type member gives a kind of scattered scope for the same existential: as
> > long as you select the same type member on equal paths, you get the same
> > type.
> > hth
> > adriaan
>
> > object TypeMembersConsideredCanonical {
>
> >   class Animal(val name: String)
>
> >   object Dog extends Animal("Dog")
>
> >   object Sheep extends Animal("Sheep")
>
> >   trait Tools[A] {
>
> >     def shave(a: A): A
>
> >   }
>
> >   def tools[A](a: A): Tools[A] = null // dummy
>
> >   object TransportBox {
>
> >     def apply[T](animal_ : T, tools_ : Tools[T]) = new TransportBox { type A
> > = T; val animal = animal_; val tools = tools_ }
>
> >   }
>
> >   trait TransportBox {
>
> >     type A <: Animal
>
> >     val animal: A
>
> >     val tools: Tools[A]
>
> >     def label: String = animal.name
>
> >   }
>
> >   // 1.
>
> >   def carry(box: TransportBox): Unit = {
>
> >     println(box.animal.name+" got carried away")
>
> >   }
>
> >   val aBox =
>
> >     if (math.random < 0.5)
>
> >       TransportBox(Dog, tools(Dog))
>
> >     else
>
> >       TransportBox(Sheep, tools(Sheep))
>
> >   // 2.
>
> >   aBox.tools.shave(aBox.animal)
>
> >   abstract class BoxCarrier[R <: Animal](box: TransportBox{type A = R}) {
>
> >     def speed: Int
>
> >     def talkToAnimal: Unit = println("The carrier says hello to
> > "+box.animal.name)
>
> >   }
>
> >   // 3.
>
> >   new BoxCarrier(aBox) {
>
> >     def speed: Int = 12
>
> >   }
>
> > }
>
> --
> Daniel C. Sobral
>
> I travel to the future all the time.

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Re: Existentials considered harmful

On Thu, Jul 21, 2011 at 00:06, RPR wrote:
> How can when show the Non-Existential of God in the following
> https://gist.github.com/1096418

Sorry, I don't understand what is the point you were making here.
Could you rephrase that?

Grey
Joined: 2009-01-03,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Existentials considered harmful
Should have been, "How can _one_ show the Non-Existential of God in the following..."  A sad, sorry and silly cut and paste joke made while watching Man. U. kick the crap out of MLS last night. 
existence, god yea/nay.... <sigh>

I was trying to construct a real life use case of an existential that had nothing to do with Java interop.The question was is it possible to construct a non-existential equivalent of God's breathLife method.  
 object God {   ... def breathLife (day: Int): MoldFromClay[_] =
    ...
    }
}



Johannes Rudolph 2
Joined: 2010-02-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Existentials considered harmful

Here would be an attempt to remove the occurrences of the existential
by use of type members as suggested before in this thread:

https://gist.github.com/1097283

On Thu, Jul 21, 2011 at 4:04 PM, Ray Racine wrote:
> Should have been, "How can _one_ show the Non-Existential of God in the
> following..."
> A sad, sorry and silly cut and paste joke made while watching Man. U. kick
> the crap out of MLS last night.
> existence, god yea/nay....
>
> I was trying to construct a real life use case of an existential that had
> nothing to do with Java interop.
> The question was is it possible to construct a non-existential equivalent of
> God's breathLife method.
>  object God {
> ...
> def breathLife (day: Int): MoldFromClay[_] =
>
>     ...
>     }
> }
>
>
>
>
>
>
>

Kris Nuttycombe
Joined: 2009-01-16,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Existentials considered harmful

Holy cow, how is it that I've never seen this syntax before?

abstract class BoxCarrier[R <: Animal](box: TransportBox{type A = R})

How long has that type member bounding syntax been available? I can't
count the number of headaches that would have saved me over the past 3
years. :P

Kevin Wright 2
Joined: 2010-05-30,
User offline. Last seen 26 weeks 4 days ago.
Re: Re: Existentials considered harmful
Also do-able with an alias:
  type ParameterisedTransportBox[R <: Animal] = TransportBox{type A = R}
Better still, you can then:
  def myMethod[A <: Animal](a: A)(implicit box: ParameterisedTransportBox[A])  //or use a context bound, if you prefer
and it will match if there's a single valid instance of TransportBox in implicit scope.



On 21 July 2011 16:12, Kris Nuttycombe <kris [dot] nuttycombe [at] gmail [dot] com> wrote:
Holy cow, how is it that I've never seen this syntax before?

abstract class BoxCarrier[R <: Animal](box: TransportBox{type A = R})

How long has that type member bounding syntax been available? I can't
count the number of headaches that would have saved me over the past 3
years. :P



--
Kevin Wright
mail: kevin [dot] wright [at] scalatechnology [dot] com
gtalk / msn : kev [dot] lee [dot] wright [at] gmail [dot] com quora: http://www.quora.com/Kevin-Wrightgoogle+: http://gplus.to/thecoda
kev [dot] lee [dot] wright [at] gmail [dot] com twitter: @thecoda
vibe / skype: kev.lee.wrightsteam: kev_lee_wright
"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra
Grey
Joined: 2009-01-03,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Existentials considered harmful
Ohhh I  really like it.  Feeling like Meg Ryan in a diner having lunch "YES, YES".  
While more verbose, it is not just equivalent to what I posted.  It is stronger in the sense that the opaque existential type is now "known".   Specifically, where before the animate method could only be defined within the the case class MoldFromClay as the existential type is opaque everywhere else.
In a sense the skolemization function type X = C removes, constrains and makes visible the previously opaque existential type.
i.e allowing for the moving of animate () anywhere.
https://gist.github.com/1097474
A second, alternate approach via implictly also gets one there but I'm not sure I prefer it over this approach.
Creatures -> AstSoul         -> Semantics
MoldFromClay -> BindAstToSemantics (a: A <: AST, s: S <: Semantics[AST]) extends Command
God  -> Interpreter which runs Commands.
On Thu, Jul 21, 2011 at 10:20 AM, Johannes Rudolph <johannes [dot] rudolph [at] googlemail [dot] com> wrote:
Here would be an attempt to remove the occurrences of the existential
by use of type members as suggested before in this thread:

https://gist.github.com/1097283

On Thu, Jul 21, 2011 at 4:04 PM, Ray Racine <ray [dot] racine [at] gmail [dot] com> wrote:
> Should have been, "How can _one_ show the Non-Existential of God in the
> following..."
> A sad, sorry and silly cut and paste joke made while watching Man. U. kick
> the crap out of MLS last night.
> existence, god yea/nay.... <sigh>
>
> I was trying to construct a real life use case of an existential that had
> nothing to do with Java interop.
> The question was is it possible to construct a non-existential equivalent of
> God's breathLife method.
>  object God {
> ...
> def breathLife (day: Int): MoldFromClay[_] =
>
>     ...
>     }
> }
>
>
>
>
>
>
>



--
Johannes

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

Viktor Klang
Joined: 2008-12-17,
User offline. Last seen 1 year 27 weeks ago.
Re: Re: Existentials considered harmful


On Thu, Jul 21, 2011 at 5:23 PM, Kevin Wright <kev [dot] lee [dot] wright [at] gmail [dot] com> wrote:
Also do-able with an alias:
  type ParameterisedTransportBox[R <: Animal] = TransportBox{type A = R}
Better still, you can then:
  def myMethod[A <: Animal](a: A)(implicit box: ParameterisedTransportBox[A])  //or use a context bound, if you prefer

what about:

def myMethod[A <: Animal : ParameterisedTransportBox]
 

and it will match if there's a single valid instance of TransportBox in implicit scope.



On 21 July 2011 16:12, Kris Nuttycombe <kris [dot] nuttycombe [at] gmail [dot] com> wrote:
Holy cow, how is it that I've never seen this syntax before?

abstract class BoxCarrier[R <: Animal](box: TransportBox{type A = R})

How long has that type member bounding syntax been available? I can't
count the number of headaches that would have saved me over the past 3
years. :P



--
Kevin Wright
mail: kevin [dot] wright [at] scalatechnology [dot] com
gtalk / msn : kev [dot] lee [dot] wright [at] gmail [dot] com quora: http://www.quora.com/Kevin-Wrightgoogle+: http://gplus.to/thecoda
kev [dot] lee [dot] wright [at] gmail [dot] com twitter: @thecoda
vibe / skype: kev.lee.wrightsteam: kev_lee_wright
"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra



--
Viktor Klang

Akka Tech LeadTypesafe - Enterprise-Grade Scala from the Experts

Twitter: @viktorklang
Kevin Wright 2
Joined: 2010-05-30,
User offline. Last seen 26 weeks 4 days ago.
Re: Re: Existentials considered harmful

On Thu, Jul 21, 2011 at 5:23 PM, Kevin Wright <kev [dot] lee [dot] wright [at] gmail [dot] com> wrote:
Also do-able with an alias:
  type ParameterisedTransportBox[R <: Animal] = TransportBox{type A = R}
Better still, you can then:
  def myMethod[A <: Animal](a: A)(implicit box: ParameterisedTransportBox[A])  //or use a context bound, if you prefer

what about:

def myMethod[A <: Animal : ParameterisedTransportBox]


As I said... Use a context bound if you prefer :)

Viktor Klang
Joined: 2008-12-17,
User offline. Last seen 1 year 27 weeks ago.
Re: Re: Existentials considered harmful


2011/7/21 Kevin Wright <kev [dot] lee [dot] wright [at] gmail [dot] com>

On Thu, Jul 21, 2011 at 5:23 PM, Kevin Wright <kev [dot] lee [dot] wright [at] gmail [dot] com> wrote:
Also do-able with an alias:
  type ParameterisedTransportBox[R <: Animal] = TransportBox{type A = R}
Better still, you can then:
  def myMethod[A <: Animal](a: A)(implicit box: ParameterisedTransportBox[A])  //or use a context bound, if you prefer

what about:

def myMethod[A <: Animal : ParameterisedTransportBox]


As I said... Use a context bound if you prefer :)

Who reads comments anyway?
 


--
Viktor Klang

Akka Tech LeadTypesafe - Enterprise-Grade Scala from the Experts

Twitter: @viktorklang
Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Existentials considered harmful
I thnk it's the same syntax as for structural types, e.g., just as you can dodef m(o: {def close: Unit})you can dodef m(o: AnyRef{def close: Unit})etc.I didn't occur to me either to use it for type members, but I think it's the regular syntax for structural types.

On Thu, Jul 21, 2011 at 11:12 AM, Kris Nuttycombe <kris [dot] nuttycombe [at] gmail [dot] com> wrote:
Holy cow, how is it that I've never seen this syntax before?

abstract class BoxCarrier[R <: Animal](box: TransportBox{type A = R})

How long has that type member bounding syntax been available? I can't
count the number of headaches that would have saved me over the past 3
years. :P

Johannes Rudolph 2
Joined: 2010-02-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Existentials considered harmful

Since I started this discussion I used type members instead of type
parameters in several places. As I noticed before this is a little bit
more complicated on the definition site but once you've have a type
alias scales very well.

Now I've hit another problem: Type members are treated differently wrt
to implicit lookup. If you have those classes:

trait X[T]

case class Test(a: Int)
object Test {
implicit val theX: X[Test] = null //...
}

Implicit search now finds the implicit value for X[Test] without an import:

implicitly[X[Test]]

That's because of implicit resolution rules which look in the
companion objects of types 'associated' with the type of the implicit
to look for. Unlike a type parameter which *is* associated, a type
member isn't. This leads the next example with type members to fail:

trait X {
type T
}
object X {
type Of[Y] = X { type T = Y}
}

case class Test(a: Int)
object Test {
implicit val theX: X.Of[Test] = null //...
}

implicitly[X.Of[Test]]

Is there any workaround for this problem? I'm not sure if extending
implicit search to additionally look into companions of concrete type
members is desirable for reasons of encapsulation and performance or
other problems, but that would perhaps be the ultimate solution.

Johannes Rudolph 2
Joined: 2010-02-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Existentials considered harmful

On Tue, Jul 26, 2011 at 2:52 PM, Johannes Rudolph
wrote:
> This leads the next example with type members to fail:
>
>  trait X {
>    type T
>  }
>  object X {
>    type Of[Y] = X { type T = Y}
>  }
>
>  case class Test(a: Int)
>  object Test {
>    implicit val theX: X.Of[Test] = null //...
>  }
>
>  implicitly[X.Of[Test]]
>
> Is there any workaround for this problem?

And here is the workaround which sprang to my mind while driving home from work:

trait X {
type T
}
object X {
type Of[Y] = X { type T = Y }

implicit def xof[T](implicit other: Helper[T]): X.Of[T] = null
}

trait Helper[T]
case class Test(a: Int)
object Test {
implicit val theX: Helper[Test] = null
}

implicitly[X.Of[Test]]

So, you have to make a little detour over a helper trait to make
implicit search look at the right places. So, for X it looks in object
X and then for a Helper[T] with a concrete T it can then look in the
companion object of T.

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: Re: Existentials considered harmful

On 7/26/11 5:52 AM, Johannes Rudolph wrote:
> Is there any workaround for this problem? I'm not sure if extending
> implicit search to additionally look into companions of concrete type
> members is desirable for reasons of encapsulation and performance or
> other problems, but that would perhaps be the ultimate solution.

I think this is in need of specification. My guess is that type members
will not figure into implicit search in this way. Type parameters are
inherently part of the visible identity of a class because they're
embedded in every reference to it. Type members, not so clear.

I wrote the attached test case to see how things are behaving. (There's
nothing newsworthy in there for you.) I didn't start looking for a
workaround yet: I suspect avoiding nontrivial changes is unlikely.

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: Re: Existentials considered harmful


On Tue, Jul 26, 2011 at 11:38 PM, Paul Phillips <paulp [at] improving [dot] org> wrote:
On 7/26/11 5:52 AM, Johannes Rudolph wrote:
Is there any workaround for this problem? I'm not sure if extending
implicit search to additionally look into companions of concrete type
members is desirable for reasons of encapsulation and performance or
other problems, but that would perhaps be the ultimate solution.

I think this is in need of specification.  

It is specified. Look for "implicit scope" in the SLS. So the question is, should we change the specification? Maybe. But it would make implicit search even slower than it already is. Also, do you follow just abstract types and aliases or inner classes as well. What about transtitive members of these? It's looks like a can of worms we do not want to get into.

Cheers

 -- Martin

Johannes Rudolph 2
Joined: 2010-02-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Re: Existentials considered harmful

On Wed, Jul 27, 2011 at 12:47 AM, martin odersky wrote:
> On Tue, Jul 26, 2011 at 11:38 PM, Paul Phillips wrote:
>>
>> On 7/26/11 5:52 AM, Johannes Rudolph wrote:
>>>
>>> Is there any workaround for this problem? I'm not sure if extending
>>> implicit search to additionally look into companions of concrete type
>>> members is desirable for reasons of encapsulation and performance or
>>> other problems, but that would perhaps be the ultimate solution.
>>
>> I think this is in need of specification.
>
> It is specified. Look for "implicit scope" in the SLS. So the question is,
> should we change the specification? Maybe. But it would make implicit search
> even slower than it already is. Also, do you follow just abstract types and
> aliases or inner classes as well. What about transtitive members of these?
> It's looks like a can of worms we do not want to get into.

That's what I thought, as well. So as long as my workaround is
working, I think the situation is ok. Though, the list in my head
about when and how to switch from type parameters to type members
still gets longer and longer...

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