Managing metadata about attribute types

Hi,

I’m porting the Apache Jakarta Commons Digester (written in Java) to
Ruby at the moment. This module processes xml in a rules-based manner.
It is particularly useful for handling complex xml configuration files.

However some of the very nice features of this module depend upon being
able to introspect a class to find what attributes it has, and what
their datatypes are.

Finding attributes on a Ruby class is simple (just look for "attr="
methods). Unfortunately, determining what object types it is valid to
assign to that attribute is not so simple…

I was wondering if there were any other Ruby projects which have faced
this problem and come up with solutions? I would rather steal a solution
than invent one :slight_smile:

Example of problem:

Input xml is:



// java
class StockItem {
public void setName(String name) {…}
public void setCost(float cost) {…}
}

Ruby

class StockItem
attr_accessor :name
attr_accessor :cost
end

In the java version, when the “cost” attribute is encountered in the xml
input, it is seen that the target class has a setCost(float) method, so
the string “12.50” is converted to a float before invoking the setCost
method.

I want to achieve the same effect in the Ruby version. I do not want
to effectively invoke this in ruby:
stock_item.cost=(‘12.50’) # string passed

Anyone have any references to “pre-existing art”???

Thanks,

Simon

Simon Kitching wrote:

Hi,

I’m porting the Apache Jakarta Commons Digester (written in Java) to
Ruby at the moment. This module processes xml in a rules-based manner.
It is particularly useful for handling complex xml configuration files.

Are you familiar with the XMLDigester project listed in the RAA?

http://raa.ruby-lang.org/list.rhtml?name=xmldigester
and
http://www.helenius.dk/ruby/digester/

James Britt

Hi,

I was wondering if there were any other Ruby projects which have faced
this problem and come up with solutions? I would rather steal a solution
than invent one :slight_smile:

Yep. I’ll take this opportunity to shamelessly plug some modules. I
do this in Mephle. First, I use the StrongTyping module and write new
attr_ functions, so I can do this:

attr_accessor_typed String, :foo, :bar

Now #foo= and #bar= complain if they don’t get a String. Even better,
I can use StrongTyping’s type querying on foo= and bar= to get what
they take, if I need to.

Next, I use the MetaTags module for actually tagging what attributes
exist:

class_info <<-DOC
!Class: Foo

!attr foo: Foo
!attr bar: Bar: This is an optional description of bar.

DOC
class Foo
:
end

Now I can ask for information about the Foo class and look through the
attributes that way.

It works… I generate UIs from this information… and I’m working on
some tools to eliminate redundancy and required typing.

hth,

···

On Wed, 5 Nov 2003 09:38:16 +0900 Simon Kitching simon@ecnetwork.co.nz wrote:


Ryan Pavlik rpav@mephle.com

“Mmm! Power lines and paint chips! My childhood rocks!” - 8BT

Hi,

···

On Wed, 5 Nov 2003, Simon Kitching wrote:

I’m porting the Apache Jakarta Commons Digester (written in Java) to

Ruby at the moment. This module processes xml in a rules-based manner.

It is particularly useful for handling complex xml configuration files.

However some of the very nice features of this module depend upon being

able to introspect a class to find what attributes it has, and what

their datatypes are.

Finding attributes on a Ruby class is simple (just look for “attr=”

methods). Unfortunately, determining what object types it is valid to

assign to that attribute is not so simple…

I’m not exactly answering your question here, but I’d like to offer some
words of caution…

Be careful about relying on what “type” of object you’ve got (I’ll
avoiding going into detailed discussion of the fact that “type” and
"class" are not equivalent). An object’s class doesn’t guarantee much of
anything in Ruby. Javaisms may not apply:

class String
undef :upcase, :downcase, :chomp, :chop, :capitalize
end

naive_method(“is this really a string?”)

or even better:

class A
end
a = A.new
def a.is_a?(x)
true
end

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.

As I said, I haven’t actually addressed your problem here. If it were me,
I would be looking for (or writing) something that generates code based on
an XML Schema. What you’re really looking for is convenience (as opposed
to “type safety”). You want to be able to add 1 + 1 and not end up with 11.
You could easily accomplish this via some tedious coding or you could
generate the tedious code. By tedious, I mean things like this:

class Person
def age=(how_old)
@age = Integer(how_old)
end
end

Ruby being as dynamic as it is, it would be pretty easy to dream up a
scheme to wrap existing classes with filters that could do this kind of
conversion for you. And, of course, you could generate the filters.

And now, the cold medication sets in…

Good night,
Chad

I’m porting the Apache Jakarta Commons Digester (written in Java)
to Ruby at the moment. This module processes xml in a rules-based
manner. It is particularly useful for handling complex xml
configuration files.

You may want to note xml-configfile in addition to XMLDigester that
James Britt mentioned.
http://raa.ruby-lang.org/list.rhtml?name=xml-configfile

Finding attributes on a Ruby class is simple (just look for
“attr=” methods). Unfortunately, determining what object types it
is valid to assign to that attribute is not so simple…

Aside from Ryan Pavlik’s StrongTyping module, I’m not sure that this
is absolutely necessary. See below for a bit more information.

I was wondering if there were any other Ruby projects which have
faced this problem and come up with solutions? I would rather
steal a solution than invent one :slight_smile:

I frankly don’t see a reason to worry about this.

Example of problem:

Input xml is:



