How do I invoke an object function using its string name - rebol

Any idea why the following doesn't work? (R3)
o: make object! [
foo: does [do "bar"]
bar: does [print "hello from bar"]
]
o/foo
** Script error: bar has no value
** Where: catch either -apply- do foo
** Near: catch/quit either var [[do/next data var]] [data]

Try this:
o: make object! [
foo: does [do get bind load "bar" self]
bar: does [print "hello from bar"]
]
o/foo ;this will work
You need that BINDing because your "bar" lives in the object, not in global scope.
Check this out as well:
my-func: does [print "ok"]
o: make object! [
foo: does [do "my-func"]
bar: does [print "hello from bar"]
]
o/foo ;this will work too
You need to LOAD it because it is a string, but it has to be a word to be BINDed.
So these will work too (put them in your object):
do get bind to-word "bar" self
or
do get bind 'bar self

No Scope!!!?
The reason do "self/bar" cannot know where to find 'BAR is because there is no scope in Rebol (not in the traditional CS meaning at least).
Words in Rebol only have meaning once they have been statically bound to a context. This automagically occurs when you 'MAKE an object, so many people don't even realize it even after years of use.
Here are the steps (loosely) when an object (a.k.a. context) is created.
It picks up all the root set words in its spec (in this case [FOO: BAR:] )
Adds them to its current internal words (SELF: by default, more if you are using an object as basis)
Then binds all the words in the block (hierarchicaly) to those set-words it added to its spec.
Executes the block.
So you see, once you execute the block its too late, the words already got assigned their meaning, which allows the interpreter to ask for their values (which could trigger an expression evaluation, hence the E in REBOL).
Global, cause that all there really is once executing.
DO and LOAD cannot automatically bind to anything but the global context... because there is no such thing as the "current context" like you'd have in traditional OOP and imperative languages (remember, no scope). Really, once its executing, that information doesn't exist anymore, unless you bound the "current" context to a word... which is what 'SELF does, but to be bound it has to already be loaded, which, when executing a string, never occured.
clueless functions
I'll finish by adding that it may not be obvious at first sight, but while it was binding the Object spec block, it still didn't know what FOO and BAR really were. in fact, the only way FOO and BAR could access the 'O object, is because their function block, when it was run thru 'MAKE, got bound to the object... yep, before it even knew it was a function. then if the function defined its own locals, it would re-bind its body block to those new locals.. because you guessed it... a function creates its own inner context, which gets the same MAKE treatment (but without the internal SELF word).
I hope this helps clear things in a more obvious light.
here is a proof that code isn't scoped:
a: make object! [
data: "HAHAHAAAAA!!!"
action: does [print self/data]
]
b: make object! [
data: "BUMBLING BEHEMOT"
action: does [print self/data]
]
b/action: get in a 'action
; this will print HAHAHAAAAA!!!
b/action

