def [](v) xx; return yy; end # returned value is ignored !?

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.

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
 
M

Mark Hubbart

It may be interesting to be able to access variables indirectly
using binding() (outside eval).
for example:

a = 2
binding().local_get:)a)
=> a == 2

binding().local_set!:)a, 5)
=> a == 5

And also Binding::ivar_set!, Binding::ivar_get and
Binding::get_self

or:

binding.local:)a) = 23 #=> 23
a #=> 23
binding.local:)a) += 19 #=> 42
a #=> 42

also:

binding.eval{ self } #=> main

... would be nice. I dislike having to eval strings when I need to do
some work within a certain binding; and I suppose that code blocks will
compile nicer, especially when we get a VM.
 
K

Kristof Bastiaensen

Hi,

binding.local:)a) = 23 #=> 23
a #=> 23
binding.local:)a) += 19 #=> 42
a #=> 42

I wonder how you could make this work. binding.local:)a)
would have to return something that could be used as a
lvalue. I could be a reference, but the reason to have
these methods would be to avoid them.
Another way to do this:

binding.local:)a).value = 23
binding.local:)a).value += 19

binding.local(sym) would then return something like
a Variable class. It will still be possible to change
the value of a variable, but not without participation
of the calling scope.
also:

binding.eval{ self } #=> main

Also here I would remove the eval. Maybe binding.get_self or
binding.current_object?
.. would be nice. I dislike having to eval strings when I need to do
some work within a certain binding; and I suppose that code blocks will
compile nicer, especially when we get a VM.

Yes, I agree. Maybe we could pack it up in a RCR?

Kristof
 
M

Mark Hubbart

Hi,



I wonder how you could make this work. binding.local:)a)
would have to return something that could be used as a
lvalue. I could be a reference, but the reason to have
these methods would be to avoid them.
Another way to do this:

binding.local:)a).value = 23
binding.local:)a).value += 19

binding.local(sym) would then return something like
a Variable class. It will still be possible to change
the value of a variable, but not without participation
of the calling scope.

I see. I momentarily forgot that while you can use methods as lvalues,
you have to choose between lvalue and method call; you can't assign to
it if you use the parens. I'm scrambling now for a replacement that I
like...

Perhaps:

binding[:a] = 23
binding[:a] += 19

Here's some rather simple code that will do it:

class Binding
def [](sym)
eval(sym.to_s, self)
end
def []=(sym, val)
code = "#{sym} = ObjectSpace._id2ref #{val.id}"
eval(code, self)
end
end

I agree that in most cases this should be avoided. However, there are
some cases where bindings are the only way of getting things done. irb,
for example, makes extensive use of programming practices that would
otherwise be looked upon as horrific. :) Serious violation of scoping
rules.
Also here I would remove the eval. Maybe binding.get_self or
binding.current_object?

I guess there could be methods added for that. But I think the eval
thing should be there for more general purpose stuff. It would be
easier to remember one eval method than five fetching methods.

Also, it would be better to call it something other than 'eval' I
suppose. #binding_eval?

By allowing a simple way of eval'ing code within a binding like that,
you end up with a very flexible way of working with bindings.
Yes, I agree. Maybe we could pack it up in a RCR?

I think that would be a good idea; Object#instance_eval takes code
blocks; why not Kernel#eval? If Kernel#eval supported code blocks, it
would be trivial to define Binding#binding_eval.
 
K

Kristof Bastiaensen

Hi,

I see. I momentarily forgot that while you can use methods as lvalues,
you have to choose between lvalue and method call; you can't assign to
it if you use the parens. I'm scrambling now for a replacement that I
like...

Perhaps:

binding[:a] = 23
binding[:a] += 19

Yes, that looks better than my version with value.
I guess there could be methods added for that. But I think the eval
thing should be there for more general purpose stuff. It would be
easier to remember one eval method than five fetching methods.

Also, it would be better to call it something other than 'eval' I
suppose. #binding_eval?

By allowing a simple way of eval'ing code within a binding like that,
you end up with a very flexible way of working with bindings.

Yes, Binding#eval with a block could be very useful.
(Sorry, I think I didn't look at your code good enough).
I would prefer Binding#eval with a block over Kernel#eval,
because it would make the meaning more clear.
Kernel#eval could be made even an alias for Binding#eval. I can see only
one problem: compilation of local-variables. Inside the eval block, any
local variables that you don't assign to will be treated as a method-call,
and this could be annoying, because they may exist in the binding.


Kristof
 
M

Mark Hubbart

Yes, Binding#eval with a block could be very useful.
(Sorry, I think I didn't look at your code good enough).
I would prefer Binding#eval with a block over Kernel#eval,
because it would make the meaning more clear.
Kernel#eval could be made even an alias for Binding#eval. I can see
only
one problem: compilation of local-variables. Inside the eval block, any
local variables that you don't assign to will be treated as a
method-call,
and this could be annoying, because they may exist in the binding.

I don't understand why local variables would be treated as a method
call... They aren't treated that way when you eval a string, correct?
is there something about the compiler that would treat the locals
differently in a block?

--Mark
 
K

Kristof Bastiaensen

I don't understand why local variables would be treated as a method
call... They aren't treated that way when you eval a string, correct?
Correct.

is there something about the compiler that would treat the locals
differently in a block?

--Mark

I may be wrong about this, but I think this is how it would be:
Eval compiles it's expression at runtime. At that time it
already knows which variables are locals, and which are method-calls.
However blocks are compiled at the same time as the rest of the
program, so at that moment Ruby cannot know if an identifier is
a local variable or a method inside the given binding.

Kristof
 

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

No members online now.

Forum statistics

Threads
474,145
Messages
2,570,826
Members
47,371
Latest member
Brkaa

Latest Threads

Top