Changing ==,>,<, etc

N

Nathan Weston

The latest Perl exegesis
(http://www.perl.com/pub/a/2003/07/29/exegesis6.html?page=4)
introduces "junction types", which are really cool.
In short, you can do expressions like (a == any(1,2,3)) or (a >
all(1,2,3)) instead of (a == 1) or (a == 2) or (a == 3), etc.

So of course, I immediately set out to do this in ruby. So, I whipped
up a Disjunction class, and the any() function for syntactic sugar,
and soon I could do:

# 1 == any(1,2,3)
true
# 4 == any(1,2,3)
false

And all was well. But then I hit a snag:
# 1 < any(1,2,3)
ERR: (eval):1:in `<': Disjunction can't be coerced into Fixnum

Fixnum#== seems to be smart enough to call Disjunction#==, which gets
the behavior I want. Unfortunately, Fixnum#< is not, and neither are
most of the <,>,== methods in the various built-in classes (i.e.
String#== and Array#== both return false for any Junction class).

Of course, with ObjectSpace and module_eval, I can redefine everyone's
comparison methods to work properly, but this is kind of ugly. Also,
if someone defines a new class after after I have done my ObjectSpace
hack, they have to explicitly write their comparison methods to know
about Junctions.

Is there a better way to do this?

If there's not a better way, should there be?
I was thinking that perhaps all comparison methods should have a
similar semantics, along the lines of:

class Foo
def ==(obj)
if obj.is_a?(Foo)
#I know how to compare myself to other Foo objects,
#so I will do so
return somevalue
else
#I don't know how to compare myself to non-Foo objects,
#but maybe they know how to compare themselves to me --
#let's give them a chance
return (obj == self)
end
end
end

Or at least, that's the basic idea. There also needs to be some way to
bail out when neither class knows how to compare to the other --
either by returning false or throwing an exception.

If all comparison methods worked this way, I could implement my
Junction classes with no hacks, since objects that didn't know
anything about Junctions would just let the Junction do the
comparison.
 
D

Daniel Carrera

I think I have an idea. I'm not sure about the implementation details:

Example: 3 < any(1,2,4)

1) Create a Disjunction#<=> and mixin Enumerable so that you can write
things like:

any(3) < any(1,2,4)
all(3) > any(1,2,4)

2) Create a Disjunction#coerce method so that the line:
3 < any(1,2,4)
Is turned into the line:
all(3) > any(1,2,4) # It doesn't matter if it's any(3) or all(3).

When Ruby sees "3 < any(1,2,4)" it'll look for Fixnum#coerce for help.
When it doesn't help, it'll look at Disjunction#coerce. Or, at least
that's what I think.

The Disjunction#coerce would be written somewhat like this:

class Disjunction
# WARNING: No error checking.
def coerce(other)
return all(other), self
end
end

Cheers,
Daniel.


The latest Perl exegesis
(http://www.perl.com/pub/a/2003/07/29/exegesis6.html?page=4)
introduces "junction types", which are really cool.
In short, you can do expressions like (a == any(1,2,3)) or (a >
all(1,2,3)) instead of (a == 1) or (a == 2) or (a == 3), etc.

So of course, I immediately set out to do this in ruby. So, I whipped
up a Disjunction class, and the any() function for syntactic sugar,
and soon I could do:

# 1 == any(1,2,3)
true
# 4 == any(1,2,3)
false

And all was well. But then I hit a snag:
# 1 < any(1,2,3)
ERR: (eval):1:in `<': Disjunction can't be coerced into Fixnum

Fixnum#== seems to be smart enough to call Disjunction#==, which gets
the behavior I want. Unfortunately, Fixnum#< is not, and neither are
most of the <,>,== methods in the various built-in classes (i.e.
String#== and Array#== both return false for any Junction class).

Of course, with ObjectSpace and module_eval, I can redefine everyone's
comparison methods to work properly, but this is kind of ugly. Also,
if someone defines a new class after after I have done my ObjectSpace
hack, they have to explicitly write their comparison methods to know
about Junctions.

Is there a better way to do this?

If there's not a better way, should there be?
I was thinking that perhaps all comparison methods should have a
similar semantics, along the lines of:

class Foo
def ==(obj)
if obj.is_a?(Foo)
#I know how to compare myself to other Foo objects,
#so I will do so
return somevalue
else
#I don't know how to compare myself to non-Foo objects,
#but maybe they know how to compare themselves to me --
#let's give them a chance
return (obj == self)
end
end
end

Or at least, that's the basic idea. There also needs to be some way to
bail out when neither class knows how to compare to the other --
either by returning false or throwing an exception.

If all comparison methods worked this way, I could implement my
Junction classes with no hacks, since objects that didn't know
anything about Junctions would just let the Junction do the
comparison.

--
Daniel Carrera | PGP: 6643 8C8B 3522 66CB D16C D779 2FDD 7DAC 9AF7 7A88
Math PhD. UMD | http://www.math.umd.edu/~dcarrera/pgp.html

* * * * * Weekly Smile * * * * * * * * * * * * * * * * * * * * * * * *
Sign in a hotel in Athens:
Visitors are expected to complain at the office between the hours
of 9 and 11 A.M. daily.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
H

Hal E. Fulton

----- Original Message -----
From: "Daniel Carrera" <[email protected]>
To: "ruby-talk ML" <[email protected]>
Sent: Thursday, July 31, 2003 1:46 PM
Subject: Re: Changing ==,>,<, etc

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
# 1 < any(1,2,3)

What you want can be done with the any? method for arrays.

[1, 2, 3].any? { |e| 1 < e }

But the syntax "1 < any(1,2,3)" is very readable.

I've always favored an "in" operator as in Pascal
or (I think) Python. It would call include? just
as for/in calls each:

1 in [1,2,3] # means [1,2,3].include? 1

Hal
 
N

Nathan Weston

That seems to work for Numeric types, but it doesn't help with other
objects (Strings, for example).
I'd like to be able to put any object inside a Disjunction or
Conjunction.

BTW, I am using ruby 1.6.8. Are there any relevant differences in 1.8?

Nathan
 
D

Daniel Carrera

How is your Disjunction class implemented? Does it understand Strings?
Can I type this?:

all("c") < any("d", "e", "f")

If so, then the Disjunction#coerce should work. If not, then the problem
is in the implementation of Disjunction I think.

Daniel.

That seems to work for Numeric types, but it doesn't help with other
objects (Strings, for example).
I'd like to be able to put any object inside a Disjunction or
Conjunction.

BTW, I am using ruby 1.6.8. Are there any relevant differences in 1.8?

Nathan

--
Daniel Carrera | PGP: 6643 8C8B 3522 66CB D16C D779 2FDD 7DAC 9AF7 7A88
Math PhD. UMD | http://www.math.umd.edu/~dcarrera/pgp.html

* * * * * Weekly Smile * * * * * * * * * * * * * * * * * * * * * * * *
Sign in a hotel in Athens:
Visitors are expected to complain at the office between the hours
of 9 and 11 A.M. daily.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 
N

Nathan Weston

At the moment, Disjunction doesn't know anything about its contents --
it just does

def ==(obj)
@contents.each { |c|
if c == obj
return true
end
}

return false
end

At the moment,
any("1", "2", "3") == "1" works fine.

But if I do the reverse:
"1" == any("1", "2", "3")

it just returns false. String#== doesn't even try to call a coerce
method. String#< gives a type error, again without trying to call
Disjunction#coerce. Looking at the API docs, it seems that only
Numeric types have coerce.

Nathan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,103
Messages
2,570,642
Members
47,245
Latest member
LatiaMario

Latest Threads

Top