Why fields are declared as 'object' with Mapper/Record?

36 views
Skip to first unread message

Oleg G.

unread,
Oct 6, 2009, 2:38:57 AM10/6/09
to Lift
Sorry if its a stupid question, but why?

I like the idea very much and trying to understand all the aspects.
Fields declared as 'objects' can't be overridden. Is it intended? If
so why?

Consider following oversimplified example:
trait Field
trait Prop1
trait Prop2
trait Prop3

class Person {
val name = new Field with Prop1 with Prop2
}

class CustomPerson extends Person {
override val name = new Field with Prop1 with Prop2 with Prop3
}

Is there something wrong?

Dirk Louwers

unread,
Oct 6, 2009, 9:12:54 AM10/6/09
to Lift
Well, I am only a beginner myself but here are my 2 cents:

- My guess is that they are declared as inner objects to make it
possible to reach certain global field properties through the
companion MetaMapper object.
- As far as I know traits cannot be directly instantiated, only
extended or mixed in.

Dirk Louwers

Kris Nuttycombe

unread,
Oct 6, 2009, 10:52:46 AM10/6/09
to lif...@googlegroups.com
One major reason for the "inner object" pattern is that when you have
a singleton object extending a trait, it is possible for the trait to
reflect upon that object's class to obtain information like the name
of the field. You'll see this pattern used throughout Lift (AnyVar
subclasses RequestVar and SessionVar *must* be instantiated as objects
- if you do something like this:

class StringRequestVar(x: => String) extends RequestVar[String](x)

object MyState {
val v1 = new StringRequestVar("foo")
val v2 = new StringRequestVar("bar")
}

then v2 will actually stomp on v1 at instantiation, and any assignment
to v1 will stomp on v2 and vice versa.

(I don't really care for this pattern either, but understand the
justification for it.)

Kris

David Pollak

unread,
Oct 6, 2009, 11:10:03 AM10/6/09
to lif...@googlegroups.com
On Tue, Oct 6, 2009 at 7:52 AM, Kris Nuttycombe <kris.nu...@gmail.com> wrote:

One major reason for the "inner object" pattern is that when you have
a singleton object extending a trait, it is possible for the trait to
reflect upon that object's class to obtain information like the name
of the field. You'll see this pattern used throughout Lift (AnyVar
subclasses RequestVar and SessionVar *must* be instantiated as objects
- if you do something like this:

class StringRequestVar(x: => String) extends RequestVar[String](x)

object MyState {
  val v1 = new StringRequestVar("foo")
  val v2 = new StringRequestVar("bar")
}

then v2 will actually stomp on v1 at instantiation, and any assignment
to v1 will stomp on v2 and vice versa.

(I don't really care for this pattern either, but understand the
justification for it.)

I don't care for the pattern, but it comes from Scala history... so....

In the days of Scala 2.3, an inner object had different class and method visibility than a val instantiated in the same way.  So, if you had:

object foo extends Object {
  def bar = "You can call me"
}
You could call bar.  However:

val foo = new Object {
  def bar = "You can't call me"
}
foo would be an Object, not visible as a subclass of Object.  So, that's the first reason for object vs. val.

The second reason is that it's possible to disambiguate:

object foo extends MappedString(this, 32)

from

var foo: MappedString[OtherThing] = _

It may be possible to disambiguate these now, but in the 2.3 days, it wasn't.

In terms of the RequestVar stuff, the RequestVar and SessionVar generates its unique name (so it knows how to put itself in a unique place in backing store) based on its class and each SessionVar and RequestVar have a unique class name based on being a subclass of RequestVar rather than an instance of RequestVar.

Hope this help.

Thanks,

David


 

Kris


On Tue, Oct 6, 2009 at 12:38 AM, Oleg G. <ojo...@gmail.com> wrote:
>
> Sorry if its a stupid question, but why?
>
> I like the idea very much and trying to understand all the aspects.
> Fields declared as 'objects' can't be overridden. Is it intended? If
> so why?
>
> Consider following oversimplified example:
>  trait Field
>  trait Prop1
>  trait Prop2
>  trait Prop3
>
>  class Person {
>    val name = new Field with Prop1 with Prop2
>  }
>
>  class CustomPerson extends Person {
>    override val name = new Field with Prop1 with Prop2 with Prop3
>  }
>
> Is there something wrong?
>
> >
>





--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Surf the harmonics

Tim Nelson

unread,
Oct 6, 2009, 11:20:16 AM10/6/09
to lif...@googlegroups.com

On Tue, Oct 6, 2009 at 10:10 AM, David Pollak <feeder.of...@gmail.com> wrote:

