Skip to content

Selection, Reflection, Accessors, and Type Queries

Gregg Irwin edited this page Jan 17, 2024 · 2 revisions

Red is flexible. Unlike some languages where there’s a single way to do something, Red allows you to use different ways to express your intent in the way you think best for a given context. This makes for a constant struggle against consistency and the value that provides. Not only that, but where some systems have a single way to access values, and all queries for attributes, facets, sub-values, or meta information must use that channel, Red does not. From a user’s perspective, there is no distinction between a datatype’s action being called directly, a native!, a function! (which may wrap either), or even user-defined op!`s. Then there are general selectors and paths, where there is no difference in how you `select values from a series where keys are entirely data-defined and path accessors that support only a fixed set of attributes to choose from. Even the words we use to describe those vary by context. For example, when talking about face! objects we call them "facets", but don’t use that term for object "words", map "keys", pick "indexes", or reflection "fields".

Selection

Select

Pick

Pick is an action, generally used against series values and with numeric indexes, but each type can support its own rules about what values may be selected. For example, date! values are not series, but support ordinal accessors that can be used with pick or via path notation.

Reflection

Reflection provides details or meta information about a value, which is separate from its "data space" (for lack of a better term). Here’s an example:

>> o: object [body: [body block]]
== make object! [
    body: [body block]
]
>> body-of o
== [body: [body block]]
>> o/body
== [body block]

Reflect

Reflect is an action, so each type can support its own rules about what values may be selected and by what names.

Standard reflectors

These use reflect internally, simply wrapping the field (value) to select in function form.

   body-of         function!     Returns the body of a value that supports reflection.
   class-of        function!     Returns the class ID of an object.
   keys-of         function!     Returns the list of words of a value that supports reflection.
   reflect         action!       Returns internal details about a value via reflection.
   spec-of         function!     Returns the spec of a value that supports reflection.
   values-of       function!     Returns the list of values of a value that supports reflection.
   words-of        function!     Returns the list of words of a value that supports reflection.

Copied results or not

Reflectors may return a copy, or newly built result, for a value. Others return a reference, whose modifications may affect the value.

Type → Reflector body-of class-of keys-of spec-of values-of words-of

object!

copied

n/a

copied

n/a

copied

copied

map!

copied

n/a

copied

n/a

copied

copied

function!

not copied

n/a

n/a

not copied

n/a

not copied

Accessors

system/catalog/accessors

Naming

  • consistency, context, and completeness

  • ? vs -of suffix

  • avoiding name conflicts

  • of op! (incl. Boris' examples)

Chat Notes

I agree that we shouldn’t have to do textual analysis. My point in starting the wiki page is to lay out the current state of affairs, and create docs as we reason about changes. For example, can we agree that we should not strive for 100% consistency?

m: USD$100 m/currency == 'USD ; R2 used FIRST and SECOND for money parts currency-of m == 'USD Currency-of seems OK, but what about [x-of y-of user-of julian-of away?-of …​]? What are the use cases where path notation doesn’t suffice? Note that reflection (as it stands now) is used for information you can’t get that way. It’s internal, and read-only. The currency of a money value is just a component value.

Now, say we go for consistency, even if some names seem so bad that I imagine they’d never be used. What about type-of for event!? Do we go for consistency and change type? for type checking, or use it for property access? We can’t have both. That’s a case we know about today, but once we lay down hard and fast rules about naming=semantics and "all accessors must have an associated function" Red becomes much more strict, and a victim of its own rules if we want to choose what to include. Thanks to Vladimir for linking to https://wiki.c2.com/?BondageAndDisciplineLanguage recently.

That said, if we think only in terms of datatypes, it shouldn’t get too out of hand, right? Wrong. No matter how much we tell people not to, they will create new datatypes for their own needs. And that’s OK. A good thing even.

Remember that I’m thinking through this along with you. Today we have a few ways to get at things (see the new wiki page). The small number of general interfaces [select pick reflect], along with type specific accessors avoid global naming conflicts. The more names we make global, and the more properties there are to access, the more confusion and conflict there will be. So we should strive to reduce conflict (both mentally and technically), and only adding things that address real pain points, which may mean being patient and waiting for people to feel a pain.

Clone this wiki locally