// java
class StockItem {
public void setName(String name) {…}
public void setCost(float cost) {…}
}

Ruby

class StockItem
attr_accessor :name
attr_accessor :cost
end

Why not do:

class StockItem
attr_accessor :name # Defaults to String
attr_reader :cost # Returns cost
def cost=(x)
@cost = x.to_f
end

This way, you don’t have to care what the appropriate type is –
your type worries about it.

-austin

···

On Wed, 5 Nov 2003 09:38:16 +0900, Simon Kitching wrote:

austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.11.04
* 22.46.52

What a vigorous discussion I seem to have triggered :slight_smile:

It’s really nice to see so many people interested in the topic - thanks
to all who replied.

Rather than reply individually to the several emails which raise
interesting points, I’ll try to gather all the different bits here.

I really am interested in the points raised, and am definitely still in
the learning phase with Ruby. So all of the statements below should
really be prefaced with “I think”, “It seems to me”, “Do you think that
… is right?”. However that would double the size of this email. Please
assume all below is tentative, and that comments/corrections are
welcome.

And I hope those people who “really wanted to stay out…” don’t stay
out and chip in. I’m interested at the very least!

···

====
Re “xml-config” module, raised by Austin: there are significant
differences between the “xml-config” and “xmldigester” approaches.

Which is “better” will depend on circumstances and developer taste. The
most significant differences are:
(a)
Xml-config first builds a complete representation of the input xml in
memory, then starts extracting data. For large xml files this is not a
good idea. Xmldigester is “event-based”, so the input xml does not have
to be completely loaded into memory.
(b)
I believe that the xmldigester rules-based approach will take less
client code, and will bind the “parsing” program less tightly to the API
of the objects being built than the xml-config approach.
(c)
If building inter-object relationships that are more complex than simple
parent/child references, the xml-config approach may prove easier. There
are some tricks that can be played with xmldigester (eg a common
“registry” object used to resolve relations), but having the entire xml
document available (DOM-style) can allow things that an event-style
approach cannot.
(d)
The xmldigester event-based approach is likely to be faster.

Of course the best test of all the above opinions is actually to create
the code, then compare the two. Once I have xmldigester knocked into
reasonable shape, I might port the xmldigester examples to xml-config
(and vice-versa) to see if any of the above is true!

Regardless of the results, I think that both approaches have their
place.

====

Regarding whether the target class should be responsible for accepting a
string and doing the conversion…

I think it is definitely not the receiving classes’ responsibility to
do the conversion.

Here’s my original class, with the initial implicit assumptions spelled
out more clearly as comments.

Ruby

class StockItem

contract with user: any value assigned to name must

act like a string

attr_accessor :name

contract with user: any value assigned to cost must act

like a float object.

attr_accessor :cost
end

Isn’t this a valid API for a class to provide? As far as the author of
StockItem is concerned, cost is a float.

I don’t see why the author of StockItem should even consider the
possibility that a string could be assigned to cost; that would violate
the contract of the class, so any program that does so can damn well
take the consequences :-). The StrictTyping module can enforce this, but
perhaps does so over-eagerly, as it doesn’t allow “duck-typing” ie
objects which aren’t Float objects but do behave like them.

Now I happen to want to configure this object based on some string-typed
information. But that’s my problem, not the problem of the author of the
StockItem class. And if I wanted to use ASN.1 format input to initialise
an instance of StockItem, then it is still my responsibility to convert
the ASN.1 representation to an appropriate type before assigning to
cost, not the StockItem class’ responsibility to understand ASN.1 format
input.

Ok, with Ruby’s “open” classes, I can alias the original cost= method
and insert some wrapper code. But I will have to restore the original
method after parsing is complete, otherwise during the real “running”
part of the program, the StockItem’s cost= method won’t behave like
other classes expect it to.

Not to mention that writing those “conversion” methods by hand is ugly.

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.

The StockItem’s contract clearly states that it only accepts Float types
for the cost attribute. It doesn’t actually need to enforce its data
integrity - it is the calling code’s responsibility to use StockItem
correctly.

attr_accessor proc { |x| x.to_i }, :item_id

That’s some very cool code. I can feel my brain expanding just by
looking at it! However I don’t feel it does what I want, because this
code actually changes the API of the target class, breaking all other
code that accesses that same attribute thereafter.

The data conversion clearly has to be done somewhere, but I would like
it to be done separately from the target class so as not to muck around
with its API.

Here’s the “conversion” code extracted out into a helper class:

def StockItemHelper
def StockItemHelper.cost=(stock_item, str)
stock_item.cost = str.to_f
end
end

In fact, why not use the Java convention and call it StockItemBeanInfo?

Applying a modified version of your attr_accessor code, this could be
written more succinctly as the following, generating effectively the
same code as shown above:

def StockItemBeanInfo
attr_from_str :name, String
attr_from_str :cost, Float
end

However I can also use something like Ryan’s MetaTag syntax to write
this. I’m not sure which syntax is more convenient.

desc = <<-END
!class StockItem
!name String
!cost Float
END

parse the string and dynamically create a wrapper class

beanInfo = createBeanInfoClass(desc)

because cost was declared as a Float in the MetaTag string,

the beanInfo class knows to convert the second (string) param

to a float.

beanInfo.set_cost(stock_item, ‘3.50’)