I don't care for the pattern, but it comes from Scala history... so....

In the days of Scala 2.3, an inner object had different class and method visibility than a val instantiated in the same way.  So, if you had:

object foo extends Object {
  def bar = "You can call me"
}
You could call bar.  However:

val foo = new Object {
  def bar = "You can't call me"
}
foo would be an Object, not visible as a subclass of Object.  So, that's the first reason for object vs. val.

The second reason is that it's possible to disambiguate:

object foo extends MappedString(this, 32)

from

var foo: MappedString[OtherThing] = _

It may be possible to disambiguate these now, but in the 2.3 days, it wasn't.


It sounds like both reasons for using objects are legacy related. Is there any reason (other than time constraints) to not update the Record framework to take advantage of the newer language features?

Tim

David Pollak

unread,
Oct 6, 2009, 12:32:22 PM10/6/09
to lif...@googlegroups.com
On Tue, Oct 6, 2009 at 8:20 AM, Tim Nelson <tnel...@gmail.com> wrote:

On Tue, Oct 6, 2009 at 10:10 AM, David Pollak <feeder.of...@gmail.com> wrote:

I don't care for the pattern, but it comes from Scala history... so....

In the days of Scala 2.3, an inner object had different class and method visibility than a val instantiated in the same way.  So, if you had:

object foo extends Object {
  def bar = "You can call me"
}
You could call bar.  However:

val foo = new Object {
  def bar = "You can't call me"
}
foo would be an Object, not visible as a subclass of Object.  So, that's the first reason for object vs. val.

The second reason is that it's possible to disambiguate:

object foo extends MappedString(this, 32)

from

var foo: MappedString[OtherThing] = _

It may be possible to disambiguate these now, but in the 2.3 days, it wasn't.


It sounds like both reasons for using objects are legacy related.

Generally, yes.  I haven't dived into the reflection-based layout or the Scala-related reflection stuff in 2.8 deeply enough to say that we could do a val-based Record implementation.
 
Is there any reason (other than time constraints) to not update the Record framework to take advantage of the newer language features?

Tim


Oleg G.

unread,
Oct 6, 2009, 12:50:01 PM10/6/09
to Lift
Thanks for all the answers and especially for David's clarification.
It would be really cool to upgrade the 'keeping the meaning with the
bytes' thing (http://blog.lostlake.org/index.php?/archives/19-Keeping-
the-meaning-with-the-bytes.html) to allow extension/customization.

> Generally, yes.  I haven't dived into the reflection-based layout or the
> Scala-related reflection stuff in 2.8 deeply enough to say that we could do
> a val-based Record implementation.
>
> > Is there any reason (other than time constraints) to not update the Record
> > framework to take advantage of the newer language features?
>
> > Tim
>
> --
> Lift, the simply functional web frameworkhttp://liftweb.net
> Beginning Scalahttp://www.apress.com/book/view/1430219890

David Pollak

unread,
Oct 6, 2009, 12:59:48 PM10/6/09
to lif...@googlegroups.com
On Tue, Oct 6, 2009 at 9:50 AM, Oleg G. <ojo...@gmail.com> wrote:

Thanks for all the answers and especially for David's clarification.
It would be really cool to upgrade the 'keeping the meaning with the
bytes' thing (http://blog.lostlake.org/index.php?/archives/19-Keeping-
the-meaning-with-the-bytes.html
) to allow extension/customization.

What kind of extensions/customizations?

MappedXXX can all be subclassed, extended and customized.
 

> Generally, yes.  I haven't dived into the reflection-based layout or the
> Scala-related reflection stuff in 2.8 deeply enough to say that we could do
> a val-based Record implementation.
>
> > Is there any reason (other than time constraints) to not update the Record
> > framework to take advantage of the newer language features?
>
> > Tim
>
> --
> Lift, the simply functional web frameworkhttp://liftweb.net
> Beginning Scalahttp://www.apress.com/book/view/1430219890
> Follow me:http://twitter.com/dpp
> Surf the harmonics





--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890

Oleg G.

unread,
Oct 6, 2009, 1:16:24 PM10/6/09
to Lift
As i said before i'm not sure that i'm getting the whole picture and
maybe my initial question is incorrect in its root. Still:

Suppose i have a Person class declared with Mapper/Record and i want
to reuse the class and all the code associated with it in another
module/project. My first thought was to extend the Person class and
override some of its fields by mixing in some additional traits (see
my simplified example code in the initial message). But i noticed that
inner objects cannot be overriden (its not obvious for me but i can
get it if i dig it).

