REBOL 3.0

Comments on: Modules: formal specification of word attributes

Carl Sassenrath, CTO
REBOL Technologies
31-Aug-2006 19:12 GMT

Article #0042
Main page || Index || Prior Article [0041] || Next Article [0043] || 17 Comments || Send feedback

Similar to script files and ordinary functions, REBOL modules provide a formal specification. It takes the form of a block at the head of the module body. Both these forms are allowed:

REBOL [spec]
body

Note that this is an extension of the script header. That is intentional. Modules can be scripts.

The dynamic make method is:

mod: make module! [
    [spec]
    body
]

The formal spec provides a lot of information and control over the module (title, name, version, author, needs, etc.) similar to script headers. This is too large of a topic to cover here, nor do I want to make this article that complex. (I will provide details soon enough.)

Instead, I want to focus on specific issues related to module context word (variable) attributes.

First, you should know that a module word can be:

  1. Commented (like functions)

  2. Restricted to a specific datatype. E.g. integer only.

  3. Local or exported. E.g. local words cannot be accessed externally.

  4. Set, get, and do protected. E.g. you can protect words in various ways.

  5. Set by an accessor function (possibly).

Each of these is a separate dimension of specification.

The obvious form for word specification is to duplicate that of function specification. That makes it easy to remember. For example:

words: [
    user "Name of user" [string!]
    pass "User password hash" [binary!]
]

That satisfies #1 and #2 dimensions above.

The question is how to specify LOCAL, EXPORT, and protections. On possibility is to integrate that into the above block:

words: [
    user "Name of user" [export string!]
    pass "User password hash" [local binary!]
]

Another approach is to split the locals from the exports:

export: [
    user "Name of user" [string!]
]
local: [
    pass "User password hash" [binary!]
]

This has the advantage of making it clear what is exported and what is not. (And, I prefer this form.)

Also note, that default rules can apply. For instance, if an export block is provided, it can be assumed that all module words not found in the export block are local only. The inverse is also possible.

And finally, there is the get, set, and do protection attributes. A possibility is to use this approach:

export: [
    user "Name of user" [get string!]
    key  "Encryption key" [set string!]
]

This allows external code to get, but not set the user field, and set but not get the key field (e.g. it is an encryption key that you want to protect).

Then, there is the issue of the DO attribute. It only makes sense for computational datatypes, e.g. functions. So, is DO really necessary? Or, should it be possible to allow DO, but not allow GET (source for the function)?

So, there are some things to think about. For now, I'm going to ignore the word protection attributes. We can return to those later once we have a running alpha for us all to "play with".

17 Comments

Comments:

Brian Hawley
31-Aug-2006 16:11:36
All right, here's a couple questions:

Can you specify different access protections to local and exported usage of the same word? If so, how would you phrase it in the spec syntax? Your preferred 2-block spec syntax would seem best for this, but what happens when the data type blocks and documentation is different?

DO constraints on words don't seem like they would work if functions are still values. These word attributes affect the word, not the value associated with them. It seems to me that a setting preventing someone from retrieving the source block of a function should be applied to the function at the time it is made, rather than to some random word that may have the function value assigned to it at some point.

As an aside, this is the first time I have seen the argument documentation comment strings put before the type specification, rather than after it. This works in REBOL 2 as well. Strange that I have never seen this done before.

Carl Sassenrath
31-Aug-2006 16:59:02
I considered it, but concluded that local protection was not that meaningful. Within the body context of the module, you can do what you want. So, the protection only applied externally.

When a word is doubly specified (both export and local), we can either generate an error (like a functions specs) or assume it is exported (because it would be local also, by default). I prefer the error.

WRT to DO, yes, functions are still values. You can either get them or do them. The reference can be by name, by context index, or by returning the value from another function. The first is protected by DO, the second would be protected by GET, and the third would be either a coding error or a feature, depending on how you look at it.