As you can see, I’m not interested in “type strictness” at all.
What I need is simply “what type of object should I generate in order to
be able to validly assign to cost without violating the API contract of
the StockItem class”…

Changing the StockItem class contract is one solution, but that screws
up all other code that really depended on the original contract being
valid.

Oh, and what if the target attribute is a “Date” class, and I want to
globally control the way string->date mapping works? If it is
distributed across every class that has a Date attribute that is much
trickier to handle than if I somehow know that classes X, Y and Z have
date attributes and the xmldigester code does the string->date
conversions before the assignment.

====

From Ryan:

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.

Yep, that’s exactly how I see it.

However I don’t want the “type enforcement at runtime” feature of
StrongTyping, and I want to avoid changing the target class’ behaviour
in any significant way. Is it possible to get the “type info” part of
StrongTyping without the “type enforcement”?

====

The thread about namespaces still has me pondering a little.
I’m not sure it’s relevant to my issue, though, is it?

I need to instantiate an object in order to assign it to an attribute
on a target object. So I do need to know the name of a concrete class to
instantiate. There’s no “duck typing” there, is there?

====

Thanks Ryan, Chad, Austin, Richard, James, David, Christoph (phew!)

As said in the intro, all comments/corrections welcome!

Regards,

Simon

Hi Austin,

Thanks for your reply.

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.

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

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.

Regards,

Simon

···

On Wed, 2003-11-05 at 17:09, Austin Ziegler wrote:

class StockItem
attr_accessor :name # Defaults to String
attr_reader :cost # Returns cost
def cost=(x)
@cost = x.to_f
end

Hi,

I’m porting the Apache Jakarta Commons Digester (written in Java) to

Ruby at the moment. This module processes xml in a rules-based manner.

It is particularly useful for handling complex xml configuration files.

However some of the very nice features of this module depend upon being

able to introspect a class to find what attributes it has, and what

their datatypes are.

Finding attributes on a Ruby class is simple (just look for “attr=”

methods). Unfortunately, determining what object types it is valid to

assign to that attribute is not so simple…

I’m not exactly answering your question here, but I’d like to offer some
words of caution…

Be careful about relying on what “type” of object you’ve got (I’ll
avoiding going into detailed discussion of the fact that “type” and
“class” are not equivalent). An object’s class doesn’t guarantee much of
anything in Ruby. Javaisms may not apply:

Yes. I’m not strictly looking for “the type of the target attribute”. As
you state, the question “what type is the attribute” is something that
is usually not worth asking in Ruby.

What I’m really looking for is “what type should I convert the string
extracted from the xml to before assignment to the attribute”. Or, in
other words I want to know the name of one type that can validly be
assigned to the attribute, so I can generate an instance of that type
from the input xml string value.

This is slightly different from “what is the type of the attribute”, but
close enough that Ryan’s MetaTags “type annotation” approach will
probably work. It can be regarded as a way of giving “type hints”. The
StrictTyping module is not, I agree, what I want.

In fact, the original Digester library has a similar problem when the
declared type of an attribute is an abstract type. The Digester library
essentially says “well, in that case you can’t use the nice convenient
SetPropertiesRule api. Use the more explicit (and long-winded)
CallMethodRule api instead”. Or you can create a BeanInfo class.
Unfortunately Java apps face this “lack of target type info” only
occasionally, while for Ruby it exists for every attribute…

Whatever syntax is used to indicate “what type should the input be
converted to” needs to be clean and concise because it will be used
quite a lot to tell xmldigester about how to map xml to
object-attributes.

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 :-).

As I said, I haven’t actually addressed your problem here. If it were me,
I would be looking for (or writing) something that generates code based on
an XML Schema.

One of the xmldigester goals is not to require an XML Schema.

There are “code generation” approaches that do this. In many
circumstances they are a good solution

However in many cases they are also a bad solution. The original
Digester fills this ecological niche nicely in Java. I hope xmldigester
will fill the same niche in Ruby. But that depends upon finding a
reasonable solution to this type-of-attribute problem that leaves the
library reasonably easy to use.

And I’m not sure that the XML Schema approach works that well either.
Yes, it can document the “type” of the data “in” the xml. But does that
always match with the datatype you want to pass to an attribute? I’ll
think about this a bit, though. However if I do go down this road, I’ll
have to change the project name as it will be quite different from the
Apache Digester :wink:

What you’re really looking for is convenience (as opposed
to “type safety”).

Yep. Absolutely.

You want to be able to add 1 + 1 and not end up with 11.

Or go
item.cost = some_value
and not have an exception thrown later when the class goes to use cost
in some arithmetic operation.

You could easily accomplish this via some tedious coding or you could
generate the tedious code. By tedious, I mean things like this:

class Person
def age=(how_old)
@age = Integer(how_old)
end
end

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.

Ruby being as dynamic as it is, it would be pretty easy to dream up a
scheme to wrap existing classes with filters that could do this kind of
conversion for you. And, of course, you could generate the filters.

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!
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…

And now, the cold medication sets in…

That may have been more information than I needed to know :slight_smile:

Good night,
Chad

Good afternoon…

Simon

···

On Wed, 2003-11-05 at 17:09, Chad Fowler wrote:

On Wed, 5 Nov 2003, Simon Kitching wrote:

Hi Ryan,

Thanks for your reply.