To explain moliad's answer a bit more, see the following explanation:
REBOL words carry a reference to their context with them. It’s not
where a word is evaluated that makes the difference, but where it’s
declared.
from http://blog.revolucent.net/2009/07/deep-rebol-bindology.html
Here is a very good example:
x: 0
b: [] loop 3 [use [x] [x: random 100 append b 'x]]
;== [x x x] <-- there are three X which looks same words (and they are same actually)
reduce b
;== [95 52 80] <-- but they have different values in their contexts
probe x
;== 0 <-- in global context, it has another value as well
This looks weird at first look, but actually it is not. USE creates a new context each time in the LOOP, we set X (in the context the USE created) to a value, then APPEND the WORD (not the value!) to a block.
All the words that we append to the block carries their own contexts with them, but they look same word.
When we REDUCE (or PRINT etc.), when we GET their values in their own contexts, we see that they all have different values!

Related

Mixing-in roles in traits apparently not working

This example is taken from roast, although it's been there for 8 years:
role doc { has $.doc is rw }
multi trait_mod:<is>(Variable $a, :$docced!) {
$a does doc.new(doc => $docced);
}
my $dog is docced('barks');
say $dog.VAR;
This returns Any, without any kind of role mixed in. There's apparently no way to get to the "doc" part, although the trait does not error. Any idea?
(This answer builds on #guifa's answer and JJ's comment.)
The idiom to use in variable traits is essentially $var.var.VAR.
While that sounds fun when said aloud it also seems crazy. It isn't, but it demands explanation at the very least and perhaps some sort of cognitive/syntactic relief.
Here's the brief version of how to make some sense of it:
$var makes sense as the name of the trait parameter because it's bound to a Variable, a compiler's-eye view of a variable.
.var is needed to access the user's-eye view of a variable given the compiler's-eye view.
If the variable is a Scalar then a .VAR is needed as well to get the variable rather than the value it contains. (It does no harm if it isn't a Scalar.)
Some relief?
I'll explain the above in more detail in a mo, but first, what about some relief?
Perhaps we could introduce a new Variable method that does .var.VAR. But imo this would be a mistake unless the name for the method is so good it essentially eliminates the need for the $var.var.VAR incantation explanation that follows in the next section of this answer.
But I doubt such a name exists. Every name I've come up with makes matters worse in some way. And even if we came up with the perfect name, it would still barely be worth it at best.
I was struck by the complexity of your original example. There's an is trait that calls a does trait. So perhaps there's call for a routine that abstracts both that complexity and the $var.var.VAR. But there are existing ways to reduce that double trait complexity anyway, eg:
role doc[$doc] { has $.doc is rw = $doc}
my $dog does doc['barks'];
say $dog.doc; # barks
A longer explanation of $var.var.VAR
But $v is already a variable. Why so many var and VARs?
Indeed. $v is bound to an instance of the Variable class. Isn't that enough?
No, because a Variable:
Is for storing metadata about a variable while it's being compiled. (Perhaps it should have been called Metadata-About-A-Variable-Being-Compiled? Just kidding. Variable looks nice in trait signatures and changing its name wouldn't stop us needing to use and explain the $var.var.VAR idiom anyway.)
Is not the droid we are looking for. We want a user's-eye view of the variable. One that's been declared and compiled and is then being used as part of user code. (For example, $dog in the line say $dog.... Even if it were BEGIN say $dog..., so it ran at compile-time, $dog would still refer to a symbol that's bound to a user's-eye view container or value. It would not refer to the Variable instance that's only the compiler's-eye view of data related to the variable.)
Makes life easier for the compiler and those writing traits. But it requires that a trait writer accesses the user's-eye view of the variable to access or alter the user's-eye view. The .var attribute of the Variable stores that user's-eye view. (I note the roast test has a .container attribute that you omitted. That's clearly now been renamed .var. My guess is that that's because a variable may be bound to an immutable value rather than a container so the name .container was considered misleading.)
So, how do we arrive at $var.var.VAR?
Let's start with a variant of your original code and then move forward. I'll switch from $dog to #dog and drop the .VAR from the say line:
multi trait_mod:<is>(Variable $a, :$docced!) {
$a does role { has $.doc = $docced }
}
my #dog is docced('barks');
say #dog.doc; # No such method 'doc' for invocant of type 'Array'
This almost works. One tiny change and it works:
multi trait_mod:<is>(Variable $a, :$docced!) {
$a.var does role { has $.doc = $docced }
}
my #dog is docced('barks');
say #dog.doc; # barks
All I've done is add a .var to the ... does role ... line. In your original, that line is modifying the compiler's-eye view of the variable, i.e. the Variable object bound to $a. It doesn't modify the user's-eye view of the variable, i.e. the Array bound to #dog.
As far as I know everything now works correctly for plural containers like arrays and hashes:
#dog[1] = 42;
say #dog; # [(Any) 42]
say #dog.doc; # barks
But when we try it with a Scalar variable:
my $dog is docced('barks');
we get:
Cannot use 'does' operator on a type object Any.
This is because the .var returns whatever it is that the user's-eye view variable usually returns. With an Array you get the Array. But with a Scalar you get the value the Scalar contains. (This is a fundamental aspect of P6. It works great but you have to know it in these sorts of scenarios.)
So to get this to appear to work again we have to add a couple .VAR's as well. For anything other than a Scalar a .VAR is a "no op" so it does no harm to cases other than a Scalar to add it:
multi trait_mod:<is>(Variable $a, :$docced!) {
$a.var.VAR does role { has $.doc = $docced }
}
And now the Scalar case also appears to work:
my $dog is docced('barks');
say $dog.VAR.doc; # barks
(I've had to reintroduce the .VAR in the say line for the same reason I had to add it to the $a.var.VAR ... line.)
If all were well that would be the end of this answer.
A bug
But something is broken. If we'd attempted to initialize the Scalar variable:
my $dog is docced('barks') = 42;
we'd see:
Cannot assign to an immutable value
As #guifa noted, and I stumbled on a while back:
It seems that a Scalar with a mixin no longer successfully functions as a container and the assignment fails. This currently looks to me like a bug.
Not a satisfactory answer but maybe you can progress from it
role doc {
has $.doc is rw;
}
multi trait_mod:<is>(Variable:D $v, :$docced!) {
$v.var.VAR does doc;
$v.var.VAR.doc = $docced;
}
say $dog; # ↪︎ Scalar+{doc}.new(doc => "barks")
say $dog.doc;  # ↪︎ barks
$dog.doc = 'woofs'; #
say $dog; # ↪︎ Scalar+{doc}.new(doc => "woofs")
Unfortunately, there is something off with this, and applying the trait seems to cause the variable to become immutable.

Documenting Rebol's dialect

I can document a function like this:
f: func [
"a description"
arg1 [string!] "a description of an argument 1"
][
arg1
]
I can use ?/help in order to retrieve informations about the the function (a description, a usage, the argument list, a description of each argument and it's type)
? f
USAGE:
F arg1
DESCRIPTION:
a description
F is a function value.
ARGUMENTS:
arg1 -- a description of an argument 1 (Type: string)
I cannot document dialects like this. Is there an automatic way to document dialects (like func does)? Do I have to do this manually?
There's nothing for it currently, but it's a good idea. So good that someone has suggested it before. :-)
Do I have to do this manually?
You can manually write a new generator which defines your "dialect spec" format. Then either do something like give it a HELP command, or extend HELP to recognize it.
Very short example to demonstrate a group of techniques which may come in handy in doing something like this (not all expected to be obvious, rather to hint at the flexibility):
make-dialect: function [spec [block!] body [block!]] [
return function ['arg [block! word!]] compose/deep/only [
case [
arg = 'HELP [
foreach keyword (spec/keywords) [
print [keyword "-" {your help here}]
]
]
block? arg [
do func [arg] (body) arg
]
'default [
print "Unrecognized command. Try HELP."
]
]
]
]
So there's your function that takes a dialect spec and makes a function. Once you've got your generator, using it can be less manual:
mumble: make-dialect [keywords: [foo baz bar]] [
print ["arg is" mold arg]
]
>> mumble help
foo - your help here
baz - your help here
bar - your help here
>> mumble [<some> [dialect] {stuff}]
arg is [<some> [dialect] {stuff}]
The techniques used here are:
Soft Quoting - Usually you would have to say mumble 'help to "quote" the help as a lit-word! to get it to pass the word! to mumble (as opposed to running the default HELP command). But because arg was declared in the generated function as 'arg it was "soft quoted"...this means that words and paths will not be evaluated. (Parens, get-words, and get-paths still will be.) It's a tradeoff because it means that if someone has a variable they want to pass you they have to say :var or (var) as the argument instead of just var (imagine if the block to pass the dialect is in a variable) so you don't necessarily want to use it...but I thought it an interesting demo to make mumble help work without the lit-word!
Deep Composition - The spec and the body variables which are passed to make-dialect only exist as long as make-dialect is running. Once it's over, they'll be gone. So you can't leave those words in the body of the function you are generating. This uses COMPOSE/DEEP to evaluate parens in the body before the function generator runs to make the result, effectively extracting the data for the blocks and stitching them into the function's body structure.
Reusing Function's Binding Work - The generated function has a spec with a parameter arg that didn't exist at the call site of make-dialect. So arg has to be rebound to something, but what? It's possible to do it manually, but one easy way is to let FUNC do the work for you.
Those are some of the techniques that would be used in the proposed solution, which seeks to not only document dialects but provide an easy method by which their keywords might be remapped (e.g. if one's Rebol system has been configured for another spoken language).

How to capture words bound in outer context when creating new context?

Let us say I have a situation like this:
;; Capture whatever the print word pointed to into a variable
outer-print: :print
foo: context [
;; within foo, override print and implement in terms of outer-print
print: func [value] [
outer-print "About to print"
outer-print value
outer-print "Done printing"
]
]
I can do this, or if I have more than one thing I want from the outer context I could capture it explicitly:
;; Capture current context into something called outer
outer: self
foo: context [
;; within foo, override print and implement in terms of outer/print
print: func [value] [
outer/print "About to print"
outer/print value
outer/print "Done printing"
]
]
Is this the right idiom, or is there a better way of doing it? Are there circumstances where this might not give me what I expect?
this is good style, especially the second, which is more flexible as it allows you to mass-effect all uses of the outer print, without any ambiguity. when using direct binding, it may occur that the word outer-print is redefined or the context changes between two calls to make foo [] and in the end, points to two different bindings.
static symbol resolving
For the sake of completeness there is a third alternative which doesn't require any extra words to be setup. I don't have a proper naming for it, feel free to suggest a better title.
This method defies any binding issues down the line because you use the function value directly:
foo: context compose/deep [
;; within foo, override print and implement using native print directly
print: func [value] [
(:print) "About to print"
(:print) value
(:print) "Done printing"
]
]
Now the interesting part is if you SOURCE the inner print function:
>> p: get in foo 'print
>> SOURCE P
== p: func [value][native "About to print" native value native "Done printing"]
see how the native value of print is used directly in the body, instead of a word referring to it.
This is, in fact, probably the closest we can get to some form of compilation in pure REBOL. instead of constantly using symbols to fetch and evaluate, we can simply statically resolve them manually, using reduce or compose as in the above.
pros:
It can never be hi-jacked by some advanced and malicious binding code, i.e. even if there are no direct word bounds to PRINT in ANY and ALL contexts, you still have a direct reference to the original function in your body.
cons:
Its a very static way to code, and isn't very "Rebolish".
The
;; Capture current context into something called outer
comment suggests that you think there is some "current context" in Rebol. That is false. Every word has got its own context. Thus, there are cases when your
outer: self
code doesn't work as you expect. For example, let's suppose that you want to access two variables, 'print and 'set. It is possible for the words to have different "outer" contexts. In that case the trick will be certain to not work for at least one of the words, but it may, in fact, not work for both.

Type declarations on REBOL objects

I know you can type declare arguments and returns on functions
some-func: function [
"some func"
number [ integer! ]
] [
result [ integer! ]
] [
help number
return number
]
some-func 1
some-func "blah"
NUMBER is an integer of value: 1
** Script error: some-func does not allow string! for its number argument
How about object properties though?
o: make object! [
a [string!]
b [integer!]
c [o2]
none
]
o2: make object! [
c [string!]
]
an-object: make o [
a: 3.141
b: "an integer"
c: "blah"
]
help an-object
N-OBJECT is an object of value:
a decimal! 3.141
b string! "an integer"
c string! "blah"
I've seen the type declaration on properties as examples, but is it just for documentation?
This is a really good question, and something I've thought about for years. It turns out that Rebol's internal object storage mechanism can handle this, but there's no way of expressing it in source code. Why not you ask? Here's why:
Rebol currently has the concept of name-value pairs. That's how contexts and objects are expressed. However, it is often desirable for objects to include other information that's not just a name or value. The datatype is a good example. Other examples are comments attached to values, and protections/permissions on values (such as allowing read and write).
So, the problem becomes: how many various features do we want to support in the language syntax, and specifically how would we do that? It gets further complicated by the "optional" characteristic of these features. So, you can't really use positional semantics to describe the object. That means adding a syntactic method, which means adding keywords (because Rebol really tries to avoid punctuation.)
So, as a result, the source form would become fairly verbose, and I think we could question whether it would be worth the benefit we'd get from allowing the feature in the first place.
So, this is a case where the simple principle of Rebol takes precedence over feature creep.
All that said, if you've got an idea for a simple method of doing it, let it be known!
It's just for documentation .. type checking is only done on functions.

Is it really all about message passing in smalltalk

I'm new to smalltalk and I'm impressed with the fact that there are only just 6 keywords in the language (self, super, true, false, nil & thisContext), and how pure it is in having almost everything as message passing, eg. looping using whileTrue, if/else using ifTrue, etc ... which are way different from what I'm used to in other languages.
Yet, there are cases where I just cannot make sense of how message passing really fit in, these include:
the assignment operator :=
the cascading operator ;
the period operator .
the way to create a set #( ... )
These aren't message passing, right?
As you've discovered, there's still some actual Smalltalk syntax. Block construction, literal strings/symbols/comments, local variable declaration (|...|), and returning (^) are a few things you didn't mention which are also syntax.
Some extensions (e.g. #(...), which typically creates an Array, not a set) are certainly expressible otherwise, for example #(1 2 3) is equivalent to Array with: 1 with: 2 with: 3; they're just there to make the code easier to read and write.
One thing that might help clarify : self, super, true, false, nil & thisContext are data primitives, rather than keywords.
They are the only 6 data primitives. These 6 are also known as pseudo-variables. Absolutely every other thing is an instance of Class Object or its subclasses.
There are very few pre-defined keywords in Smalltalk. They can be written in a very condensed form.
A famous example is Smalltalk Syntax on a Postcard (link)
exampleWithNumber: x
| y |
true & false not & (nil isNil) ifFalse: [self halt].
y := self size + super size.
#($a #a "a" 1 1.0)
do: [ :each |
Transcript show: (each class name);
show: ' '].
^x < y
Here's the comment for this method - which is larger than the method itself:
"A method that illustrates every part of Smalltalk method syntax
except primitives. It has unary, binary, and keyboard messages,
declares arguments and temporaries, accesses a global variable
(but not an instance variable), uses literals (array, character,
symbol, string, integer, float), uses the pseudo variables
true, false, nil, self, and super, and has sequence, assignment,
return and cascade. It has both zero argument and one argument blocks."