If another function of the module exports an internal function by value, that is a violation of context security, and all bets would be off. I'm not sure if we want to explicitly protect this case. (And, they may already be protected by R3.0's function body export rules). But, this is also a deeper issue that deserves its own discussion (e.g. what happens to exported module bindings). Let's not go there in this thread.

-pekr-
31-Aug-2006 17:04:43
Brian, interesting thoughts. With object language we used, IIRC we could specify export, local, private variables for each class. The same went for module IIRC, but maybe I am wrong.

With Rebol, the idea is interesting, and one has to think properly, as things could start complicate easily. But really - Rebol uses repetitive patterns. The basic idea is that of - "block". Function, object, just sets of blocks tied together semantically. So - Brian can be right, that it could/should(?) be an option to provide such extensive specs to functions themselves too, but maybe it is not necessary, dunno.

But let's be carefull - we already use separate block for locals ('function), /local ('func) for the same thing, and modules will use yet another syntax?

... just a brainstorming of semi-reboller :-)

-pekr-

Carl Sassenrath
31-Aug-2006 17:07:30
Good point Pekr. The /local option is another approach worth considering.
Brian Wisti
31-Aug-2006 19:28:37
For accessors, what about something similar to Ruby's handling of such details?

readers: [
    user "Name of user" [string!]
]
writers: [
    key  "Name of user" [string!]
]
methods: [
    rename "Change user name"
]
accessors: [
    description "Description of user" [string!]
]

readers, writers, methods, and accessors would all be exported. They just describe various limitations on the basic raw 'export.

  • export: export words and give them listed privileges or all privileges if no privileges are specified
  • readers: export words but only give them 'get privilege
  • writers: export words but only give them 'set privilege
  • methods: export words but only give them 'do privilege
  • accessors: export words but only give them 'get and 'set privilege
It's just a stray thought, and might be more useful in an class context. I also can't help but think my suggested names could be improved.
Brian Hawley
31-Aug-2006 21:39:25
OK, that interpretation of the DO attribute sounds good to me, Carl.

It seems that a DO attribute would then be a subset of the GET attribute, because once you get a function value you can execute it at your convenience. GET would imply DO, but not vice-versa. GET functions would be first-class, able to be assigned and used as parameters, while DO functions could only be applied.

I can't think of a security reason right now to keep people from getting function values, particularly if you could restrict access to their source in some other way if you need to. It would make it easier to implement module unloading I suppose.

The distinction between GET and DO could help a great deal with optimization though. If you know that the function won't change and won't be used by metafunctions, it could be inlined, specialized or otherwise optimised. Changes within the module could be determined statically as long as changes outside the module can be controlled.

So given all that, I am very much in favor of a DO attribute on exported words.

Brian Hawley
31-Aug-2006 21:48:05
If you do have the exports and locals specified seperately (which I am also in favor of), it would be good for locals to not be shown when the module header is viewed from the outside. For that matter, it makes me a little uncomfortable at times that you can currently pass local variables as parameters to a function - it would be nice if locals were really local, no matter how they are specified.

The question I have about locals is: How do specified locals interact with adhoc locals that are just assigned within the module?

Brian Hawley
31-Aug-2006 22:00:10
I think it would be an interesting idea to extend the GET/SET/DO attributes on exported words with support for accessor functions. You could specify something like this:

export: [
    x [string! get: get-x set: set-x]
]

The above get-x function would return a string, and the set-x function would take a string as an argument and return a string. The regular word get and set attributes would be shorthand for normal REBOL treatment.

Does that sound good? I like the property proposal from earlier and I don't want these export attributes to clash with property accessors.

Robert
1-Sep-2006 0:18:28
I vote for splitting all those "declarative" things into independent blocks. This has several advantages:

1. As Carl said, it's ovious with one view what happens 2. You can cut & paste 3. It's much simpler to handle for generators, dialects etc.