So how do i reuse Mapper/Record based code if i need to extend/
customize the data structures?

On Oct 6, 11:59 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:

Naftoli Gugenheim

unread,
Oct 6, 2009, 2:06:24 PM10/6/09
to lif...@googlegroups.com
I want to raise another related point.
Currently all mapped fields have to be passed "this". Is there a way to not require it?
Can Mapper or Record use an implicit object etc. to fill in "this"?
Or could there be an inner derived class that knows its parent, like I recently did for ModelView? In other words, e.g., a MappedString that is a member of Mapper and derived from the real MappedString. This option is less optimal because (1) it's possible it will break code, although not necessarily, and (2) it cannnot be extended to a MappedField defined outside of lift-mapper. A similar approach that solves #2 is traits that can be mixed into a Mapper containing these MappedField subclasses, but it has the disadvantage of requiring more traits to be mixed in.
In any case, although it is now possible to access members of an anonymous class, doesn't it currently use reflection? So maybe this is dependent on that being improved.
On the other hand the advantage of vals is that they don't have to be lazy, thus eliminating all the reflection necessary to get the list of fields.
To go off topic, maybe it would make sense, in order to allow either vals or objects *or JPA entities*, a base trait that defines the ability to get information about fields. A JPA subtrait could use JPA APIs, while the main Lift subtrait could either use reflection to get the objects, or use eager vals that populate the entities knowledge of them directly.


-------------------------------------
David Pollak<feeder.of...@gmail.com> wrote:

On Tue, Oct 6, 2009 at 8:20 AM, Tim Nelson <tnel...@gmail.com> wrote:

>
> On Tue, Oct 6, 2009 at 10:10 AM, David Pollak <
> feeder.of...@gmail.com> wrote:
>
>>
>> I don't care for the pattern, but it comes from Scala history... so....
>>
>> In the days of Scala 2.3, an inner object had different class and method
>> visibility than a val instantiated in the same way. So, if you had:
>>
>> object foo extends Object { def bar = "You can call me"
>> }
>>
>> You could call bar. However:
>>
>> val foo = new Object { def bar = "You can't call me"
>> }
>>
>> foo would be an Object, not visible as a subclass of Object. So, that's
>> the first reason for object vs. val.
>>
>> The second reason is that it's possible to disambiguate:
>>
>> object foo extends MappedString(this, 32)
>>
>> from
>>
>> var foo: MappedString[OtherThing] = _
>>
>> It may be possible to disambiguate these now, but in the 2.3 days, it
>> wasn't.
>>
>
>
> It sounds like both reasons for using objects are legacy related.
>

Generally, yes. I haven't dived into the reflection-based layout or the
Scala-related reflection stuff in 2.8 deeply enough to say that we could do
a val-based Record implementation.


> Is there any reason (other than time constraints) to not update the Record
> framework to take advantage of the newer language features?
>
> Tim
>
> >
>


--

Stefan Scott

unread,
Oct 6, 2009, 4:34:08 PM10/6/09
to Lift
I hope I don't wander too far off-topic here as I wend my way to the
question at the end of this post ("would it be useful to look at
something like the OWL/SWRL web ontology and rule languages for
liftweb?").

I tend to generalize/abstract a lot - so if anyone with a theoretical
bent is willing to peruse the following, I would appreciate any
feedback as to whether it could be applicable to liftweb.

Basically I just want to wonder out loud here whether support in
liftweb for some of the specific ideas referenced in this thread
(particularly the "keeping the meaning with the bytes" thing) could be
helped along by looking at some possibly related theoretical work
being done involving contexts and policies for web services, eg here:

http://www.csee.umbc.edu/swpw/papers/
PDF! - http://www.csee.umbc.edu/swpw/papers/denker.pdf

Some of this admittedly "theoretical" work from this particular
conference is being implemented in machine-executable languages (eg,
Maude) which are also fortunately quite close to Scala, thanks to
Scala's support for both the functional and the object-oriented
programming paradigms. (Maude is similar to Haskell and ML, which are
sometimes also compared to Scala.)

This means that it might be quite straightforward to map any needed
portions of this related theoretical work on policies and contexts for
web services from languages such as Maude to liftweb in a fairly
straightforward manner.


((Full disclosure: Maude is one of the few languages I know and use,
eg for conceptual data modeling of entity-relationship databases:

http://www.springerlink.com/content/35gxk6g83vn6q648/

I mention this mainly to show that although I'm still a relative
newbie when it comes to web frameworks - particularly regarding
security - I do have some background in specification of databases
with contraints which I hope could somehow be applicable to database-
oriented programming in liftweb.))


I was intrigued by the 2006 post by DP mentioned upthread:

http://blog.lostlake.org/index.php?/archives/19-Keeping-the-meaning-with-the-bytes.html

where he suggests some excellent web framework design principles:

- "The semantic meaning of fields on objects should be retained
throughout the field's life."

- "Conversion to a semantic-free format (String, double, etc.) should
be done as late in the field's life-cycle (e.g., a String returned
from 'render as HTML') as possible."

