[Lift] Re: is there a name for this pattern?
Eric Bowman wrote: The basic trick where a superclass has its subclass as a type parameter, e.g. class User extends MegaProtoUser[User] I've run into this before, I remember struggling to get it, then getting it, but I can't recall the epiphany. But obviously this is a relatively common technique, so something to google is much appreciated. Thanks all for the great replies! cheers, Eric -- Eric Bowman Boboco Ltd ebow...@boboco.ie http://www.boboco.ie/ebowman/pubkey.pgp +35318394189/+353872801532 --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: is there a name for this pattern?
Having type parameters on Mapper classes helps with a lot of things and is not going to go away. Marius and I discussed this a bunch of months ago onlist. Basically, the QueryParam (stuff that's passed to find(), findAll(), etc.) must be type checked against the current Mapper so you don't pass in queries for fields not on the current mapper. To get this to play nicely with the type inferencer (so you don't have to type By[User, Long](User.friend, 33L)), you need type parameters. There are shadow dependent types in the Mapper class so that things like IdPK work without having to type IdPK[Address]. But, this issue has popped up every 6 months since Lift began and I've looked at the issue and tried to have less verbose types declarations, but at the end of the day, type parameters serve an important purpose in Mapper and will be staying in Mapper (and Record). On Fri, Jun 19, 2009 at 5:22 PM, Alex Boisvert boisv...@intalio.com wrote: On Thu, Jun 18, 2009 at 11:18 PM, Eric Bowman ebow...@boboco.ie wrote: The basic trick where a superclass has its subclass as a type parameter, e.g. class User extends MegaProtoUser[User] I've run into this before, I remember struggling to get it, then getting it, but I can't recall the epiphany. But obviously this is a relatively common technique, so something to google is much appreciated. I agree with Kris, this circular/recursive type pattern has two edges and I'm not sure one is worth the other. The main benefit I see with this pattern is that it enforces class User to pass the User type parameter to Mapper, so you don't end up with a mixin that doesn't make sense... although I doubt this error would actually happen in reality because you'd quickly realize at design-time that calling any method on User/MetaUser would return you something different than what you expect. ( The drawback is that type signatures are more verbose, complex and viral (if you care about extensibility). Just try explaining the type signature to Scala newbie and they'll run away scared! :) An alternative design would be something of the form, trait Mapper { type MapperType } class User extends Mapper { type MapperType : User } I think this design would be simpler to read and understand, without any practical loss in type checking. (As a side note, if you don't specify the correct type to Mapper with the current design, you get an error message that's hardly helpful to newbies, so it doesn't help much either... but I disgress.) While I'm at it, there are also places in the code that could use this.type instead of MapperType (also called A as parameter) to give out an even more specific type without any tradeoffs. A prime example of this would be Mapper.saveMe(). Just to be clear, I'm not proposing anything be changed in Lift's current mapper since it would break compatibility I'm just suggesting it for consideration in future design work... perhaps in Lift's new record module? alex -- Lift, the simply functional web framework http://liftweb.net Beginning Scala http://www.apress.com/book/view/1430219890 Follow me: http://twitter.com/dpp Git some: http://github.com/dpp --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: is there a name for this pattern?
Eric, I believe that something like that, in C++ at least, is referred to as the curiously recurring template pattern. Jeremy On Fri, Jun 19, 2009 at 1:18 AM, Eric Bowman ebow...@boboco.ie wrote: The basic trick where a superclass has its subclass as a type parameter, e.g. class User extends MegaProtoUser[User] I've run into this before, I remember struggling to get it, then getting it, but I can't recall the epiphany. But obviously this is a relatively common technique, so something to google is much appreciated. Thanks, Eric -- Eric Bowman Boboco Ltd ebow...@boboco.ie http://www.boboco.ie/ebowman/pubkey.pgp +35318394189/+353872801532http://www.boboco.ie/ebowman/pubkey.pgp%0A+35318394189/+353872801532 --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: is there a name for this pattern?
On Thu, Jun 18, 2009 at 11:18 PM, Eric Bowman ebow...@boboco.ie wrote: The basic trick where a superclass has its subclass as a type parameter, e.g. class User extends MegaProtoUser[User] The circular dependent type that gives Martin and the compiler fits? :-) I've run into this before, I remember struggling to get it, then getting it, but I can't recall the epiphany. But obviously this is a relatively common technique, so something to google is much appreciated. Thanks, Eric -- Eric Bowman Boboco Ltd ebow...@boboco.ie http://www.boboco.ie/ebowman/pubkey.pgp +35318394189/+353872801532http://www.boboco.ie/ebowman/pubkey.pgp%0A+35318394189/+353872801532 -- Lift, the simply functional web framework http://liftweb.net Beginning Scala http://www.apress.com/book/view/1430219890 Follow me: http://twitter.com/dpp Git some: http://github.com/dpp --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: is there a name for this pattern?
I've always called this recursive type parameterization. It's useful when the base class needs to know the type that it's eventually instantiated as, usually so that it can provide implementations of methods where dispatch is based upon the instantiated type. I've found myself using the pattern mostly in cases where I wanted to have parallel class hierarchies, say of model objects and rendering strategies for those models. In this case, the model base class would have a recursive type parameter, and the rendering strategy would be parameterized with respect to the model type. ex: class Model[T : Model[T]] { self: T = def render(r: Renderer[T]) { r.render(this) } } class ConcreteModel extends Model[ConcreteModel] class Renderer[T : Model[T]] { abstract def render(model: T) } class ConcreteModelRenderer extends Renderer[ConcreteModel] { def render(model: ConcreteModel) { ... } } In Java, you have a little more boilerplate because you don't have an explicit self-type, so you have to define an abstract method self: T which you implement trivially in each subclass. Recursive self-types can be really useful but they're definitely a double-edged sword because they're sort of viral; you have to add a bunch of boilerplate type information everywhere that you refer to the base type, and if you ever discard the more specific type information (in the example above, say by putting a bunch of different subclasses of Model into a List[Model[_]]) then you can never get it back without reflection, and any use of the members of that list in a contravariant position (say as an argument to another method call) becomes impossible. Kris On Fri, Jun 19, 2009 at 12:18 AM, Eric Bowmanebow...@boboco.ie wrote: The basic trick where a superclass has its subclass as a type parameter, e.g. class User extends MegaProtoUser[User] I've run into this before, I remember struggling to get it, then getting it, but I can't recall the epiphany. But obviously this is a relatively common technique, so something to google is much appreciated. Thanks, Eric -- Eric Bowman Boboco Ltd ebow...@boboco.ie http://www.boboco.ie/ebowman/pubkey.pgp +35318394189/+353872801532 --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: is there a name for this pattern?
This is also a common technique in C# too. Unfortunately, I'm not sure of the pattern's name either. I usually just google for non-generic inheritance from generic class On Fri, Jun 19, 2009 at 10:31 AM, Jeremy Dayjeremy@gmail.com wrote: Eric, I believe that something like that, in C++ at least, is referred to as the curiously recurring template pattern. Jeremy On Fri, Jun 19, 2009 at 1:18 AM, Eric Bowman ebow...@boboco.ie wrote: The basic trick where a superclass has its subclass as a type parameter, e.g. class User extends MegaProtoUser[User] I've run into this before, I remember struggling to get it, then getting it, but I can't recall the epiphany. But obviously this is a relatively common technique, so something to google is much appreciated. Thanks, Eric -- Eric Bowman Boboco Ltd ebow...@boboco.ie http://www.boboco.ie/ebowman/pubkey.pgp +35318394189/+353872801532 --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: is there a name for this pattern?
On Thursday June 18 2009, Eric Bowman wrote: The basic trick where a superclass has its subclass as a type parameter, e.g. class User extends MegaProtoUser[User] I've run into this before, I remember struggling to get it, then getting it, but I can't recall the epiphany. But obviously this is a relatively common technique, so something to google is much appreciated. The example I find helps the most to make this comprehensible is Ordered (generically, not necessarily Scala's actual Ordered class): What does it mean for something to be able to be ordered? It means that if you give me two instances of such a thing, I can tell you whether they stand in a particular order (which may be numeric but need not be; other examples include lexicographic ordering or something defined on a metric space like edit distance). No breathtaking insight there. So naturally when I want to capture orderability generically, I have to say that it establishes a relationship with other things of the same (or compatible / conformant) type. Thus: class Ordered[WithRespectToWhat] Now, to define a class that conforms to Ordered, we have to specify ordered with respect to what? And the usual answer is with respect to the same type: class Numeric extends Ordered[Numeric] You might reasonably want that to be covariant: class Ordered[+WithRespectToWhat] But by cheating (me cheating in writing this up, that is) we find in the ScalaDocs this note on the page for Ordered [1]: Note that since version 2006-07-24 this trait is no longer covariant in a. It is important that the equals method for an instance of Ordered[A] be consistent with the compare method. However, due to limitations inherent in the type erasure semantics, there is no reasonable way to provide a default implementation of equality for instances of Ordered[A]. Therefore, if you need to be able to use equality on an instance of Ordered[A] you must provide it yourself either when inheiriting or instantiating. [1] http://www.scala-lang.org/docu/files/api/scala/Ordered.html Thanks, Eric Randall Schulz --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: is there a name for this pattern?
On Thu, Jun 18, 2009 at 11:18 PM, Eric Bowman ebow...@boboco.ie wrote: The basic trick where a superclass has its subclass as a type parameter, e.g. class User extends MegaProtoUser[User] I've run into this before, I remember struggling to get it, then getting it, but I can't recall the epiphany. But obviously this is a relatively common technique, so something to google is much appreciated. I agree with Kris, this circular/recursive type pattern has two edges and I'm not sure one is worth the other. The main benefit I see with this pattern is that it enforces class User to pass the User type parameter to Mapper, so you don't end up with a mixin that doesn't make sense... although I doubt this error would actually happen in reality because you'd quickly realize at design-time that calling any method on User/MetaUser would return you something different than what you expect. ( The drawback is that type signatures are more verbose, complex and viral (if you care about extensibility). Just try explaining the type signature to Scala newbie and they'll run away scared! :) An alternative design would be something of the form, trait Mapper { type MapperType } class User extends Mapper { type MapperType : User } I think this design would be simpler to read and understand, without any practical loss in type checking. (As a side note, if you don't specify the correct type to Mapper with the current design, you get an error message that's hardly helpful to newbies, so it doesn't help much either... but I disgress.) While I'm at it, there are also places in the code that could use this.type instead of MapperType (also called A as parameter) to give out an even more specific type without any tradeoffs. A prime example of this would be Mapper.saveMe(). Just to be clear, I'm not proposing anything be changed in Lift's current mapper since it would break compatibility I'm just suggesting it for consideration in future design work... perhaps in Lift's new record module? alex --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---