MetaTags (http://raa.ruby-lang.org/list.rhtml?name=metatags) may be what
I was looking for. I’ll try to figure out exactly what it does over the
next few days.

Mephle looks very interesting … might have to look into it later on.

Regards,

Simon

···

Next, I use the MetaTags module for actually tagging what attributes
exist:

class_info <<-DOC
!Class: Foo

!attr foo: Foo
!attr bar: Bar: This is an optional description of bar.

DOC
class Foo
:
end

Now I can ask for information about the Foo class and look through the
attributes that way.

It works… I generate UIs from this information… and I’m working on
some tools to eliminate redundancy and required typing.

hth,

> Regarding whether the target class should be responsible for > accepting a string and doing the conversion... I think it is > definitely *not* the receiving classes' responsibility to do the > conversion.

It’s not simply a matter of conversion from a String, as I’ll
demonstrate below.

Here’s my original class, with the initial implicit assumptions
spelled out more clearly as comments.

class StockItem
# contract with user: any value assigned to name must act
# like a string
attr_accessor :name

# contract with user: any value assigned to cost must act
# like a float object.

attr_accessor :cost
end

Let’s test that assumption.

s = StockItem.new
s.name = “Apple Pie” # An apple pie…
s.cost = 10 # Costs $10…
per_slice = s.cost / 8 # Split it eight ways…
puts per_slice # => 1

Therefore, by simply assuming that you’re getting an object that
can act like a float, you’ve introduced a huge error. Should I have
entered 10.0 as the price, or divided by 8.0? Either of those would
have guaranteed me a Float context in which type coercion will be
used to ensure a Float result. If, however, we had converted cost to
a float explicitly during assignment, this wouldn’t even be an
issue. Without talking about Strings, we’ve already run into a
problem with StockItem’s assumption of Float-ness.

Compare the same Java:

class StockItem {
String name;
float cost;

void setName(String n) { name = n; }
void setCost(float  c) { cost = c; }

String getName() { return name; }
float  getCost() { return cost; }

}

In Java, it doesn’t matter if you pass an int to setCost because the
compiler has already marked that as a float – and it will do an
implicit conversion from int to float. (IIRC, that won’t work in
Ada, which disallows implicit conversions.)

In a statically typed language, conversions like this can be made
implicit because the types themselves are explicit. The Java version
will always be dealt with as if it were a float … because it
always will be a float.

The author of the StockItem class should have considered that any
numeric value could have been assigned – and that integer math
wouldn’t be a good idea.

Isn’t this a valid API for a class to provide? As far as the author of
StockItem is concerned, cost is a float.

I disagree. If you want to treat the attribute as a float, then it’s
your responsibility to ensure that it is a float. Otherwise,
you’ll get unexpected results when someone doesn’t quite respect
the API/contract.

[…]

Not to mention that writing those “conversion” methods by hand is
ugly.

Well, they can be. That’s why I wrote the extension that I did.

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.
The StockItem’s contract clearly states that it only accepts Float
types for the cost attribute. It doesn’t actually need to enforce
its data integrity - it is the calling code’s responsibility to
use StockItem correctly.

Well, yes, the documented contract is violated … but there’s no
programmatic contract. IMO, defensive programming suggests that if
you need something to behave a particular way, you do what you can
to ensure it.

attr_accessor proc { |x| x.to_i }, :item_id
That’s some very cool code. I can feel my brain expanding just by
looking at it! However I don’t feel it does what I want, because
this code actually changes the API of the target class, breaking
all other code that accesses that same attribute thereafter.

Actually, it doesn’t change the API at all. It enforces the
documented constraints. It’s the difference between early and late
detection.

[snip bean info stuff]

I donno. That still doesn’t feel very “Ruby” to me, and I personally
find both StrongTyping and MetaTag clunky, trying to solve things
that I’m not sure are best solved that way.

As you can see, I’m not interested in “type strictness” at all.
What I need is simply “what type of object should I generate in
order to be able to validly assign to cost without violating the
API contract of the StockItem class”…

Maybe there’s a place here for an enhanced version of #coerce.

Changing the StockItem class contract is one solution, but that
screws up all other code that really depended on the original
contract being valid.

No, it doesn’t. Doing a #to_f doesn’t change the original contract.

Oh, and what if the target attribute is a “Date” class, and I want
to globally control the way string-> date mapping works? If it is
distributed across every class that has a Date attribute that is
much trickier to handle than if I somehow know that classes X, Y
and Z have date attributes and the xmldigester code does the
string-> date conversions before the assignment.

Why would you want to globally control it? The parsedate routine
(don’t quite remember where it sits) handles this.

The thread about namespaces still has me pondering a little. I’m
not sure it’s relevant to my issue, though, is it?

It’s an offshoot of StrongTyping. When you do a #kind_of? test, you
are doing something of a namespace test.

-austin

···

On Thu, 6 Nov 2003 10:16:41 +0900, Simon Kitching wrote:

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

Hi –

I don’t see why the author of StockItem should even consider the
possibility that a string could be assigned to cost; that would violate
the contract of the class, so any program that does so can damn well
take the consequences :-). The StrictTyping module can enforce this, but
perhaps does so over-eagerly, as it doesn’t allow “duck-typing” ie
objects which aren’t Float objects but do behave like them.