Especially the last point is very important. Rebol is that dynamic, and dialects are Reboler best friends, so let's make the life of dialects and dialect writers as easy as possible.

Maxim Olivier-Adlhoch
1-Sep-2006 1:19:33
I very like Brian's Idea of being able to set an accessor function for set and get.

For example:

Only allow set in certain circumstances:

set-func: func [value][
  either module-port-opened? [
    module-username: value
  ][
    to-error "cannot change user while port is opened"
  ]
]

or copying values crossing the module, if changing them might prove security risks.

get-func: [path][
  if path = 'user  [
    copy module-username
  ]
]

The above prevents the caller, the capacity to 'CHANGE the password to another (note that preventing SET would not have prevented this or some other more complex hole on more complex datatypes like objects or ports).

Being able to specify the accessors, makes a module an instant datatype. especially if you add the option of receiving a path argument on either and allow auto-init and finalization. :-)

Volker
1-Sep-2006 2:11:34
export: [
    user [get] "Name of user" [string!] 
    key  [set] "Name of user" [string!] 
]
Exports are something which must be very clear IMO. It must be obvious with a quick look. Their rights too. The rest is technical stuff. Means i read it the first time carefull, and later have a quick look to check my memories. And that should be fault-resistant.
Carl Sassenrath
1-Sep-2006 2:29:48
Another REBOLish possibility is:

export [
    :user "User name" [string!]  ; for GET only
    key: "User key"[string!]  ; for SET only
]
Carl Sassenrath
1-Sep-2006 2:42:26
Brian, it is probably worth considering the format for accessor functions.

Problem is, do we want those accessor references that deep? Something to think about. Do we want this complexity? (Consider the effects on construction and reflection).

Volker
1-Sep-2006 4:17:56
Good idea to.
export [
    user "User name" [string!]  ; execute only
    'key "User key"[string!]  ; everything
]
?
Brian Hawley
1-Sep-2006 9:57:35
Carl, I think that if you do accessors, the only efficient place to put them is that deep. Feel free to prove me wrong with a better solution though :)

As for the complexity, well you can have accessors or not. If you have them there is less of an impedance mismatch when interoperating with other object models that do, like .NET or ActiveX, a must for Windows. If you don't have accessors you can push their complexity into the REBOL code that we write, like Java does with their component model. There are advantages to both approaches.

I know it seems at odds with the REBOL-only approach that many like, but much as I would like to I can't use REBOL unless it is interoperable with the other technologies I have to use because of the choices of other people. So I'm looking forward to plugins and custom types, and paying attention to semantic subtleties. (No trolling intended)

Carl Sassenrath
1-Sep-2006 13:55:31
Brian, it is a funny thing: due to the strong influence of Self on REBOL from the start, I've been considering them REBOL since the beginning.

In Self there is no direct variable access. All access is done via functions. (David use to enjoy saying "Self has no variables!").

The problem with accessor functions is really at the meta level. It comes down to how do you define (set, or change) the accessor function.

What I would really is a datatype, similar to function! rather than just an object access method. But, both approaches have their merits and problems.

I'll have to table (defer) this for a while, because the priority is to get R3.0 alpha running well enough to get the rest of the development team at full speed with additional datatypes, plugins, and environments.

Volker
1-Sep-2006 17:30:08
I may miss the point, but: How about a bootstrap-accessor which is installed by default (similar to none for the value), and which can be used to replace itself with another one?

Post a Comment:

You can post a comment here. Keep it on-topic.

Name:

Blog id:

R3-0042


Comment:


 Note: HTML tags allowed for: b i u li ol ul font span div a p br pre tt blockquote
 
 

This is a technical blog related to the above topic. We reserve the right to remove comments that are off-topic, irrelevant links, advertisements, spams, personal attacks, politics, religion, etc.

REBOL 3.0
Updated 26-Apr-2024 - Edit - Copyright REBOL Technologies - REBOL.net