- "Every field of every object {should} be semantically meaningful."

- - "getSSN() returns an object of SSN class that derives from
Taxpayer class that derives from SensitiveIndentifyingInformation
class."

- - "the toHtml() method on the SSN class returns '***-**-****' unless
the context in which the SSN is being accessed has permissions /
grants permissions to see more."

- "All pieces of the object are defined in a central place."


It would just seem that the above-postulated "central place" could be
(built using some of the rich facilities provided by) Scala's type
system itself - perhaps even neatly packaged into a kind of "liftweb
MOP" (meta object protocol, like CLOS added to Lisp), or presented as
a DSL (domain-specific language).


The road from design-time to run-time is of course a sequence of
stages where abstraction is concretized and type info is erased - as
we move from an SSN, to a String, and eventually to a bunch of pixels
on the screen.

I would think that the line could be drawn so that the web framework
always "knows" that something is an SSN. I don't know enough about
JVMs to understand if or when this kind of "type info" is eventually
"erased" - but I understand that it's important that a level of
abstraction should be provided to the end-programmer using a web
framework where this type info is always available, enabling centrally
specified policies to make the web app just "do the right thing"
depending on the particular context.


Coming back to the problem of providing a completely general framework
for specifying policies and contexts: Would any of the theoretical
papers from, say, this conference:

http://www.csee.umbc.edu/swpw/papers/

provide any useful pointers as to how to approach this?

I'm not sure if the implementation languages there seem too ivory-
tower, or the conceptual languages there sound too "designed by
committee" (eg, OWL/SWRL), in the eyes of liftweb committers and
programmers who might be quite understandably up to their necks in
hands-on issues like cross-site scripting attacks etc. But formal,
theoretical stuff can have its place, since:

(a) Scala itself is based on a rigorous theoretical, formal foundation

(b) tools supporting checking of programs written in commercial/
industrial languages (eg, Erlang) are now being written in formal/
academic languages (eg, Maude):

(WARNING - PS file!) http://www-i2.informatik.rwth-aachen.de/old/Staff/Current/neuhaeusser/publications/wrla06.ps


And Scala's fuctional/object-oriented paradigms and very rich type
system (making Scala very close to many formal/academic languages)
could make it straightforward to map code and concepts from some of
these languages and systems to Scala and liftweb.

For example, one of these papers:

PDF! http://www.csee.umbc.edu/swpw/papers/denker.pdf
"Towards Integrated Specification and Analysis of Machine-Readable
Policies Using Maude" by Rukman Senanayake, Grit Denker and Jon Pearce

uses the executable specification language Maude (which, like Scala,
supports both functional and object-oriented programming paradigms,
pattern matching, type parameterization, per-operator user-definable
evaluation strategies, and reflection) to provide an executable
specification for an example of a web service which processes returns
for Amazon.

Would it be useful to look at stuff like the "OWL-S" process model and
"OWL/SWRL" web ontology and rule languages used in the above paper, as
a point of reference for providing a more general set of facilities
for dealing with policies and contexts in liftweb?

David Pollak

unread,
Oct 6, 2009, 4:54:33 PM10/6/09
to lif...@googlegroups.com
On Tue, Oct 6, 2009 at 10:16 AM, Oleg G. <ojo...@gmail.com> wrote:

As i said before i'm not sure that i'm getting the whole picture and
maybe my initial question is incorrect in its root. Still:

Suppose i have a Person class declared with Mapper/Record and i want
to reuse the class and all the code associated with it in another
module/project. My first thought was to extend the Person class and
override some of its fields by mixing in some additional traits (see
my simplified example code in the initial message). But i noticed that
inner objects cannot be overriden (its not obvious for me but i can
get it if i dig it).

You can't do this.  There was a Scala language feature that would have allowed this (overriding an object) but it hasn't made the cut for 2.7 or 2.8.
 

So how do i reuse Mapper/Record based code if i need to extend/
customize the data structures?

Unfortunately, you can't.  Once you've got a field defined, there's nothing you can do to change it in a subclass.
 

