metaeducation / rebol-issues

6 stars 1 forks source link

SET/CASE and GET/CASE for case-sensitive selection in MAP! and OBJECT! #2172

Open rebolbot opened 10 years ago

rebolbot commented 10 years ago

Submitted by: fork

The nature of OBJECT! vs. MAP! has often been questioned, as many dynamic languages (such as JavaScript) have a single key/value store that is used for both purposes. This brings to the table the question of how the two are different--or should be different--at both an interface and implementation level.

The desire of case-sensitivity in MAP! has brought to the forefront an issue to look at. I will summarize an explanation I gave in chat of the design space here:

A general rule of Rebol is that case is preserved, but not significant unless a lookup requests it.

>> blk: ["A" 1 "a" 2]
>> select blk "a"
== 1
>> select/case blk "a"
== 2

This applies whether you are using strings or words:

>> blk: [A 1 a 2]
>> select blk 'a
== 1
>> select/case blk 'a
== 2

With objects, however, assignments run in sequence:

>> select object [A: 1 a: 2] 'a
== 2
>> select/case object [A: 1 a: 2] 'a
== 2

This is in accordance with assignments through set-word!, which in Rebol do not have a way to set with case. The default evaluator ignores case, and you cannot override it with a /CASE refinement on GET or SET:

>> A: 1
== 1
>> a: 2
== 2
>> print a
== 2
>> print A
== 2

But imagine that there existed a /CASE refinement on OBJECT, MAP, SET and GET. It would be theoretically possible for OBJECT and MAP (a.k.a. MAKE OBJECT! and MAKE MAP!) to store information in a case-sensitive way...accounting for duplicates, and then retrieve it by default in a case-insensitive way, with an override if you wanted case-sensitivity.

Unlike blocks, there is not a linear order to determine who would be the "winner" in a case-insensitive read or write to an object. If it is storing values for both the key a and A then which should obj/a or obj/A overwrite?

What you'd need would be to basically have a list of the case-equivalent written variants. Each SET/CASE call would move the case instance you used last to the top of the list as the next one to be read by an un-cased GET. If you were to ever use a SET without a /CASE it would clear out the list and replace it with a single entry of that casing.

>> obj: object/case [XyZ: 100 xYz: 200]
>> print obj/xyz
200
>> print get/case 'obj/XyZ
100
>> set/case 'obj/AbC 10
>> set/case 'obj/aBc 20
>> print obj/abc
20
>> print get/case 'obj/AbC
10
>> obj/abc: 3
>> print get/case 'obj/AbC
** Script error: cannot access AbC in path obj/AbC
>> print obj/AbC
3
>> print get/case 'obj/abc
3

While this may seem weird for OBJECT!, it is pretty much what you need to get the behavior working for MAP! that people are asking for. MAP/CASE would need to be pretty much the same to make sense.

CC - Data [ Version: r3 master Type: Wish Platform: All Category: Datatype Reproduce: Always Fixed-in:none ]

rebolbot commented 10 years ago

Submitted by: BrianH

This was proposed before for the map! type, in #1315, #1437, and with #1774 as a counter-proposal. You can look there for the issues involved with having a modal type with no way to specify the mode in syntax or reflection.

It comes down to not having a way to indicate the mode, since the spec block is already a block of arbitrary data. #1774 was the only workable solution for the map! type - to have another type that's more strict (and hopefully more compatible with JSON semantics). For the object! type, we'd have to finally implement the object headers that we've suggested over the years, but never had a syntax for (not been discussed yet, and backwards-incompatible).

As it is, this proposal has the same problems that similar proposals had in the past.