J
Jean-Hugues ROBERT
You may skip this msg if you don't care about a potential future
new construct in Ruby about pattern matching and assignments combined.
You're right. Thanks. I actually just got close with another trick:
Class Object
def []() self end
def [](x) self end
end
Combined with my version of a Pointer class at
http://www.c2.com/cgi/wiki?SinisterSchemeSampleInRuby
it makes it almost possible to code methods than handle both Pointers and
direct
objects. I just turned the problem upside-down: If I can't have
auto-dereferencing
Reference objects, then always dereference and make it so that it does nothing
on non reference objects. Nota: for classes where [] is redefined, some more
tricky work would be necessary to turn this protypical code into production
one.
def meth( x ) x[] = [x[], "world"] end
# By Val: Returns [x, "world"]
# By Ref: x's content becomes [previous x's content, "world"]
v = nil
r = Pointer.new( {:v})
r[] = :hello
p r # => <Pointer v>:hello
p v # => :hello
meth( r) # By ref
I am not going to propose anything radical. The RCR proposes 2 new builtins,
"assign" & "match", 1 optional unary op, "ref", plus 3 constants "free"
"bound" & "tail", and a Class, Lvalue. That does not include allowing for
global redefinition of =.
If implemented, some new syntax becomes available:
assign [ref a, ref b], [1,2] # eqv a, b = [1,2]
match [ref a, "toto"], x # eqv a = x[0] if x == [x[0],"toto"]
def meth( *p )
assign [ref a, ref b], p
match [a,b], [2,3]
eqv: def meth( a = 1, b = 2)
def meth( *p )
raise ArgumentError unless match [ref a = bound], p
eqv: def meth( a )
def meth( *p )
assign [[ref a]], p
eqv: def meth( (a,) ) # Not valid for def today, only for proc
proc { |*p|
assign [ref x], p
match x, nil
eqv: proc { |x = nil| # Not valid for proc today, only for def
def meth( h )
assign { name: ref n, color: ref c }, h
match [n,c], ["w",:white]
eqv: def meth( name: n = "w", color: c = :white ) ??? 1.9 ?
if match [free,ref c], x then
assert { x.kind_of? Array }
assert { x.length == 2 }
assert { c == x[1] }
if match {msg: ref c = "hello"}, x then
assert { x.kind_of? Hash }
assert { x.include? :msg }
assert { x[:msg] == "hello" }
assert { c == "hello" }
patt = [ref f, ref t = tail]
if assign patt, a_list then
assert { a_list.length >= 1 }
assert { f == a_list[0] }
assert { t == a_list[1..-1] }
If you got it so far, try to imagine what this can do
when objs have a is_a? relationship:
match obj1, obj2
or this: match obj1, obj2, obj3
or (much more tricky): match obj
One hint: some instance variables can be Lvalue objects.
Much more in the future RCR. I believe match/assign are
reasonably easy to implement, efficient and very expressive.
I hope the RCR will communicate that belief.
That's nice ! I almost missed a "this" variable to access the
"self" of the invoker:
class Scope
def with(&b)
self.this = eval( :self, b)
instance_eval &b
end
end
env.with { p "I can have my #{self} cake and eat #{this} too" }
I would normally agree. However I am right now designing the RCR.
I feel like it will have more impact if I provide
a prototype for the implementation where you can see the pros of the
proposal. The pros are more obvious if the syntax to use my prototypal
implemention is close to what it would be should the proposal be implemented
in Ruby.
Yours,
Jean-Hugues
new construct in Ruby about pattern matching and assignments combined.
Jean-Hugues ROBERT said:... Yet, I am missing the []=() where I do control the value
assigned to the lvalue.
Well, you can get close by using the same approach that OpenStruct uses...
You're right. Thanks. I actually just got close with another trick:
Class Object
def []() self end
def [](x) self end
end
Combined with my version of a Pointer class at
http://www.c2.com/cgi/wiki?SinisterSchemeSampleInRuby
it makes it almost possible to code methods than handle both Pointers and
direct
objects. I just turned the problem upside-down: If I can't have
auto-dereferencing
Reference objects, then always dereference and make it so that it does nothing
on non reference objects. Nota: for classes where [] is redefined, some more
tricky work would be necessary to turn this protypical code into production
one.
def meth( x ) x[] = [x[], "world"] end
# By Val: Returns [x, "world"]
# By Ref: x's content becomes [previous x's content, "world"]
v = nil
r = Pointer.new( {:v})
r[] = :hello
p r # => <Pointer v>:hello
p v # => :hello
meth( r) # By ref
The problem is that to achieve what you want the language would need to be
radically changed and I don't think it's a good idea to allow for global
redefinition of =. That could wreak too much havoc on existing code.
I am not going to propose anything radical. The RCR proposes 2 new builtins,
"assign" & "match", 1 optional unary op, "ref", plus 3 constants "free"
"bound" & "tail", and a Class, Lvalue. That does not include allowing for
global redefinition of =.
If implemented, some new syntax becomes available:
assign [ref a, ref b], [1,2] # eqv a, b = [1,2]
match [ref a, "toto"], x # eqv a = x[0] if x == [x[0],"toto"]
def meth( *p )
assign [ref a, ref b], p
match [a,b], [2,3]
eqv: def meth( a = 1, b = 2)
def meth( *p )
raise ArgumentError unless match [ref a = bound], p
eqv: def meth( a )
def meth( *p )
assign [[ref a]], p
eqv: def meth( (a,) ) # Not valid for def today, only for proc
proc { |*p|
assign [ref x], p
match x, nil
eqv: proc { |x = nil| # Not valid for proc today, only for def
def meth( h )
assign { name: ref n, color: ref c }, h
match [n,c], ["w",:white]
eqv: def meth( name: n = "w", color: c = :white ) ??? 1.9 ?
if match [free,ref c], x then
assert { x.kind_of? Array }
assert { x.length == 2 }
assert { c == x[1] }
if match {msg: ref c = "hello"}, x then
assert { x.kind_of? Hash }
assert { x.include? :msg }
assert { x[:msg] == "hello" }
assert { c == "hello" }
patt = [ref f, ref t = tail]
if assign patt, a_list then
assert { a_list.length >= 1 }
assert { f == a_list[0] }
assert { t == a_list[1..-1] }
If you got it so far, try to imagine what this can do
when objs have a is_a? relationship:
match obj1, obj2
or this: match obj1, obj2, obj3
or (much more tricky): match obj
One hint: some instance variables can be Lvalue objects.
Much more in the future RCR. I believe match/assign are
reasonably easy to implement, efficient and very expressive.
I hope the RCR will communicate that belief.
Btw, by adding:
class Scope
def with(&b)
instance_eval &b
end
end
You can use variables that you have defined already quite naturally:
irb(main):034:0> env.x=10
=> 10
irb(main):035:0> env.with { p x; x = 20; p x; p self }#
10
20
#<Scope:0x10182e60 @x=10>
That's nice ! I almost missed a "this" variable to access the
"self" of the invoker:
class Scope
def with(&b)
self.this = eval( :self, b)
instance_eval &b
end
end
env.with { p "I can have my #{self} cake and eat #{this} too" }
IMHO there are lots of ways to do what you want in Ruby, only the exact way
you want is not possible. Sometimes it's better to step back and open one's
mind for other solutions. As I suggested before, a Hash can pretty much do
what you want - it just doesn't give you the exact syntactic sugar you want.
-- robert
I would normally agree. However I am right now designing the RCR.
I feel like it will have more impact if I provide
a prototype for the implementation where you can see the pros of the
proposal. The pros are more obvious if the syntax to use my prototypal
implemention is close to what it would be should the proposal be implemented
in Ruby.
Yours,
Jean-Hugues