REBOL3 tracker
  0.9.12 beta
Ticket #0002180 User: anonymous

Project:

Previous Next
rss
TypeIssue Statussubmitted Date19-Oct-2014 17:46
Versionr3 master CategoryNative Submitted byfork
PlatformAll Severitymajor Prioritynormal

Summary Semantics of "to-end-of-series" vs /PART for COPY, SWAP, TAKE, etc.
Description (Found unfinished writing on this subject. Thought pasting it here would be less of wasteful than writing it in Cuneiform and sending it on an exoplanetary satellite. I'm hedging my bets and doing both. Note it is tagged "issue" and not "wish" or "bug", hence it is "thing I was thinking about" without a prescribed answer to be thumbed up or thumbed down.)

---

@DarrelBrodgon asked in SO chat how to modify a series temporarily and then put it back. I suggested copying to a backup, and then using CHANGE to put it back...

repo-name: "foo/bar"
repo-name-copy: copy repo-name
replace repo-name {/} {:}
print repo-name
change repo-name repo-name-copy
print repo-name

My first instinct was to look for a SWAP to do this. But the behavior of SWAP vs. CHANGE led me to question what the overarching semantics are of how such generic words are used.

Some (like COPY) operate on series "up to the end" by default...then that behavior is modified by /PART. Both COPY and COPY/PART, when given a series as input, produce a series. The /PART just potentially limits how far it goes.

Others, like TAKE, carry an implication of a single element. Then a /PART refines it to return a series.

I started to wonder... what is it about the word TAKE vs the word COPY that would imply whether the end of the series is the default boundary or not? There is a TAKE/LAST but not a COPY/LAST... nor a TAKE/FIRST or COPY/FIRST. What is it--as a user--that should cue you to imagine TAKE and COPY would be different?

What I started thinking was that in the series "worldview" of Rebol, a generic word that would have meaning for a range (opposed to, let's say, FIRST) would mean "up to end of range for series input". So TAKE would be like COPY in that respect:

>> foo: [a b c d e]

>> take/first foo
== a

>>take/part foo 2
== [b c]

>> probe foo
== [d e]

>> bar: take foo
== [d e]

>> probe foo
== []

>> probe bar
== [d e]

Although `COPY/PART series 1` and `COPY/FIRST series` would be the same in such a paradigm, and same for take, I think it wouldn't hurt to have the /FIRST refinement for both TAKE and COPY.

I also noticed that other languages think themselves clever when they do things like use negative numbers to refer to coming from the tail instead of the head. Though with the type-based versatility, it's hard to really cohere the logic of series *positions* with usage to /PART. You can't sign a series *position*, only an index. This suggests that perhaps /PART could be split into /FRONT and /BACK.

>> foo: [a b c d e f]

>> copy/front foo (next foo)
== [a]

>> copy/back foo (next foo)
== [c d e f]

An interesting consideration, but as this was motivated in the first place by SWAP, that throws a bit of a wrench into any plan. Let's imagine that one wants to argue this behavior:

>> foo: [a b c]

>> bar: [d e f]

>> swap foo bar

>> probe foo
[d e f]

>> probe bar
[a b c]

That would bring SWAP into alignment with the other routines. But SWAP involves "two sources". The prescriptions don't work; how would you swap the first element of one series with the last of another? Imagine that both COPY and TAKE had chosen to have /FIRST and /LAST refinements; they have meaning there. But here, the refinements would have to play favorites. There is no single input or output as both are inputs and outputs.

One possibility would be that if you don't want to swap both series contents up to the end, that the refinements would pick one series (presumably the first input) as the bias to apply the refinements to, while the other series is implied to be to the end.

>> foo: [a b c]

>> bar: [d e f]

>> swap/first foo bar

>> probe foo
[d e f b c]

>> probe bar
[a]

That's fairly semantically consistent, but you lose the ability (for instance) to swap single elements between series, which is all you can do with the current swap.

I suppose the argument I might make is that swapping single elements between series is not a particularly interesting native, relatively speaking. In fact, it can be efficiently implemented without native support; while this definition of SWAP cannot.

>> swap-single-element: function [a [series!] b [series!]] [temp: a/1 a/1: b/1 b/1: temp]

>> foo: [a b c]

>> bar: [d e f]

>> swap-single-element foo bar

>> probe foo
[d b c]

>> bar
[a e f]

So the "issue" I'm raising is: if you're going to offer something called SWAP a native, why not make it:

* consistent
* useful in a way that is non-trivial to write outside the interpreter
Example code

			

Assigned ton/a Fixed in- Last Update31-Oct-2014 02:32


Comments
(0004522)
johnk
19-Oct-2014 19:17

copy/front and copy/back are nice concepts, but we need to think more about the words as back has a well defined meaning for series.
One option could be copy/pre and copy/post.
I haven't used take much, but if I recall correctly it is an r3 addition so hopefully would have less impact if we wanted to change behaviour.
(0004536)
fork
31-Oct-2014 02:32

Hrrrm, yes you're right that there's an asymmetry there. NEXT and BACK are complements, not FRONT and BACK. (Presumably because "PREV" was considered not aesthetically literate, and "PREVIOUS" too long a word.) The crazy design intersection under the crazy space of English...forward and backward, next and previous, etc.

Yet something having a well defined meaning doesn't make it bad. How about COPY/HEAD and COPY/TAIL?

USAGE:
COPY value /head last /tail first /deep

DESCRIPTION:
Returns a copy of a value.
COPY is an action value.

ARGUMENTS:
value -- Usually a series (Type: series port bitset)

REFINEMENTS:
/head -- Copies the series from the head to a given position
last -- Last position to include in the copy (Type: number series port pair)
/tail -- Copies the series from a position up to the tail
first -- First position to include in the copy (Type: number series port pair)
/deep -- Also copies series values within the block.

(Note: "value -- usually a series"? :-/ That's one of those "could use some work" documentation strings.)

In the spirit of "not having incompatible refinements" it isn't necessarily the case that /head and /tail be exclusive.

>> copy/head/tail "abcdefghi" 2 4
== "abfghi"

Although if these were truly independent you could wind up just duplicating something twice.

>> foo: "abcdefghi"

>> copy/tail/head foo (head foo) (tail foo)
== "abcdefghiabcdefghi"

Seems like it could be useful, and COPY/HEAD is actually a bit more clear than COPY/PART. If I say COPY/HEAD "abcdef" 3 then it seems more clear than COPY/PART "abcdef" 3

I'll randomly remark on my leaning of support that /DEEP really seems like it should be the default, and should be safer than it is.

Date User Field Action Change
31-Oct-2014 02:51 fork Comment : 0004536 Modified -
31-Oct-2014 02:35 fork Comment : 0004536 Modified -
31-Oct-2014 02:32 fork Comment : 0004536 Added -
19-Oct-2014 19:17 johnk Comment : 0004522 Added -
19-Oct-2014 17:46 Fork Ticket Added -