On Oct 6, 11:59 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:
> On Tue, Oct 6, 2009 at 9:50 AM, Oleg G. <ojo...@gmail.com> wrote:
>
> > Thanks for all the answers and especially for David's clarification.
> > It would be really cool to upgrade the 'keeping the meaning with the
> > bytes' thing (http://blog.lostlake.org/index.php?/archives/19-Keeping-
> > the-meaning-with-the-bytes.html) to allow extension/customization.
>
> What kind of extensions/customizations?
>
> MappedXXX can all be subclassed, extended and customized.


Naftoli Gugenheim

unread,
Oct 6, 2009, 8:28:36 PM10/6/09
to lif...@googlegroups.com
What you can do if I'm not mistaken, although not as good, is define
in a base trait,
def myField: MappedXXX ...
and give different concrete object implementations. This way you have
a common type that guarantees the "field."
Message has been deleted

Oleg G.

unread,
Oct 12, 2009, 5:22:58 AM10/12/09
to Lift
I've spent some time and came up with this code:
http://github.com/ojow/Random-code/blob/master/Test.scala
I think it should allow all the needed stuff, it keeps meaning with
the bytes and also:
* its reusable/extensible in many directions, no static stuff
* i checked generated .class files and looks like no extra hidden
fields or heave methods are generated, so its technically light-weight
* it doesn't use/need reflection (not sure if its good or bad)

Would be nice to hear some feedback on that.

P.S. I'm still a newbie in Scala so excuse me if this code is
worthless and if i'm just stealing your time.
> Beginning Scalahttp://www.apress.com/book/view/1430219890

David Pollak

unread,
Oct 12, 2009, 12:37:42 PM10/12/09
to lif...@googlegroups.com


On Mon, Oct 12, 2009 at 1:55 AM, Oleg G. <ojo...@gmail.com> wrote:

I spent some time and came up with this code:

http://github.com/ojow/Random-code/blob/master/Test.scala
I think it should allow all the needed stuff, it keeps meaning with
the bytes and also:
* its reusable and extensible in many directions
* looks like generated .class files don't have any additional hidden
fields or heavy methods, so it's technically light-weight
* it doesn't need reflection (not sure if its good or bad :)


Would be nice to hear some feedback on that.

P.S. I'm still a newbie in Scala so please excuse me if this code is
worthless and if i'm just wasting your time.

Oleg,

I wrote the existing Mapper code when I was 2-3 weeks into Scala.  It's very Javaesque.

Your code is interesting, but the complexity of what the developer has to type would make it pretty difficult to use in practice.  Sorry.

David
 
> Beginning Scalahttp://www.apress.com/book/view/1430219890

> Follow me:http://twitter.com/dpp
> Surf the harmonics


naf g

unread,
Oct 12, 2009, 1:08:21 PM10/12/09
to lif...@googlegroups.com
Why do you have two classes, Model and Record? What are they and why are they interdependent?

-------------------------------------
David Pollak<feeder.of...@gmail.com> wrote:

Oleg G.

unread,
Oct 12, 2009, 1:38:17 PM10/12/09
to Lift
Well i don't think its a lot of complexity.

Yes its 4 declarations instead of 1 for each field. But 3 of those are
generatable by IDE same way as any Java IDE generates setters and
getters. All the rest is spring-style instantiations/injections. And
usage is even simplier and readable (i mean syntax
record.field=value). The field list method can be implemented via
reflection to reduce amount of code required from users.

Anyway thanks for your feedback, David, i really appreciate it. I'll
try to see if i can integrate my model with Lift and implement it for
Google Datastore. Then i'll report back. I understand that i still
need to do more work before any serious conclusions can be made.

Oleg G.

unread,
Oct 12, 2009, 1:48:16 PM10/12/09
to Lift
On 13 окт, 00:08, naf g <naftoli...@gmail.com> wrote:
> Why do you have two classes, Model and Record? What are they and why are they interdependent?
I thought about Model being a place for metainformation (like database
structure, options/properties etc), or maybe it can be described as
global context for Record-related activity. They are dependant to
allow Field operations to reach the context.

Naftoli Gugenheim

unread,
Oct 12, 2009, 2:00:03 PM10/12/09
to lif...@googlegroups.com
So Model represents the database connection?

2009/10/12 Oleg G. <ojo...@gmail.com>

David Pollak

unread,
Oct 12, 2009, 2:19:18 PM10/12/09
to lif...@googlegroups.com
On Mon, Oct 12, 2009 at 10:38 AM, Oleg G. <ojo...@gmail.com> wrote:

Well i don't think its a lot of complexity.

You asked for feedback, I gave you my opinion.
 

