[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Re: Managing metadata about attribute types

Austin Ziegler

11/5/2003 7:14:00 AM

On Wed, 5 Nov 2003 13:27:05 +0900, Simon Kitching wrote:
> On Wed, 2003-11-05 at 17:09, Austin Ziegler wrote:
> One of the goals of xmldigester is to be able to instantiate and
> initialise objects from some input xml without making any changes
> to the classes themselves.

> Thus if you already have a library of warehouse management
> classes, I can write some rules that can take an xml description
> of the contents of that warehouse and build appropriately
> configured objects without changing that library. And once the
> parsing is complete, the resulting tree of objects should look no
> different than one created using normal calls to the library API.

You're right, they shouldn't. But if your warehouse management
classes don't do what they can to ensure their data integrity, then
there's a problem with the classes -- not with the XML library. I'm
not trying to be difficult here; just pointing out that I think
you're trying to fix the problem from the wrong end.

> In addition, the approach you suggest is quite labour-intensive;
> for every attribute, a "wrapper" method needs to be written.

Not really. See below for one option. The way that I've implemented
this makes it easy to drop into place.

> I feel Ryan's MetaTags approach is easier to use; a simple string
> format can be used to document the types to which strings from the
> xml input should be converted before assignment to various
> attributes.

Ryan's MetaTags really does not do anything different than I'm
talking about except that it could can it (although I think
StrongTyping does that better). See, you can make a simple
metamethod that wraps this for you and it becomes a relatively
simple change to make it "simple." If you don't want to create a
method for each, define a proc and use the following extension to
attr_accessor. (Does anyone else think that this is a good idea? I
do. I'd love to see it become part of "standard" Ruby.)

class Module
alias_method :__attr_accessor, :attr_accessor

def attr_accessor(block, *symbols)
if block.kind_of?(Symbol)
symbols.unshift(block)
__attr_accessor(*symbols)
else
symbols.each do |get|
var = "@#{get}"
set = "#{get}=".intern
self.class_eval do
define_method(get) { self.instance_variable_get(var) }
define_method(set) { |*val|
self.instance_variable_set(var, block.call(*val))
}
end
end
end
end
end

class Foo
attr_accessor :item
attr_accessor proc { |x| x.to_i }, :item_id
attr_accessor proc { |x| x.to_f }, :cost
end

f = Foo.new
f.item = "item"
f.item_id = "37352"
f.cost = "12.50"
p f.inspect

(I've actually attached a further enhanced and unit-tested version
of this extension to this message. If anyone wants to add to the
test cases, such as for array parameters, feel free.)

On Wed, 5 Nov 2003 14:06:55 +0900, Simon Kitching wrote:
>> As you can see, any code that that relies on this kind of "type
>> checking" in Ruby is naive. Worse than that, the desire to
>> shoe-horn Ruby into Java-like "strictness" can blind the user
>> into missing the point, and therefore the full benefit, of what
>> Ruby has to offer.
> Yep. I can't see how to offer what I want without some type info,
> though.
>
> If you look at my original example, how to I know that
> item.cost = '3.50'
> is wrong (the instance will eventually trigger some error), and
> item.cost = '3.50'.to_f
> is the right thing to do?
>
> With Java it just happens magically and "does the right thing"
> (except in the abstract type case mentioned above). Surely Ruby
> can't be inferior :-).

It isn't inferior, and you don't need type info. Remember -- an
object should validate or transform its own data. Using the method I
described above, it becomes "cheap" to fix the problem as I see it
without imposing a requirement for the use of type strictness that
IMO, is a really bad idea for a Ruby library.

> Yep. Tedious indeed. And as mentioned in my reply to Austin, it is a goal
> to instantiate and initialise objects without requiring any changes to
> their code. Digester manages this fine.

Digest manages this because the objects in Java automatically take
care of their own type. Sort of. The compiler prevents you from
using any type except those that are signaled. If you have not
massaged your own type attributes to make sure that they are
receiving valid data, then there's something wrong with the classes
themselves. NOT with Ruby for not providing type strictness.

In several libraries I've written, I have either rejected types that
I can't handle, or I have transformed types into what I can handle,
or I have operated on the data and thrown an error because it can't
be handled. I prefer being proactive, so I choose the former two
methods most often.

> The problem is : in order to automatically generate the filters, I need
> to know what type of object is required for each attribute. Full circle!

Again, not really. The class you're building has to know what it
expects. It's an inverse of what you'd expect from a statically
typed language, but I have found it easier to understand in the long
run.

> That code in the Person class you show above could only be generated
> because a human "knew" that Integer was an appropriate type to store into
> the age attribute (and String was not). I just want to represent that
> info in the code somehow...

But defining the Person class to convert its "age" parameter into an
Integer does exactly that. Not as meta-data, to be sure, but in the
only way that matters, IMO.

-austin
--
austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.11.05
* 02.12.59

3 Answers

Ryan Pavlik

11/5/2003 9:08:00 AM

0

On Wed, 5 Nov 2003 16:13:37 +0900
Austin Ziegler <austin@halostatue.ca> wrote:

<big snip>
> It isn't inferior, and you don't need type info. Remember -- an
> object should validate or transform its own data.
<big snip>

I'm trying to stay out of this because I mostly disagree. This
however warrants addressing.

This is demonstrably wrong. An object cannot validate and transform
its own data in this context in any reasonably general manner. It's
simple when you're addressing a few basic types... String, Float,
Integer, Hash and Array.

This isn't general, though. What if I want a Foo, and you give me a
Bar? Foo was from one module (which shouldn't know about Bar), and
Bar was from another module (which shouldn't know about Foo). There
is there no #to_foo (which may be fortunate depending on your
culinary preferences). This leaves us with only a few options:

* We just don't allow it. This does no good for us.

* We convert through an intermediary type. This is inefficient
and may lose data or not work, either.

* We decide the approach is wrong and do something else.

Using #to_* methods are the ruby equivalent of type casting. The
point in this case is not to _convert_ types, it's to provide the
right type in the first place. Instead of giving the attribute a
string and expecting it to be parsed, we want to create the desired
type and hand that off.

It has nothing to do with the #attr= function. Strict type checking
at that point is merely a convenience. It's all about getting the
input into a useful format without writing n^2 functions (for n
classes). This is the primary reason I wrote StrongTyping in fact;
the strict checking has merely helped with debugging a whole lot.

--
Ryan Pavlik <rpav@mephle.com>

"Do not question wizards, for they are quick to
turn you into a toad." - 8BT

Richard Kilmer

11/5/2003 2:53:00 PM

0


On Nov 5, 2003, at 4:08 AM, Ryan Pavlik wrote:

> On Wed, 5 Nov 2003 16:13:37 +0900
> Austin Ziegler <austin@halostatue.ca> wrote:
>
> <big snip>
>> It isn't inferior, and you don't need type info. Remember -- an
>> object should validate or transform its own data.
> <big snip>
>
> I'm trying to stay out of this because I mostly disagree. This
> however warrants addressing.

I as well have tried to stay out of this but...

>
> This is demonstrably wrong. An object cannot validate and transform
> its own data in this context in any reasonably general manner. It's
> simple when you're addressing a few basic types... String, Float,
> Integer, Hash and Array.
>
> This isn't general, though. What if I want a Foo, and you give me a

What is fascinating is that the original request is not seeking a
general
manner. Everything that comes from XML is a String...period, so to
transform
a String into something you expect (if possible) can be done.

> Bar? Foo was from one module (which shouldn't know about Bar), and
> Bar was from another module (which shouldn't know about Foo). There

Again, this is a strawman. Simon's requirement is to deal with Strings
being parsed into valid attributes of objects. Austin's 'proc based'
approach is a nice way to do this. Its funny, because we do not have a
problem as developers (for debugging purposes, etc) having a to_s
method on our classes...if we also had a 'from_s' class method on all of
our classes we could round-trip XML quite nicely.

> is there no #to_foo (which may be fortunate depending on your
> culinary preferences). This leaves us with only a few options:
>
> * We just don't allow it. This does no good for us.
>
> * We convert through an intermediary type. This is inefficient
> and may lose data or not work, either.
>
> * We decide the approach is wrong and do something else.
>
> Using #to_* methods are the ruby equivalent of type casting. The

I disagree. to_* is not type casting, its duck typing. You actually
care less about the type of thing you are dealing with and more the
behavior of the thing you are given. In this instance, to_i is the
behavior you want and you expect it to return an Integer. The "type"
of thing you are given is not relevant, but its ability to understand
the message (to_i) is. After coming from Java I found myself doing
lots of "kind_of?" checking on parameters, now I do many more
"respond_to?" if I care to do checks in my class to return a specific
error message.

> point in this case is not to _convert_ types, it's to provide the
> right type in the first place. Instead of giving the attribute a

This goes beyond just setting attributes. Many methods need parameters
that are not simply attr_accessor methods. Those parameters are
going to be an object. The question is whether you write your methods
to expect a certain "namespace" (Class/Module), or just simply a set
of behaviors.

I hope this makes sense.

> string and expecting it to be parsed, we want to create the desired
> type and hand that off.
>
> It has nothing to do with the #attr= function. Strict type checking
> at that point is merely a convenience. It's all about getting the
> input into a useful format without writing n^2 functions (for n
> classes). This is the primary reason I wrote StrongTyping in fact;
> the strict checking has merely helped with debugging a whole lot.
>
> --
> Ryan Pavlik <rpav@mephle.com>
>
> "Do not question wizards, for they are quick to
> turn you into a toad." - 8BT
>
>


Ryan Pavlik

11/5/2003 6:33:00 PM

0

On Wed, 5 Nov 2003 23:52:49 +0900
Richard Kilmer <rich@infoether.com> wrote:

> On Nov 5, 2003, at 4:08 AM, Ryan Pavlik wrote:
>
<snip>
> > This isn't general, though. What if I want a Foo, and you give me a
>
> What is fascinating is that the original request is not seeking a
> general manner. Everything that comes from XML is a String...period,
> so to transform a String into something you expect (if possible) can
> be done.

Right, but each string in XML is representative of a general desired
type. This seems to trivialize the problem, but it doesn't---it's
still the same problem; you just expect String to have a conversion
type for every known class.

> > Bar? Foo was from one module (which shouldn't know about Bar), and
> > Bar was from another module (which shouldn't know about Foo). There
>
> Again, this is a strawman. Simon's requirement is to deal with Strings
> being parsed into valid attributes of objects. Austin's 'proc based'
> approach is a nice way to do this. Its funny, because we do not have a
> problem as developers (for debugging purposes, etc) having a to_s
> method on our classes...if we also had a 'from_s' class method on all of
> our classes we could round-trip XML quite nicely.

As above, this isn't really the case. You could write a proc for
every attribute, every time you need it, but this is rather ugly.
You could reuse procs to avoid some code duplication. I did propose a
similar type conversion system awhile back that would allow this, but
it never seemed to take off.

Also, in this case, (and unlike the independent conversion mechanism),
it still requires your class to know how to convert each type into the
desired type.

Finally, since Austin is _proposing_ this as a generalized solution to
these problems, it should be considered in a general light. It
doesn't even really work for just strings.

<snip>
> > Using #to_* methods are the ruby equivalent of type casting. The
>
> I disagree. to_* is not type casting, its duck typing.

This is just a cutesy name for doing the same thing. In C++, I could
write things the same way, explicitly doing a cast to my desired type
everytime I want it.

> You actually care less about the type of thing you are dealing with
> and more the behavior of the thing you are given. In this instance,
> to_i is the behavior you want and you expect it to return an
> Integer. The "type" of thing you are given is not relevant, but its
> ability to understand the message (to_i) is.

Actually, I would argue that this is _not_ duck typing. This is
explicitly asking for a type: string, float, etc. Duck typing is
treating the type as duck you want, without explicitly referring to
type.

But calling #to_* methods is explicitly converting ("casting") one
type to another. It's just less formal and less general (since in C++
you can at least define operator-Type for explicit conversions).

> After coming from Java I found myself doing lots of "kind_of?"
> checking on parameters, now I do many more "respond_to?" if I care
> to do checks in my class to return a specific error message.

Well, in my case, #respond_to? is insufficient information. (My other
argument against it is that it doesn't tell me semantically what the
method does, if it's actually going to quack, so to speak, or if it's
just a duck-billed platypus.)

> > point in this case is not to _convert_ types, it's to provide the
> > right type in the first place. Instead of giving the attribute a
>
> This goes beyond just setting attributes. Many methods need parameters
> that are not simply attr_accessor methods. Those parameters are
> going to be an object. The question is whether you write your methods
> to expect a certain "namespace" (Class/Module), or just simply a set
> of behaviors.
>
> I hope this makes sense.

It makes sense. However, the way I program, the Class _is_ the
definition of a behavioral set. This is highly important, and results
in a lot of benefits.

As above, checking #respond_to? doesn't actually guarantee that your
method set is actually the behavioral set you desire.

(Yes, I'm fully aware you can modify classes and break things so that
things behave differently in the same class. There is no reason to do
this. However, on the flip side, you cannot guarantee that every
given method #N has the same semantic identity across every class
without severely restricting your functionality.)

--
Ryan Pavlik <rpav@mephle.com>

"Do not question wizards, for they are quick to
turn you into a toad." - 8BT