SETIFY, PLAINIFY, GETIFY, SYMIFY... or MORPH ?

There are historical cases where you have to navigate a situation like:

"I have either a foo or a foo/bar and I want a foo: or a foo/bar: as appropriate."

You can't exactly classify this in the type system. There's no to set! value that can do the right thing generically for WORD! and ANY-WORD!

This has gotten to be more of an issue, now that there are more such types. This has led to a menagerie of morphing operations:

>> setify 'foo
== foo:

>> setify 'foo/bar
== foo/bar:

>> plainify @[foo bar]
== [foo bar]

>> getify '(foo bar):
== :(foo bar)

>> symify 'foo/bar
== @foo/bar

The names aren't so hot. But the functionality is important.

This kind of made me wonder if there might be a more clever operation, using quoting and driven by example:

 >> value: the foo/bar

 >> morph (value):
 == foo/bar:

 >> morph @(second [foo bar])
 == @bar

 >> value: '[foo bar]:
 >> morph (value)  ; let no decoration on the group mean "plainify"
 == [foo bar]

You could technically make it more minimal than that...by fetching the word:

 >> value: the foo/bar

 >> morph value:
 == foo/bar

There'd still have to be an escaping mechanism anyway, and I think it kind of scans better to have the parentheses in there.

Requiring a quote mark at the callsite might help hint that it's not running normal evaluation:

 >> value: the foo/bar

 >> morph '(value):
 == foo/bar:

But it needs to be a quoting function..we want the visual marker to be at the callsite:

 >> value: the foo/bar

 >> indirect: '(value):

 >> morph indirect  ; this defeats the whole point of the idea
 == foo/bar:

Given that it has to quote anyway, people who know how to use morph would probably be more annoyed by the quote tic than feel informed by it. (Would you like it if THE (e.g. Rebol2 QUOTE) required you to put a quote mark on its argument, just to emphasize that it wasn't evaluated...or is it better to just know what it means and have less typing and characters?)

Also it might be nice if the ticks let you set the quoting level on the output:

 >> value: [foo bar]

 >> morph '(value):
 == '[foo bar]:

So I think my suggestion is always have a GROUP! to have some container separation from the evaluation, don't have a tick mark unless you want the result quoted. And MORPH seems like an okay-enough name.

But the quoting does mean it breaks the flow of evaluation, and forces you to put things in a GROUP! when you might not want to. Does this mean PLAINIFY and friends are still necessary?

Another option to add here would be to morph the basic container type. This is done less often. I don't know exactly what you would call that:

>> value: the [a b]:

>> morph/class (value)
== (a b):

>> morph/class /value
== a/b:

Fitting both isn't actually that interesting:

 >> morph/class/sigil @.value
 @a.b

...because the whole point is preservation of some information in the original. You could have thrown all the original information out nearly as easily with:

 >> as sym-block! as tuple! value
 == @a.b

But, it's not useless. Anyway, I'm sure there are more creative or less creative ideas out there. If the morphed value is always a GROUP!, there could be a skippable element in the middle:

 >> value: the [a b]:

 >> morph @(value)
 == @[a b]

 >> morph tuple! @(value)
 == @a.b

That's less puzzling than morph/class/sigil @.value. So the remaining question might be how to get it to morph to a tuple while leaving the sigil alone more cleanly. Quote marks might be used for this, meaning "leave it alone, don't plainify it"

 >> value: the [a b]:

 >> morph tuple! (value)
 == a.b

 >> morph tuple! '(value)
 == a.b:

Seems a waste of being able to indicate how quoted you want the result. Maybe this could be what BLOCK! does.

 >> value: the [a b]:

 >> morph tuple! [value]
 == a.b:

Another option would be that single group leaves it alone, and double group removes any sigil:

 >> value: the [a b]:

 >> morph (value)
 == [a b]:

 >> morph ((value))
 == [a b]

 >> morph tuple! (value)
 == a.b:

 >> morph tuple! (value)
 == a.b

That's not what COMPOSE does with (( )), so it should probably be avoided. I dunno. Actually, keeping compatibility with COMPOSE and making ( ) do nothing and the BLOCK! plainify would be more consistent...

 >> value: the [a b]:

 >> morph tuple! [value]
 == a.b

 >> morph tuple! (value)
 == a.b:

It's just not an option in COMPOSE since it has only one type to work with. Oh well. That makes morph (value) a no-op... so it should probably just error and point you to the right thing to do. Though maybe if you have some constructed code case that might have N quotes on it, then this could be a base case that you want as a no-op... hmmm.

Anyway...there's a very weird function proposal that could come in handy...I'll try it out for a while and see what I think.

1 Like

Three years later, COMPOSE no longer has any specific meaning for ((...)) slots, they act the same as (...). (In the pre-isotopic era when SPREAD didn't exist, there was a period where doubled groups were used to mark slots that should splice blocks or groups, while single groups composed them as-is.)

So arguably, the MORPH operation could be defined as doing the same thing that COMPOSE does with its slots, and include this for plainify.

>> set-word: 'foo:

>> compose [bar: '((set-word))]
== [bar: 'foo]

>> morph '((set-word))
== 'foo

Could work. I definitely like the idea that the code would be common, so whatever that COMPOSE is doing is factored out into a reusable operation.

Perhaps for safety, the (( )) to plainify should be required if the type is not already plain?

>> word: 'bar

>> morph :(word)
== :bar

>> morph :(set-word)
** Error: Must use (( )) to remove sigil in MORPH

>> morph :((set-word))
== :foo

I don't know if that's an interesting axis of protection or not.

A new proposal is to make : & $ ^ their own sigil datatypes, with special consideration for :: as "the sigil of a SET-WORD!".

That offers a kind of clean path toward generalizing this:

>> morph '$ 'var
== $var

 >> morph ':: 'var
 == var:

 >> morph void '$var  ; maybe morph '_
 == var

If you mention array/sequence types, it would be assumed the thing you were morphing was also an array/sequence.

 >> morph '[]: '$(a b c)
 == [a b c]:

 >> morph '$. 'a/b/c
 == $a.b.c

 >> morph ':: 'a/b/c
 == a/b/c:

 >> morph '[]: 'a
 ** Error: a is not an any-array type, can't make it []

Maybe it would be useful if it recursed if you had nestings of single items:

 >> morph '[({})]: '${[(a b c)]}
 == [({a b c})]:

Or maybe not. Anyway, the general concept seems nice and orthogonal.

And importantly, it means you can do things like take a sigil off of one thing and put it onto another one.

  morph (sigil of path-or-word-etc) other-path-or-word-etc