Yes its 4 declarations instead of 1 for each field. But 3 of those are
generatable by IDE same way as any Java IDE generates setters and
getters.

And this is a failure on its face.  Anything that requires an IDE to generate code represents a failure.  It represents failure because the code is (1) much less maintainable, (2) depends on IDE vendors to support it, and (3) is harder for non-IDE users (me included... emacs is my tool for a lot of my Lift-based development).
 
All the rest is spring-style instantiations/injections. And
usage is even simplier and readable (i mean syntax
record.field=value). The field list method can be implemented via
reflection to reduce amount of code required from users.

Calculating what fields are to be included in the List is what originally led to using object.
 

Anyway thanks for your feedback, David, i really appreciate it. I'll
try to see if i can integrate my model with Lift and implement it for
Google Datastore. Then i'll report back. I understand that i still
need to do more work before any serious conclusions can be made.

While I appreciate your effort, it is is not in line with the goals of Lift.  The default is not to keep the meaning with the bytes (having _name implies to me "don't use this, use name instead").  The complexity is significant... so significant that it requires IDE support to use effectively.  The problem that it addresses is the "overriding definitions of fields in subclasses" issue which is important, but in this case the cure is much, much worse than disease.

Thanks,

David
 


Oleg G.

unread,
Oct 12, 2009, 2:41:09 PM10/12/09
to Lift
On 13 окт, 01:00, Naftoli Gugenheim <naftoli...@gmail.com> wrote:
> So Model represents the database connection?
I tried to keep my thoughts at quite high abstraction level for the
sake of flexibility. And i don't have rich enough experience in
implementing object-database mapping to give you good full answer -
also that's what i meant when i said 'i still need to do more work
before any serious conclusions can be made' in reply to David.

But... I thought of a following possible usage: a Model implementation
gets instantiated and reads existing database tables structure, it
also receives a set of 'register' events from Record prototypes. Then
it can decide if the database structure is consistent with registered
Records and maybe even alter it. The code might look like:

val m = new DatabaseModel(params).validate

Lift looks very good for quick start but i'm not sure about its
strength in evolving/changing projects and overall code reusability.
That's why i asked about extensibility/customizability and that's what
i'm trying to express with my code.

Oleg G.

unread,
Oct 12, 2009, 3:08:03 PM10/12/09
to Lift
Thanks again for your answers, David. I've got your point and it all
looks much more clear to me now.
> Beginning Scalahttp://www.apress.com/book/view/1430219890

Naftoli Gugenheim

unread,
Oct 12, 2009, 3:09:57 PM10/12/09
to lif...@googlegroups.com
  1. Personally I would not take the approach of creating a totally new ORM with a completely new paradigm just to eliminate the object syntax.
The first step, as with anything in life, is to define our goals. What exactly is it about the object syntax that's problematic? Is overridability a problem? Is reflection evil? Let's make a list of our objectives over here.
In any case, I would take the following approach.
Currently, the way Mapper works is as follows. Every Mapper instance delegates database actions to its MetaMapper instance, which, upon initialization, builds the information about its fields using reflection, including the list of fields and their names.
Three reasons for the object approach were mentioned in this thread.
  1. To allow access to members. It was mentioned that this is now possible with anonymous classes (val x = new Y { ... } ). What was not mentioned but I read elsewhere IIRC is that the latter is implemented by scalac via reflection. Not that that's the end of the world, especially as we're relying on reflection as it is. Also, at least the argument remains--allow one to choose whichever syntax one desires. On the other hand, if Mapper would not use reflection, objects, being lazy, would not be known about until initialized, thus requiring the user to manually reference them.
  2. To disambiguate members that reference or return other fields. However, this argument assumes that the list of fields is built via reflection; maybe it should not be.
  3. To allow the field names to become available via reflection. I don't there's any way around this other than a compiler plugin (or maybe scala.reflect.Code?) So my suggestion would require passing the field name to a constructor. On the other hand, as I mentioned in a previous post and will explain better below, passing "this" to the constructor can be eliminated.
So the first reason basically says, "since people may need to use 'object,' and objects are lazy and we won't necessarily know about them, we had better use reflection." On the other hand, if we use vals most of the time, the constructor itself can inform the MetaMapper of its existence, and if someone wants to use 'object' or 'lazy val' then they will be required to initialize it before using it with Mapper.
The second reason is eliminated if we don't use reflection.
The third reason remains valid but the question is how important it is, at the expense of preventing subclasses from overriding fields.

If it is acceptable to eliminate the "this" parameter and instead require a field name parameter, then it would seem possible to use a  val based system instead of an object + reflection system.

