From: Jean-Hugues ROBERT Date: 2004-05-05T22:20:15+09:00 Subject: Re: Why no Proc##[]=() ? Why no Proc##replace() ? At 09:16 05/05/2004 +0900, you wrote: > > > a[i, j, k] = l, m, n #=> becomes a.call(i, j, k, [l, m, n]) > > Its actually # => becomes a.call( i, j, k, l). >:) No, it does what I said. Try it out. You are right, I was wrong. Fortunately a[i, j, k], b = l, m, n *does* become a.call( i, j, k, l) plus b = m. I was worried that multiple assignments would not work specifically with []=, but it does work, as long as both sides of = are "multiple". > > We both agree I think that o.x = y is a nicer syntax then o.x( y). > > Only I think that p[] = y is a nicer syntax then p[y] if proc were an > > accessor. > >But p isn't always an accessor. Your #[]= proposal is similar to saying >that because we can call p.a, we should always be able to call p.a = b. The thing is, the one that use a block usually knows about what the block semantic is and what is the protocol to use it (including what params the block expects). My proposal is just a convenience thing so that you can keep doing x = proc { xxx } instead of some x = LvalueProc.new { xxx }, when your proc can be a lvalue in addition to a rvalue. Not a big deal really. > > Back to block: b[] = x, to me, reads as "the content of b is assigned the > > value of x". > > What that means exactly depends on the semantic of the block b. > >But b[] isn't "the content of b" for Proc objects. It's "call b." They're >different conventions. That is the way it is today, I am proposing a small change where in addition to [] meaning "value of" or "call" there would be []= meaning "gets assigned xx". This would be an additional convention/meaning that the user may want to attach to the semantic of the block. It would not work for read only block, much like Konst = x does not work for constants. I understand that you prefer []= to be undefined by Proc because there are blocks which are read-only and as a result []= is meaningless for them whereas [] allways make sense because a block is always "callable", if you know what parameter to use. At the end of the day it the user of the block that knows what can be done, not class Proc. I suppose we may safely assume that []= makes no sense, never, with parameterless blocks. >In C++, I can do > cout << "Hello."; ><< also works on ints. Does this make sense? > a = 5 << "Hello"; >Just because it's the same symbols (<< or []) doesn't mean it does the same >thing. [] for Array means something different than [] for Proc. It's handy >that they can be used interchangably in some circumstances, but that doesn't >mean they're interchangeable in all circumstances. Sure. In some circumstances I do believe it is "handy" that a Proc could implement slightly more of the protocol of Array. That is possible by some user defined ArrayLikeProc subclass of Proc. That class Proc would implement []= would just be convenient, a convenience that is questionable and is maybe a matter of taste. >Just because Arrays have >[] doesn't mean I expect them to define #call like a Proc. So just because >Procs have [] doesn't mean they should have []= like an array. Symmetry for symmetry is not a good enough reason, I agree with you. On a similar plane but for rvalue, I think Smalltalk as a xx.value() method that a lot of objects implement. Such objects are called Valueables I think. This is handy. I think Smalltalk .value() for blocks/procs would be equivalent to xx.call(). About an hypothetical Lvalue class: c = Foo.new() > > If c is_a? Lvalue then the lvalue object that c refers to (& which can be > > any lvalue, > > a variable included), becomes a new Foo (which means that it now refers to > > a Foo: > > c now refers to the same something but that something now refers to the new > > Foo). > >So the interpreter has to do runtime checks to see if a variable has an object >of type Lvalue and then does special things if it is? What if I want to make >my own class that has special assignment characteristics, but don't want to >inherit from Lvalue? I can't. How the interpretor determines that an object references another one is to be refined. Obviously a Lvalue object would reference another object. As a result the Interpretor could handle assignment doing something like: if (target_var = exiting_var).kind_of? Lvalue then target_var = existing_var_current_value end assign new_value to target_var OTOH the interpretor could use .respond_to? := instead of .kind_of? Lvalue. In the first case, you are right, if you want to make your own class that has special assignment characteristics, you have to inherit from Lvalue. In the second case you merely have to define .=() in your own class. It is probable that there are other ways for the interpretor to determine that it has to do an additional level of dereferencing. Each way would have pros/cons. I would tend to pick a solution with minimal speed overhead because the check would occur frequently. Please note that I believe that a Lvalue class would be usefull even without any auto-dereferencing by the interpretor. Auto dereferencing is just something that would make my prototype implementation of LogicPointer more transparent (much as can be done with delegation on some other plane). > > However, this does not mean that all Ruby variables would have to be > > different from what they are today (whatever the name you use to describe > > what they are today). Only variables that holds a reference to a Lvalue > > object would have to be treated differently than the "normal" variables. > > That's because the Ruby interpretor would have to invoke some .getter() or > > .setter() method of the Lvalue instead of using the variable's content > > directly (or, to rephrase more formally, xxx instead of directly using the > > reference to some object that the variable holds). > >How do you determine at compile time whether > > a = b > >means "a = b" or "a.setter(b.getter)"? The only answer I can see is, "it's >always 'a.=(b)'." Which means by-value assignment. The only way you could >keep Ruby's existing assignment semantics, is if 'a.=(b)' was by default >'a.become(b)'. I think that you are right, you cannot determine it easely at compile time. I believe you should to do it at runtime. But the interpretor can optimize that a lot because most of the time the full blown Lvalue object is not needed by the user. > > I am not proposing such a radical change at all. I would rather go forward > > than backward :-) What I am proposing is an additional tool, by the way of > > an additional level of indirection. When the programmer need that tool it > > has to be explicit and she/he would create a Lvalue object using some > > explicit syntax: > > b = "toto" > > c = ref b # *explicit* > > c = "titi" > > p b # => "titi" > > c is like an alias for b. > >So is c a different type of variable than b? Does this mean we have regular >type variables and reference type variables? Not exactly. What happens is that a variable can reference a Lvalue object. When assigning a value to a variable, the interpretor would check the previous value of the variable. If the variable references an Lvalue object (or, maybe, an object that responds to := ) then it would be the Lvalue object that is told to reference a new value instead of the initial variable. > Are reference type variables >only able to have their references set at time of definition? How does this >work: > > a = "toto" > b = "frodo" > c = ref b > c = ref a > >At the end, what is b? Is b a reference to a? Is b still "frodo"? If you >pick one, what if I want the other? Also, what about: > > a = "toto" > c = "frodo" > c = ref a > >How does the compiler know which type of variable c is, because it holds both >regular and reference types at various times? Very good questions indeed. I believe that c = ref b c = ref a implies that at the end b is a Lvalue that references the variable a (and c stays unchanged) If you want the other you need to use the RCR's "assign" operator: assign c, ref a. That will be described further in the "match, assign & Lvalue" RCR that I am working on. >Here's what I'm saying. By distinguishing between regular and reference >variables or whatever, what you're saying is that "A variable represents a >chunk of memory." Currently, Ruby says, "A variable is a reference to an >object." For your proposal to work, 'a = b' for reference variables would >mean, "copy b's memory into the place pointed by a." Currently, it's >"make a point to the same place as b." In your proposal, 'a = b' for regular >variables (and there is a distinction) would mean "copy b's memory into >a's memory." I was not clear enough in my explanations. Thanks to the Lvalue class, a variable can sometimes reference another variable. At the end of the day *all* variables reference an object, exactly as it is the case with Ruby today. There would be no such thing as a "regular" versus a "reference" variable. There would be only "regular" variables, but some of them would reference an object of class Lvalue instead of an object of some other class. >I guess what you want is to keep "every variable is a reference" and you >want reference references. Or something like that. You've already built >that with your pointer class, it just isn't as transparent as regular >assignment. But in my mind, that's okay, because what you want isn't >useful/the correct way to do things very often. If we had what you >envision, we could have people writing: > > foo(a, ref b) > >instead of > > b = foo(a) > >Which is bad. It's like C. The only reason you write C code like that is >to return error codes while still passing out information, or to pass out >multiple values. We have exceptions for error conditions, and we can >easily return out multiple values. So variable references are, in fact, >_the incorrect Ruby-way to do things_ for the two biggest cases of their use >in C. I understand that any features can be poorly used. That something can be used in a bad way is also the responsibility of the user (assuming that the intended "good" usage was properly documented). I believe that a Lvalue class is actually something that makes it possible to do things in a cleaner object oriented way than what you can do today with more questionable mechanisms, eval() included. It may also be a tool that makes it easy to do things that are really difficult to do in Ruby. The Lvalue class is to lvalues what the Method class is to methods. Think about Ruby's local_variables(), it returns an array of strings. Then you typically use the strings with eval(). A Lvalue class is kind of implemented there, with strings object ids in some parallel object space. Imagine now a local_variables() that would return an array of Lvalue instances. Now, instead of using eval() to get/set the local variables, you would use the Lvalue objects' getter()/setter() methods. I think most people would agree that you should use eval() only when there is no better way to do something (when there is no better way, you are really happy that at least eval() does exist !). A Lvalue class is I believe something useful to do things that can be done with eval() only, as of today. It helps make these things in a more object oriented way. I suspect it makes it possible to do things that are really difficult to do today (never say "impossible"). I hope it is clearer now that my intention is to help people do things in a cleaner object oriented way. I don't feel responsible for the perverse use of some new Lvalue class as long as my mechanism is well designed enough to work well for natural "good" usages. > > Then you don't mind that much that "In Ruby everything is an object, but > > variables and > > ... and ...". > > I would prefer "In Ruby everything is an object". Introspection is a great > > tool, the more, the better. > >To me, variables in Ruby are imaginary. They only help me, and don't exist >as far as the objects/interpreter are concerned. The only way to get an >object is by it's object id, and a variable holds an id for my convenience. This is most often the case. It is only when you start toying with introspection and meta-programming that the Lvalue class starts becoming interesting. >I doubt reference variables would be used for introspection. You can already >get and set instance variables by a method. What is the purpose of knowing >that a method has a local variable called "x"? Being able to assign to a >variable in one place and have it affect a variable in an entirely different >would, in my opinion, be more often confusing than useful. Your logic >methods are the only good example I know of, and you can implement them >in pure Ruby as long as you don't mind using explicit dereferencing and some >eval evil. I don't think they would be generally useful, because there are >better ways to do most of what they let you accomplish. Thanks about the "logic" methods. The prototype implementation is in progress, using explicit dereferencing (and some eval evil). How much useful it is may become more apparent in the future RCR unless I am overlooking better ways to do most of waht they let you accomplish. I suppose my intention has some connections with yours regarding evil eval. We both look for better ways to do things that only eval can do today. Now, about how much introspection is enough, it varies depending on your needs. >Cheers. > >- Dan Thanks for your feedbacks. Yours, Jean-Hugues ------------------------------------------------------------------------- Web: http://hdl.handle.net/1030.37/1.1 Phone: +33 (0) 4 92 27 74 17