The set (collection) operations in Rebol are useful.
rebol2>> set1: [a b c]
rebol2>> set2: [b c d]
rebol2>> intersect set1 set2
== [b c]
When I first encountered it, I thought it strange that it wasn't mutating the first argument.
rebol2>> set1
== [a b c] ; not modified by INTERSECT
Everywhere else in Rebol it seemed when you had a verb like that (APPEND, REVERSE, etc.) it was modifying. What was special about INTERSECT that it didn't?
Anyway, that was just the first thing I noticed. But digging around in the code there were questions... such as, why should it only take block lists?
rebol2>> intersect [a b c] quote (b c d)
** Script Error: Expected one of: block! - not: paren!
red>> intersect [a b c] quote (b c d)
*** Script Error: intersect does not allow paren! for its set2 argument
I raised the question to @BlackATTR who suggested that maybe single elements should just go in the list, as whole items:
>> intersect [a b c] '(b c d)
== []
>> union [a b c] '(b c d)
== [a b c (b c d)]
And then I noticed... splices could draw the distinction!
>> union [a b c] [b c d]
== [a b c [b c d]]
>> union [a b c] spread [b c d]
== [a b c d]
>> union [a b c] spread '(b c d)
== [a b c d]
>> union '(a b c) spread [b c d]
== (a b c d)
This gives you the power to easily do set operations with single elements, and splices with SPREAD dispel the type information so there's no question what the return type should be: the type of the first set!
And I think to be consistent with the rest of the language, the operations should modify the first set argument by default. But if you use the OF operations you get a copy.
Then, the OF operations might have different parts of speech:
intersection of set1 set2
=> intersect (copy set1) set2
union of set1 set2
=> unite (copy set1) set2
This would open up things like union and intersection to be nouns.
union: union of set1 set2
This seems to me to be much better and a lot more consistent!