[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