I believe that’s by design; as I understand it, the StrongTyping
module performs parameter gatekeeping based exclusively on the
class/module ancestry of an object (the namespaces to which it
belongs, as Rich and Chad were discussing), not on what the object
actually can do. This means, as you say, that objects which might fit
the bill may not get through, if their class/module ancestry is wrong,
and also that objects which do not fit the bill can get through – for
example:

irb(main):014:0> s = “a b c d”
=> “a b c d”
irb(main):015:0> def s.split; “Don’t split me!”; end
=> nil
irb(main):016:0> s.split
=> “Don’t split me!”
irb(main):017:0> def x(str); expect(str, String);
puts “Got a String!”; end
=> nil
irb(main):018:0> x(s)
Got a String!

This brings up the general point that checking an object’s
class/module ancestry doesn’t tell you anything about its type (its
interface or behavior or capabilities, or its similarity to other
objects on any of these criteria). This is definitely an area where
confusion has reigned; pretty much everything I’ve ever heard
described as “type checking” in Ruby is actually classname or ancestry
checking. I don’t know of any sustained examples of type checking in
Ruby (though there may be some). In fact, I’m not exactly sure what
it would be, though the thought is intriguing :slight_smile:

I think this class/type conflation persists in part because of
the historical circumstance of the existence of the #type method – a
synonym for #class – which, I believe, exists only because in earlier
days there were issues with parsing the word ‘class’ as a method name
(rather than a keyword). People got used to using ‘class’ and ‘type’
interchangeably.

(#type, I’m happy to say, is deprecated:

candle:~$ ruby -we ‘“”.type’
-e:1: warning: Object#type is deprecated; use Object#class

so maybe the distinction will be clearer in the future :slight_smile:

Florian Gross was (and maybe still is?) working on an interesting
project which sort of weighted method arguments on various criteria,
not just their module ancestry. Last time we talked about it, it was
still in the very early/experimental stages, but it was intriguing.

David

···

On Thu, 6 Nov 2003, Simon Kitching wrote:


David Alan Black
home: dblack@wobblini.net # New email address
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

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.)

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

extend.rb (2.2 KB)

···

On Wed, 5 Nov 2003 13:27:05 +0900, Simon Kitching wrote:

On Wed, 2003-11-05 at 17:09, Austin Ziegler wrote:
On Wed, 5 Nov 2003 14:06:55 +0900, Simon Kitching wrote:

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

Hi Ryan,

Thanks for your reply.

