User-Defined Types in R3-Alpha and Red

R3-Alpha user defined types (UTYPE!) were vaporware

Prior to R3-Alpha's open sourcing, some of the loyal followers of Rebol were excited to hear that there was a plan for user-defined types.

Once it was released, it could be seen that all that existed of user-defined datatypes in R3-Alpha was a cell payload definition ("REBUDT"), which was essentially two objects--the content (data/methods) and an additional object. Then there was about a paragraph's worth of code.

The code doesn't appear to even be trying to work. :-/ But the gist is that you'd provide functions for each "type action". A prototype object with a NONE! for each action name was initialized in Init_Utype_Proto(), and your user defined type would run through the object creation process inheriting from that.

So maybe you'd say something like:

matrix: make utype! compose/deep [
   [append: (func [matrix value] [print "this gets called on append"])] ;-- spec part
   [rows: 2 columns: 2 data: [[0 0] [0 0]]] ;-- data part
append matrix [10 10] ;-- perhaps should add a row to the matrix?  :-/

But even if that worked, there's pretty much nothing but unanswered questions.

  • There was a stub for where molding/forming/comparison would provide hooks, but those stubs were empty.

  • How would path dispatch work?

  • Does every user-defined datatype report its TYPE? as UTYPE!...and implementers are responsible for doing some finer-grained type checking?

So nothing to see, there.

Red recognizes methods with certain names

When it comes to being able to "hook" a datatype, Red has at least one example mentioned when they added object support. It was set up so that if you had a method called on-change* it would call it when the object changed. Here's an example from their page:

book: object [
    title: author: year: none

    on-change*: func [word old new /local msg][
        if all [
            word = 'year
            msg: case [
                new >  2014 ["space-time anomaly detected!"]
                new < -3000 ["papyrus scrolls not allowed!"]
           print ["Error:" msg]

This doesn't put the specially dispatched methods into another namespace (e.g. the "second object" of the UTYPE!'s REBUDT cell). This puts them in-band. So you couldn't have an object with field called on-change* that was just a regular data member.

I don't know how many of these they're planning on adding, but the idea of reserving names in the space where ordinary data fields live seems suspect to me. It might seem harmless as there's no reason to have a plain data member called on-change* at the outset. But whenever you introduce a meta thing like this you wind up wanting to make other objects that mention it, so on-change*: true can become interesting when making something that lists the meta methods something has--for example.

Ren-C added out-of-band ADJUNCT feature

R3-Alpha's MODULE! data type was very much like an OBJECT!, but instead of just having one set of keys and values it had two.

  • The first was the module contents

  • The second set of information held things like the module's title, list of exports...basically anything that it was keeping track of that it got out of the header. But by putting it in a separate place, it wasn't stealing any potential key names from the module's content. (e.g. if you put it in a field called "header", you couldn't have a top-level declaration in the module body called "header")

In Ren-C, this module-only ability was turned into something more generalized, which allowed an ANY-CONTEXT! to have an associated "adjunct" context. So this applied not just to MODULE!, but to OBJECT!, ERROR!, PORT!, and FRAME!. It seemed like a step in the right direction.

Could the ADJUNCT context store type information?

The thing that the meta object has in common with UTYPE! is that it's a second context associated with an ANY-CONTEXT!, whose fields don't interfere. Should it be the place to look for methods the system might call, such as on-change*...or something for path dispatch, like on-path*?

This also raises some questions about how meta information should be handled in terms of COPY. Does copying an object give you the same meta information, a copy of the meta information (deep? shallow?) or does the new object not have any meta information at all? Most of those questions got punted on, but could be informed by what would be needed for the type handling.

Solving user-defined types is a big area, given that there really is no precedent in Rebol's class of languages. We don't know how to say b: make book! [author: {...} isbn: #...] and then say type of b and get BOOK! back, or make a function like checkout: function [b [book!]] [...]. But neither does JavaScript (its JavaScript typeof only has 9 possibilities). Nor does Ruby or Python.

Extension Types Were Briefly Added to Ren-C

The type system wasn't mature enough to handle extension types, so they were removed in order to do more foundational work.

But something like them may be added back:

"Extension Types" Implementation (On Hold)