Here is how it would work.
class Field(name: String)(implicit meta: MetaModel) {
  meta.register(this)
}
class Model {
  implicit def meta = getSingleton
}
====
class MyModel extends Model {
  val field1 = new Field("firstname")  // meta gets passed automatically
  val fieldRef = field1 // doesn't cause duplication because fields are registered on instantiation
}
val fieldX = new Field("lastname") // compiler error because no implicit MetaModel in scope
val fieldY = new Field("xxx")(MyModelMeta) // works, but obviously a bad idea



Two points.
  1. The concept of the meta being passed implicitly could probably be implemented into the current system. The only thing it has to do with this discussion is that if you would have to pass 'this' and the field name it would be really verbose, so I'm just pointing out that we could have a non-reflection-based system with about the same verbosity that we use now, although we could make the current system less verbose too.
  2. On the surface it would seem impossible to implement this in Mapper, because by not using reflection there's no way to get all the object members since objects are lazy. However since the reflection system only picks up objects and not vals, one could solve this as follows. As long as the same field cannot be registered twice, i.e., registration of a field skips it if it's already registered, there's no problem with allowing both methods to exist side by side. Let MappedField or some class or trait high in the hierarchy call a register method on the MetaMapper upon its initialization. This will register all val fields, as well as any object fields initialized early. Then, when the MetaMapper goes through its reflection process, any previously register fields will be skipped.
However a basic problem with my approach may be the following. Say we have instances a and b of model C, if a field is a val, not a new class, what prevents that field from being registered twice? In other words, how can fields in multiple instances of a given model be recognized as being the same field? One answer, though maybe not the best, is through the field's field name.


2009/10/12 Naftoli Gugenheim <nafto...@gmail.com>

Oleg G.

unread,
Oct 12, 2009, 3:34:38 PM10/12/09
to Lift
Very interesting reading, Naftoli! Thanks alot for such extensive
explainations. I'm going to read your message again and again until i
understand it all :)

On 13 окт, 02:09, Naftoli Gugenheim <naftoli...@gmail.com> wrote:
>    1. Personally I would not take the approach of creating a totally new ORM
>    with a completely new paradigm just to eliminate the object syntax.
>
> The first step, as with anything in life, is to define our goals. What
> exactly is it about the object syntax that's problematic? Is overridability
> a problem? Is reflection evil? Let's make a list of our objectives over
> here.
> In any case, I would take the following approach.
> Currently, the way Mapper works is as follows. Every Mapper instance
> delegates database actions to its MetaMapper instance, which, upon
> initialization, builds the information about its fields using reflection,
> including the list of fields and their names.
> Three reasons for the object approach were mentioned in this thread.
>
>    1. To allow access to members. It was mentioned that this is now possible
>    with anonymous classes (val x = new Y { ... } ). What was not mentioned but
>    I read elsewhere IIRC is that the latter is implemented by scalac via
>    reflection. Not that that's the end of the world, especially as we're
>    relying on reflection as it is. Also, at least the argument remains--allow
>    one to choose whichever syntax one desires. On the other hand, if Mapper
>    would not use reflection, objects, being lazy, would not be known about
>    until initialized, thus requiring the user to manually reference them.
>    2. To disambiguate members that reference or return other fields.
>    However, this argument assumes that the list of fields is built via
>    reflection; maybe it should not be.
>    3. To allow the field names to become available via reflection. I don't
>    1. The concept of the meta being passed implicitly could probably be
>    implemented into the current system. The only thing it has to do with this
>    discussion is that if you would have to pass 'this' and the field name it
>    would be really verbose, so I'm just pointing out that we could have a
>    non-reflection-based system with about the same verbosity that we use now,
>    although we could make the current system less verbose too.
>    2. On the surface it would seem impossible to implement this in Mapper,
>    because by not using reflection there's no way to get all the object members
>    since objects are lazy. However since the reflection system only picks up
>    objects and not vals, one could solve this as follows. As long as the same
>    field cannot be registered twice, i.e., registration of a field skips it if
>    it's already registered, there's no problem with allowing both methods to
>    exist side by side. Let MappedField or some class or trait high in the
>    hierarchy call a register method on the MetaMapper upon its initialization.
>    This will register all val fields, as well as any object fields initialized
>    early. Then, when the MetaMapper goes through its reflection process, any
>    previously register fields will be skipped.
>
> However a basic problem with my approach may be the following. Say we have
> instances a and b of model C, if a field is a val, not a new class, what
> prevents that field from being registered twice? In other words, how can
> fields in multiple instances of a given model be recognized as being the
> same field? One answer, though maybe not the best, is through the field's
> field name.
>
> 2009/10/12 Naftoli Gugenheim <naftoli...@gmail.com>