MetaTags (http://raa.ruby-lang.org/list.rhtml?name=metatags) may be what
I was looking for. I’ll try to figure out exactly what it does over the
next few days.

Hope you find it useful. You may still want to couple it with
strongtyping, as this provides a really convenient way to do what you
want (check desired types), but you can do it with metatags alone.

I’ve thought about doing this, in fact, for “documenting” builtin
classes and their methods before. You shouldn’t have a problem
modifying the existing method_info or class_info tagsets to handle
this.

Mephle looks very interesting … might have to look into it later on.

It’s another can of worms. I should have an app or two that uses it
coming out soon, though.

ttyl,

···

On Wed, 5 Nov 2003 14:16:53 +0900 Simon Kitching simon@ecnetwork.co.nz wrote:


Ryan Pavlik rpav@mephle.com

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

What I’m really looking for is “what type should I convert the string
extracted from the xml to before assignment to the attribute”. Or, in
other words I want to know the name of one type that can validly be
assigned to the attribute, so I can generate an instance of that type
from the input xml string value.

Looking at a programming language to solve this is not the right way to
go. The type of the cost attribute should come out of an XML schema,
not from introspection. What are happens with something like this:

class StockItem {
public void setCost(float cost) {…}
public void setCost(String cost) {…}
}

One of the xmldigester goals is not to require an XML Schema.

That’s because you have another source than an XML Schema to pull
type information from. In Ruby you don’t have that metadata source,
so the xmldigester is coupled with Java (and other typed languages).

I agree with Chad that an XML Schema (or similar) would be an
appropriate place to store this information.

What you’re really looking for is convenience (as opposed
to “type safety”).

Yep. Absolutely.

Well, you’ll have type safety automatically with Java because you have a
source of that metadata handy.

Your problem makes me think of type inferencing…

How do you generate this XML in the first place? Could you generate the
type metadata then?

···

Simon Kitching (simon@ecnetwork.co.nz) wrote:

On Wed, 2003-11-05 at 17:09, Chad Fowler wrote:


Eric Hodel - drbrain@segment7.net - http://segment7.net
All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

I hope you realise that the StockItem was just an example I made up out
of thin air for the purposes of the discussion - it isn’t a real class
in use anywhere. Xmldigester is like xml-config; it is a library to
configure any set of objects. The StockItem class is just one example.

Here’s a slightly more complex example, that might get away from the
triviality of the StockItem’s cost attribute example.

class Weight
def initialize(units, amount)
@units = units
@amount = amount
end

other weight-related methods here, with defined behaviours

and associated contracts…

end

class StockItem

user contract: anything assigned to this attribute must behave

like a Weight object.

attr_accessor :weight

attr_accessor :name
attr_accessor :cost # Float
end

Now as a programmer dealing with the above problem, I want to be able to
tell xmldigester than when it encounters the tag it is to
create a StockItem instance, then create a Weight instance and
initialise it appropriately from a string, then assign that initialised
object to the StockItem’s weight attribute.

The Java Digester version does this automatically, by determining that
the StockItem has a “weight” attribute of type “Weight”, and that there
is a “weight” xml attribute (with a string value). It then invokes a
table of data-conversion methods to convert the string to the target
type; built-in types are already in the table, and user-specific types
(like Weight) can be added as needed.

You’re suggesting that in the file which contains the “xml-parsing”
code, I re-open the existing StockItem class, and use some approach like
the attr_accessor modification to “wrap” the existing weight= code,
resulting in something that effectively works like:

class StockItem # reopen existing class
alias :__weight= :weight=

def weight=(param)
weight2 = param.to_weight
__weight = weight2
end
end

and Weight doesn’t have a to_weight method yet, so I’d need to reopen
that class too:

class Weight # reopen existing class
def to_weight
return self
end
end

and finally add a method to String:

class String # reopen string
def to_weight
# some code to instantiate a Weight object and
# initialise it from the String’s value
end
end

Yes, I am now able to do this, which would indeed satisfy my
requirements:

can now assign via strings

stock_item.weight = ‘0.75 kg’
and

can still use StockItem instances as per normal

stock_item.weight = Weight.new(‘g’, 750)

It seems a lot of work, though. And I’m not sure I’m too fond of adding
methods to the String class. Nor of the overhead that now exists on
every assignment to stock_item.weight, though that’s not so important.

And what about if Weight is actually Acme::Warehouse::Weight?

I’ll give this approach some serious thought, though.

Hmm … libraries don’t ever call “freeze” on their classes, do they?

s = StockItem.new
s.name = “Apple Pie” # An apple pie…
s.cost = 10 # Costs $10…
per_slice = s.cost / 8 # Split it eight ways…
puts per_slice # => 1

Therefore, by simply assuming that you’re getting an object that
can act like a float, you’ve introduced a huge error. Should I have
entered 10.0 as the price, or divided by 8.0? Either of those would
have guaranteed me a Float context in which type coercion will be
used to ensure a Float result. If, however, we had converted cost to
a float explicitly during assignment, this wouldn’t even be an
issue. Without talking about Strings, we’ve already run into a
problem with StockItem’s assumption of Float-ness.

Yes, but as you noted yourself, you’ve violated the contract on the
class. I haven’t bothered to clutter my example with “defensive
programming”. I grant that you may be right that the cost method should
call to_f on its parameter, so that Integers can also be passed. And
maybe every method expecting a String parameter should call to_s on its
parameter?

Oh, unless nil is acceptable as a parameter, in which case it would be
better to do:
@name = name.to_s if name

I wonder how many of the Ruby standard libraries do this? Certainly no
attribute declared with “attr_reader” etc does.

Hmm … by the way, aren’t you arguing against duck-typing here?
If someone deliberately creates a type which is like a Float, then
this code would force it to be a real float (assuming that to_f returns
a real Float object).

Compare the same Java:

class StockItem {
String name;
float cost;

void setName(String n) { name = n; }
void setCost(float  c) { cost = c; }

String getName() { return name; }
float  getCost() { return cost; }

}

In Java, it doesn’t matter if you pass an int to setCost because the
compiler has already marked that as a float – and it will do an
implicit conversion from int to float. (IIRC, that won’t work in
Ada, which disallows implicit conversions.)

Yes, but for the Java Digester library, implicit conversions are
irrelevant. It doesn’t try to pass a String to the setCost method and
hope the compiler will insert the correct conversion code (it won’t
anyway). Instead, as described earlier, a table of “type conversion
operations” is used to map from the input String to the target type,
allowing any target type (such as Weight) to be correctly dealt with.

The author of the StockItem class should have considered that any
numeric value could have been assigned – and that integer math
wouldn’t be a good idea.

As above, I agree that for safety cost= could try to ensure the
parameter passed to it complies with the contract. I still don’t believe
that it is mandatory for methods to enforce their contracts,
though…user beware should be the motto, for performance and
simplicity.

attr_accessor proc { |x| x.to_i }, :item_id
That’s some very cool code. I can feel my brain expanding just by
looking at it! However I don’t feel it does what I want, because
this code actually changes the API of the target class, breaking
all other code that accesses that same attribute thereafter.

Actually, it doesn’t change the API at all. It enforces the
documented constraints. It’s the difference between early and late
detection.

I didn’t initially realise that if a real Float was passed to this
method, then calling to_f on it is ok; it just returns the same object.

[snip bean info stuff]

I donno. That still doesn’t feel very “Ruby” to me, and I personally
find both StrongTyping and MetaTag clunky, trying to solve things
that I’m not sure are best solved that way.

Yes, I’m still worried that there is some obvious rubyish way to handle
this. Maybe when I actually issue 0.1 of xmldigester (with whatever
API), then start trying to build some apps with it some epiphany will
happen.

Cheers,

Simon

···

On Thu, 2003-11-06 at 16:27, Austin Ziegler wrote:
On Thu, 6 Nov 2003 10:16:41 +0900, Simon Kitching wrote:

> I believe that's by design; as I understand it, the StrongTyping > module performs parameter gatekeeping based exclusively on the > class/module ancestry of an object (the namespaces to which it > belongs, as Rich and Chad were discussing), not on what the object > actually can do. This means, as you say, that objects which might fit > the bill may not get through, if their class/module ancestry is wrong, > and also that objects which do not fit the bill can get through -- for > example:

This is the fundamental philosophical disagreement, or
miscommunication, or what have you. If an object fits the bill, and
its class/ancestry is wrong, then there is a error in design.
It should not be the case that this happens, or you have found an
error in your code.

I realize not all of Ruby is documented in this manner; that’s a
simple matter to change. A few smaller modules would solve this; for
instance, Set, HashedSet, IndexedSet, etc. Array would be an
IndexedSet; modules such as CGI would include HashedSet. Then you
could ask for the simple behavioral pattern you desire, and know that
you have it. You would further be assured that this #[] means what
you want it to.

This isn’t really any different than duck typing, except you’re just
making sure that it really does quack, it doesn’t just have a bill.

···

On Thu, 6 Nov 2003 12:45:39 +0900 dblack@wobblini.net wrote:


Ryan Pavlik rpav@mephle.com

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

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

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.

···

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


Ryan Pavlik rpav@mephle.com

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

Given:

class Weight
def initialize(units, amount)
@units = units
@amount = amount
end
end

class StockItem
attr_accessor :weight
attr_accessor :name
attr_accessor :cost # Float
end

Now as a programmer dealing with the above problem, I want to be
able to tell xmldigester than when it encounters the
tag it is to create a StockItem instance, then create a Weight
instance and initialise it appropriately from a string, then
assign that initialised object to the StockItem’s weight
attribute.

Fair enough. But what is a Weight? You say that the StockItem
expects that the object stored in @weight will behave as a Weight.
Do you enforce it, or just let it blow up later if someone disobeys
the contract?

The Java Digester version does this automatically, by determining
that the StockItem has a “weight” attribute of type “Weight”, and
that there is a “weight” xml attribute (with a string value). It
then invokes a table of data-conversion methods to convert the
string to the target type; built-in types are already in the
table, and user-specific types (like Weight) can be added as
needed.

Um. You’ve sort of given the right answer here, I think, for making
this generic. There’s a conversion table in the digester. So what
you do is you make your conversion table a bit more rich than it is
in Java.

Someone – Rich Kilmer? – suggested that a #from_string is
appropriate. Well, why not?

You’re suggesting that in the file which contains the
“xml-parsing” code, I re-open the existing StockItem class, and
use some approach like the attr_accessor modification to “wrap”
the existing weight= code, resulting in something that effectively
works like:

class StockItem # reopen existing class
alias :__weight= :weight=
def weight=(param)
weight2 = param.to_weight
__weight = weight2
end
end

That’s certainly a possibility; but not the one that I’d do, because
it requires creating a “to_weight” on both the original class and
String. Rather, I might suggest doing:

class Weight
def self.from_String(weight_string)
# code necessary to parse the string into units and amount,
# which are then passed into Weight.new
end
end

class StockItem
alias_method :__set_weight, :weight=

def weight=(param)
  w = Weight.__send__("from_#{param.class}".intern, param)
  self.__set_weight(w)
rescue
  self.__set_weight(param)
end

end

You’ve now got everything you need to be able to handle such a
string very easily. You would still be able to do both:

stock_item.weight = “0.75 kg”
stock_item.weight = Weight.new(“kg”, 0.75)

Hmm … libraries don’t ever call “freeze” on their classes, do they?

Rarely.

Therefore, by simply assuming that you’re getting an object
that can act like a float, you’ve introduced a huge error. Should
I have entered 10.0 as the price, or divided by 8.0? Either of
those would have guaranteed me a Float context in which type
coercion will be used to ensure a Float result. If, however, we
had converted cost to a float explicitly during assignment, this
wouldn’t even be an issue. Without talking about Strings, we’ve
already run into a problem with StockItem’s assumption of
Float-ness.
Yes, but as you noted yourself, you’ve violated the contract on
the class. I haven’t bothered to clutter my example with
“defensive programming”. I grant that you may be right that the
cost method should call to_f on its parameter, so that Integers
can also be passed. And maybe every method expecting a String
parameter should call to_s on its parameter?

If you’re expecting to deal explicitly with the string result, yes.
But if you’re going to operate on the object and then deal with the
string, no. The violation of the contract is a small one – and, I
argue, a common one. I’ve been bitten more than once by the lack of
a .0 …

Hmm … by the way, aren’t you arguing against duck-typing here? If
someone deliberately creates a type which is like a Float, then
this code would force it to be a real float (assuming that to_f
returns a real Float object).

No, not really. There’s a BigDecimal class that works a bit like the
Bignum class.

(333).class # => Bignum
(333).to_i.class # => Bignum

Just because #to_i/#to_f is called doesn’t mean that an actual
Integer or Float will be returned; but if it isn’t returned, then
the class is more or less guaranteeing that it will behave exactly
as it should.

In Java, it doesn’t matter if you pass an int to setCost because
the compiler has already marked that as a float – and it will do
an implicit conversion from int to float. (IIRC, that won’t
work in Ada, which disallows implicit conversions.)
Yes, but for the Java Digester library, implicit conversions are
irrelevant. It doesn’t try to pass a String to the setCost method
and hope the compiler will insert the correct conversion code (it
won’t anyway). Instead, as described earlier, a table of “type
conversion operations” is used to map from the input String to the
target type, allowing any target type (such as Weight) to be
correctly dealt with.

Fair enough, as I said. But don’t you, the user, have to create such
conversions before running the digester (excepting well-known
“common” types)?

The author of the StockItem class should have considered that
any numeric value could have been assigned – and that integer
math wouldn’t be a good idea.
As above, I agree that for safety cost= could try to ensure the
parameter passed to it complies with the contract. I still don’t
believe that it is mandatory for methods to enforce their
contracts, though…user beware should be the motto, for
performance and simplicity.

I never said it was mandatory. I have a lot of methods that operate
on objects assuming that they are of the correct type (per duck-
typing). But on those few methods where I must ensure behaviour, I
will do such enforcement. I tend to do failure enforcement (that is,
I fail when I encounter something that doesn’t match), but I enforce
compliance regardless.

Actually, it doesn’t change the API at all. It enforces the
documented constraints. It’s the difference between early and
late detection.
I didn’t initially realise that if a real Float was passed to this
method, then calling to_f on it is ok; it just returns the same
object.

Right.

I donno. That still doesn’t feel very “Ruby” to me, and I personally
find both StrongTyping and MetaTag clunky, trying to solve things that
I’m not sure are best solved that way.
Yes, I’m still worried that there is some obvious rubyish way to handle
this. Maybe when I actually issue 0.1 of xmldigester (with whatever API),
then start trying to build some apps with it some epiphany will happen.

Perhaps. It’s been an interesting discussion, and I think I’ve
created some useful metacode out of the discussion, for sure.

-austin

···


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.11.06
* 01.55.07

Hi –

> I believe that's by design; as I understand it, the StrongTyping > module performs parameter gatekeeping based exclusively on the > class/module ancestry of an object (the namespaces to which it > belongs, as Rich and Chad were discussing), not on what the object > actually can do. This means, as you say, that objects which might fit > the bill may not get through, if their class/module ancestry is wrong, > and also that objects which do not fit the bill can get through -- for > example:

This is the fundamental philosophical disagreement, or
miscommunication, or what have you. If an object fits the bill, and
its class/ancestry is wrong, then there is a error in design.
It should not be the case that this happens, or you have found an
error in your code.

At this point you’re waging a battle directly against the design of
Ruby. Ruby allows you to extend objects at runtime; to decide that
this is sloppy or wrong or a second-rate programming technique is
entirely arbitrary.

When you check whether or not an object’s response to #is_a? includes
an element you’ve specified, that’s the one and only thing you’re
checking. It’s not as if Ruby somehow pulls up its socks and
straightens its tie and says “Better stop this dynamic stuff!” when it
sees a call to #is_a? It doesn’t; it remains dynamic, and the
programming techniques required to ascertain the interface of an
object do not change. (Besides, its socks and tie were just fine to
start with :slight_smile:

I realize not all of Ruby is documented in this manner; that’s a
simple matter to change. A few smaller modules would solve this; for
instance, Set, HashedSet, IndexedSet, etc. Array would be an
IndexedSet; modules such as CGI would include HashedSet. Then you
could ask for the simple behavioral pattern you desire, and know that
you have it. You would further be assured that this # means what
you want it to.

I think I must be not getting something here; it sounds like you’re
suggesting that every possible behavior of every future object in
every Ruby program be anticipated by modules in the core distribution,
with multi-barreled names to suit the purpose. I’m thinking that
can’t really be what you mean.

Also, remember that you’re never 100% assured that # or any other
method means what you want it to. It’s ducks all the way down :slight_smile:
(Trực tiếp bóng đá xoivotv, xem bóng đá xôi vò tv có bản quyền) Every method call
operates under the same conditions as every other. You can light a
candle, dance a jig, call #is_a? twenty times… but in the end,
obj# is whatever obj# is. You can’t change Ruby for a few
nanoseconds at a time through sheer willpower.

Hence the quest to harness the dynamism, rather than wishfully think
that it comes and goes.

This isn’t really any different than duck typing, except you’re just
making sure that it really does quack, it doesn’t just have a bill.

Also, to reintroduce something Chad brought up: this isn’t just a
matter of the illusory nature of the rigor of #is_a? It’s also about
missed opportunities. It’s quite possible to write Ruby programs that
mimic statically-typed languages as closely as possible, and where
checking an #is_a? list almost certainly will, in practice, indirectly
give you accurate information about an object’s type. But that means
that, in the name of some kind of imagined rigor or discipline or
something, one has decided to program using a small, somewhat
impoverished subset of Ruby’s capabilites. One then ends up with
pretty-looking (because it’s Ruby :slight_smile: pseudo-statically-typed code.

Mind you, as with discussions of thread safety and name clashes, a lot
of the possible weakness of #is_a?-checking pertain to what would
happen if little gremlins got into your program and changed
everything. In practice, if you decide to treat Ruby classes as
immutable and Ruby objects as wannabe statically-typed, you will
probably do OK (though your code may lack scaleability). But it’s
important to remember that, in doing this, you’re not dealing
directly with the types of objects.

In the end, everyone has to do what they think best. I’m more
interested in an object’s type than its response to #is_a?, so I tend
to feel comfortable with extending objects dynamically and putting
them in situations where they can do their thing. If you spot (real)
errors in the design of any of my programs, please let me know :slight_smile:

David

···

On Thu, 6 Nov 2003, Ryan Pavlik wrote:

On Thu, 6 Nov 2003 12:45:39 +0900 > dblack@wobblini.net wrote:


David Alan Black
home: dblack@wobblini.net # New email address
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

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

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.

···

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:

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