Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

[REBOL] Re: On mutability and sameness

From: joel:neely:fedex at: 12-Jun-2001 23:48

Hi, Ladislav, Ladislav Mecir wrote:
> Hi Joel, > > I see, that the only reason why you think, that set-path > is a DATE! mutation instrument is the analogy with the > behaviour of set-path for objects. >
That's not the only reason. Rather, it is by analogy with the behavior for paths and set-paths for all other "data-like" uses (by which phrase I am excluding functions with refinements). To illustrate ... ... with an object:
>> a: make object! [x: 17 y:42] >> a/x == 17 >> a/x: 19 == 19 >> b: a >> b/x == 19
... with a block (by position):
>> a: [2 3 5 7 9 11 15 17 19] == [2 3 5 7 9 11 15 17 19] >> a/7 == 15 >> a/7: 13 == [2 3 5 7 9 11 13 17 19] >> a == [2 3 5 7 9 11 13 17 19]
... with a block (by association):
>> a: [fee "a" fie "b" foe "d"] == [fee "a" fie "b" foe "d"] >> a/foe == "d" >> a/foe: "c" == [fee "a" fie "b" foe "c"] >> a == [fee "a" fie "b" foe "c"]
... with a tuple:
>> a: 5.4.6.2.1 == 5.4.6.2.1 >> a/3 == 6 >> a/3: 3 == 3 >> a == 5.4.3.2.1
In all of the above cases, the simplest and most natural model I can think of is that: 1) a path provides access to a component of a composite data item, and 2) the corresponding set-path modifies that same component of the composite value, without modifying (or copying) the other components. (That's mutation, in my book!) Although the implementation details of all four examples above are likely to be different internally, the higher-level meaning (access to, and modification of, a component) is the same. Therefore, I find that the simplest and most natural explanation of the following:
>> a: 21:18:17 == 21:18:17 >> a/minute == 18 >> a/minute: 28 == 28 >> a == 21:28:17
is that the MINUTE component of a time has been accessed and modified. How else can we explain the fact that afterwards all of the other components of A have the same value as previously? I suppose we *could* try to explain it by imagining that a/minute: 28 asks the interpreter to exhibit the following behavior: - Determine that A/MINUTE: is a SET-PATH! - Get the value of A - Determine that it is a TIME! value - Look ahead to see that the "affected" component is the MINUTE part - Create a new time value by: - Copying the HOUR part of A's value to the HOUR part of the new TIME! value (since we're only "affecting" the MINUTE part) - Placing 28 in the MINUTE part of the new TIME! value (since we're only "affecting" the MINUTE part) - Copying the SECOND part of A's value to the SECOND part of the new TIME! value (since we're only "affecting" the MINUTE part) - Replace the (entire!) old value of A with the new TIME! value just created I have yet to see any reason to imagine such a complicated meaning, especially since the simpler one (change the MINUTE part of A's value "in place") adequately predicts the observed behavior. Let's not forget Occam's razor! To anyone who is bothered by the following:
>> a: 21:18:17 == 21:18:17 >> b: a == 21:18:17 >> a/minute: 28 == 28 >> a == 21:28:17 >> b == 21:18:17
I would point out that (unlike series or object values) TIME! values are represented "directly", rather than "indirectly" (by reference). Evaluating b: a causes B to be set to a copy of whatever A is currently set to. In this case, it is the entire TIME! value. It is important to realize that exactly the same meaning applies in this case:
>> a: [21 18 17] == [21 18 17] >> b: a == [21 18 17] >> a/2: 28 == [21 28 17] >> b == [21 28 17]
(even though the effect appears to be different), because A is not set to "the series itself", but rather is set to "a reference to the series". Therefore, evaluating b: a causes B to be set to a copy of *the*reference* to which A is currently set.
> That is why I used a different instrument - my own definition > of mutability/immutability for values, a definition helping me > to find out whether a value has/ hasn't been mutated/altered > or not. >
I'm afraid your instrument is the problem. It is testing for mutable-by-a-function instead of simply "mutable". In my world those are not the same thing.
> Here it is: > > Let's have a Rebol function F and a DATE! value VALUE. I tell, > that F can mutate i.e. alter VALUE, if the following > expression yields true: > > do function [ > {does F alter VALUE?} > f [any-function!] > value [date!] > ] [camera picture] [ > camera: func [value [date!]] [ > reduce [ > type? value > value/1 > value/2 > value/3 > value/4 > value/5 > ] > ] > picture: camera value > (f value) > not equal? picture camera value > ] :f :value > > Moreover, I call a DATE! value immutable, if there isn't a > function F able to mutate it. >
There's the problem.
> In this sense all DATE! values are immutable. Just an > illustration: > > f: func [value [date!]] [ > either value/1 = 2000 [ > value/1: 2001 > ] [value/2: 2000]
; Here I assume you meant [value/1: 2000] but that's not ; the problem
> ] > value: now > > the result we get is: > > == false >
Of course we do! REBOL passes arguments using what is normally termed "call-by-value". That means that F gets (and operates on) a copy of VALUE, which leaves VALUE unaffected. I believe that your definition/test conflates two issues: 1) Whether DATE! values can be mutated, and 2) Whether we can pass a "reference" to a DATE! value as an argument to a function. If (as I believe to be the case) DATE! values are mutable, but are not represented via references, then the answer to (1) is yes but the answer to (2) is "no". Therefore your test will always return FALSE for functions that take DATE! arguments. Back when I was teaching comparative programming languages, one of the standard "booby-trap" questions was this: If a programming language (e.g., C) only provides call-by-value argument passing, how can you write a function that modifies a data structure that is external to that function? The simplest answer, of course, is to write a function whose (by-value) argument is a pointer to the data structure. (One could also use a handle, etc...) How can we get that effect in REBOL? Let me first restate the model from an earlier post in this thread: ============================================================= Class of data SAME?-ness EQUAL?-ity Values Sample types in class based on based on mutable? ======================== ========== ========== ======== Atomic scalar data value data value no CHAR!, INTEGER! Composite scalar data value data value yes, via DATE!, TIME! refinemts Built-in reference reference content yes STRING!, BLOCK! comparison comparison User-defined reference reference unsupported yes OBJECT! comparison (always false) ============================================================= Combining the above remarks, the quoted model, and Holger's remarks of this afternoon, we get "direct" = "scalar" = "simple" = "small" in contrast with "indirect" = "reference" = "more complex" = "variable size" When we pass a DATE! or TIME! to a function, we're actually passing a copy of the value, because those data types are in the direct/scalar/simple/small category. When we pass a series or object to a function, we're actually passing a (copy of a) *reference* to that series or object. I shudder to say it, but essentially we're "getting the pointer for free". That's why your test always seems to fail. DATE! and TIME! values are mutable, but passing one of those types to a function will cause copying to occur *before* the function can perform any mutation, and the mutation is made upon the copy. The only ways around this are: 1) to construct a "pseudo-reference" by embedding the date in a reference-type value, and then passing that to the function, or 2) to modify your test to allow an in-line expression instead of insisting on function evaluation only. -jn- ------------------------------------------------------------ Programming languages: compact, powerful, simple ... Pick any two! joel'dot'neely'at'fedex'dot'com