David Pollak

unread,
Oct 12, 2009, 11:40:17 PM10/12/09
to lif...@googlegroups.com
I've got a change on review board that will pick up vals and lazy vals that are MappedFields.  See http://reviewboard.liftweb.net/r/40/

However, until certain defects in the Scala compiler (https://lampsvn.epfl.ch/trac/scala/ticket/2463 and https://lampsvn.epfl.ch/trac/scala/ticket/1006) are fixed, these features are dangerous and should not be used.

2009/10/12 Oleg G. <ojo...@gmail.com>

Oleg G.

unread,
Oct 13, 2009, 1:15:43 AM10/13/09
to Lift
After reading Naftoli's post i checked the way inner object laziness
is implemented. And it appears to be not thread safe.
Following code:

class A { val a = 1 }
class B { object b extends A }

Generates getter for b as follows:

public final B$b$ b();
Code:
0: aload_0
1: getfield #18; //Field b$module:LB$b$;
4: ifnonnull 19
7: aload_0
8: new #20; //class B$b$
11: dup
12: aload_0
13: invokespecial #23; //Method B$b$."<init>":(LB;)V
16: putfield #18; //Field b$module:LB$b$;
19: aload_0
20: getfield #18; //Field b$module:LB$b$;
23: areturn

Looks like its not synchronized? I also looked at lazy vals generated
code (when i was thinking on val based Records) and dropped the idea
because of synchronization for every single access to a field.

Oleg G.

unread,
Oct 13, 2009, 6:54:58 AM10/13/09
to Lift
to Naftoli: i added implicits as you said and removed all 'onRegister'
stuff, i think its useless..
Here's the result: http://github.com/ojow/Random-code/blob/master/RecordRelatedStuff.scala

Oleg G.

unread,
Oct 13, 2009, 6:58:05 AM10/13/09
to Lift
edit: not all 'onRegister' stuff but most of it, only left two points
of extension for intercepting the prototype creation.

Oleg G.

unread,
Oct 13, 2009, 9:47:53 AM10/13/09
to Lift
On 13 окт, 10:40, David Pollak <feeder.of.the.be...@gmail.com> wrote:
> I've got a change on review board that will pick up vals and lazy vals that
> are MappedFields.  Seehttp://reviewboard.liftweb.net/r/40/
> However, until certain defects in the Scala compiler (https://lampsvn.epfl.ch/trac/scala/ticket/2463andhttps://lampsvn.epfl.ch/trac/scala/ticket/1006) are fixed, these features
> are dangerous and should not be used.
That's cool! Thanks.

David Pollak

unread,
Oct 13, 2009, 10:55:58 AM10/13/09
to lif...@googlegroups.com
On Mon, Oct 12, 2009 at 10:15 PM, Oleg G. <ojo...@gmail.com> wrote:

After reading Naftoli's post i checked the way inner object laziness
is implemented. And it appears to be not thread safe.

It is not threadsafe.  This is a known defect and is supposed to be addressed in 2.8.  But, the cost of a failure is dual initialization of the field.
 
Following code:

class A { val a = 1 }
class B { object b extends A }

Generates getter for b as follows:

public final B$b$ b();
 Code:
  0:   aload_0
  1:   getfield        #18; //Field b$module:LB$b$;
  4:   ifnonnull       19
  7:   aload_0
  8:   new     #20; //class B$b$
  11:  dup
  12:  aload_0
  13:  invokespecial   #23; //Method B$b$."<init>":(LB;)V
  16:  putfield        #18; //Field b$module:LB$b$;
  19:  aload_0
  20:  getfield        #18; //Field b$module:LB$b$;
  23:  areturn

Looks like its not synchronized? I also looked at lazy vals generated
code (when i was thinking on val based Records) and dropped the idea
because of synchronization for every single access to a field.

That's not the case.  There's only a synchronization if the initial non-synchronized test fails.
 

Oleg G.

unread,
Oct 13, 2009, 11:22:26 AM10/13/09
to Lift
Yes, i rechecked it and found it's a double checked locking. So lazy
vals is best choice, they are also better for overriding than normal
vals, because normal vals call super initialization.

So it looks like initial problem is now solved and the thread reached
its happy end.
And i want to sincerelly thank all the participants and especially
David for implementing the fix.
> Beginning Scalahttp://www.apress.com/book/view/1430219890
Reply all
Reply to author
Forward
0 new messages