Skip to content

Commit

Permalink
Remove CLOSURE and <durable>, <has> back to <static>
Browse files Browse the repository at this point in the history
Ren-C originally aimed to bring the features of CLOSURE! to all
FUNCTION!s at "acceptable cost".  This was partially achieved with
specific binding, and with the ability to use non-reified stack frames
as "specifiers" the costs are hopefully becoming very low for that
binding property (and will be extended to other binding forms).

The second property of CLOSURE! was the indefinite lifetime of all
arguments and locals after the function call.  This was costly, and
entailed making a deep copy of the body of the function on every
invocation.  Ren-C managed to remove the need for that copy, but still
the idea of creating a GC-tracked object for *all* the locals and
arguments in each invocation was heavy-handed.

With implementation techniques getting more sophisticated, the burden
was increasing on having two modalities of function arguments.  Also,
it was seeming less like closure semantics by default was truly a
good idea...the likelihood of obscuring bugs or introducing performance
problems was high.

Rather than dig deeper into CLOSURE and `<durable>` issues for now, it
is likely better to focus on streamlining the code so that methods of
virtual binding can be explored.  In the meantime, USE has been made
into a native, so closure-like effects can be accomplished by putting
USE in the body (as opposed to previously, where it was done in reverse
and USE was written in terms of CLOSURE).

Due to the possibility of `<has>` being coopted for "durable" variables
and given the desire for `<static>` to be retained regardless, this
plays it safe and returns the usages of `<has>` to `<static>`.
  • Loading branch information
hostilefork committed Sep 9, 2017
1 parent 9b06aa0 commit d470f4c
Show file tree
Hide file tree
Showing 30 changed files with 103 additions and 169 deletions.
2 changes: 1 addition & 1 deletion scripts/changes-file/changes-file.reb
Expand Up @@ -134,7 +134,7 @@ load-cherry-pick-map: does [map lock load %cherry-pick-map.reb]
notable?: function [
{Is this a notable change?}
c [block!] {Commit-log block}
<has>
<static>
cherry-pick (load-cherry-pick-map)
related (make block! 0)
][
Expand Down
2 changes: 1 addition & 1 deletion scripts/changes-file/cherry-pick-map.reb
Expand Up @@ -141,7 +141,7 @@ fail "Simple error"
type: 'Added
related: ["47aef8a" "9fb973c" "0dff611"]
trello: https://trello.com/c/IyxPieNa/
summary: {<in>, <with>, <static> / <has> function specs}
summary: {<in>, <with>, <static> function specs}
] ; <in>, <with>, <static> function specs...plus defaults


Expand Down
1 change: 0 additions & 1 deletion src/boot/errors.r
Expand Up @@ -203,7 +203,6 @@ Script: [
frame-already-used: [{Frame currently in use by a function call} :arg1]
frame-not-on-stack: {Frame is no longer running on the stack}

recursive-varargs: {VARARGS! chained into itself (maybe try <durable>?)}
varargs-no-stack: {Call originating VARARGS! has finished running}
varargs-make-only: {MAKE *shared* BLOCK! supported on VARARGS! (not TO)}
varargs-no-look: {VARARGS! may only lookahead by 1 if "hard quoted"}
Expand Down
1 change: 0 additions & 1 deletion src/boot/root.r
Expand Up @@ -33,7 +33,6 @@ ellipsis-tag ; FUNC+PROC use as alternative to [[]] to mark varargs
opt-tag ; FUNC+PROC use as alternative to _ to mark optional void? args
end-tag ; FUNC+PROC use as alternative to | to mark endable args
local-tag ; marks the beginning of a list of "pure locals"
durable-tag ; !!! In progress - argument word lookup survives call ending

;; !!! See notes on FUNCTION-META in %sysobj.r

Expand Down
2 changes: 0 additions & 2 deletions src/core/b-init.c
Expand Up @@ -469,7 +469,6 @@ static void Init_Function_Tags(void)
Init_Function_Tag(ROOT_OPT_TAG, "opt");
Init_Function_Tag(ROOT_END_TAG, "end");
Init_Function_Tag(ROOT_LOCAL_TAG, "local");
Init_Function_Tag(ROOT_DURABLE_TAG, "durable");
}


Expand Down Expand Up @@ -812,7 +811,6 @@ static void Init_Root_Vars(void)
Init_Unreadable_Blank(ROOT_OPT_TAG);
Init_Unreadable_Blank(ROOT_END_TAG);
Init_Unreadable_Blank(ROOT_LOCAL_TAG);
Init_Unreadable_Blank(ROOT_DURABLE_TAG);

// Evaluator not initialized, can't do system construction yet
//
Expand Down
24 changes: 0 additions & 24 deletions src/core/c-function.c
Expand Up @@ -178,8 +178,6 @@ REBARR *Make_Paramlist_Managed_May_Fail(
}
#endif

REBOOL durable = FALSE;

REBDSP dsp_orig = DSP;
assert(DS_TOP == DS_AT(dsp_orig));

Expand Down Expand Up @@ -263,20 +261,6 @@ REBARR *Make_Paramlist_Managed_May_Fail(
else if (0 == Compare_String_Vals(item, ROOT_LOCAL_TAG, TRUE)) {
mode = SPEC_MODE_LOCAL;
}
else if (0 == Compare_String_Vals(item, ROOT_DURABLE_TAG, TRUE)) {
//
// <durable> is currently a lesser version of what it
// hopes to be, but signals what R3-Alpha called CLOSURE!
// semantics. Indicating that a typeset is durable in
// the low-level will need to be done with some notation
// that doesn't use "keywords"--perhaps a #[true] or a
// #[false] picked up on by the typeset.
//
// !!! Enforce only at the head, if it's going to be
// applying to everything??
//
durable = TRUE;
}
else
fail (Error_Bad_Func_Def_Core(item, VAL_SPECIFIER(spec)));

Expand Down Expand Up @@ -529,14 +513,6 @@ REBARR *Make_Paramlist_Managed_May_Fail(
default:
fail (Error_Bad_Func_Def_Core(item, VAL_SPECIFIER(spec)));
}

// !!! This is a lame way of setting the durability, because it means
// that there's no way a user with just `make function!` could do it.
// However, it's a step closer to the solution and eliminating the
// FUNCTION!/CLOSURE! distinction.
//
if (durable)
SET_VAL_FLAG(typeset, TYPESET_FLAG_DURABLE);
}

Drop_Frame(f);
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/locale/ext-locale-init.reb
Expand Up @@ -454,7 +454,7 @@ unless 'Windows = first system/platform [

hijack 'locale function [
type [word!]
<has>
<static>
iso-639 (iso-639-table)
iso-3166 (iso-3166-table)
][
Expand Down
21 changes: 14 additions & 7 deletions src/include/sys-bind.h
Expand Up @@ -348,13 +348,20 @@ inline static REBVAL *Get_Var_Core(

if (CTX_VARS_UNAVAILABLE(context)) {
//
// Currently if a context has a stack component, then the vars
// are "all stack"...so when that level is popped, all the vars
// will be unavailable. There is a <durable> mechanism, but that
// makes all the variables come from an ordinary pool-allocated
// series. Hybrid approaches which have "some stack and some
// durable" will be possible in the future, as a context can
// mechanically have both stackvars and a dynamic data pointer.
// Currently the storage for variables in a function frame are all
// located on the chunk stack. So when that level is popped, all the
// vars will be unavailable.
//
// Historically the system became involved with something known as a
// CLOSURE!, which used non-stack storage (like an OBJECT!) for all of
// its arguments and locals. One aspect of closures was that
// recursions could uniquely identify their bindings (which is now a
// feature of all functions). But the other aspect was indefinite
// lifetime of word bindings "leaked" after the closure was finished.
//
// The idea of allowing a single REBSER node to serve for both a
// durable portion and a stack-lifetime portion of a FRAME! is on the
// table, but not currently implemented.

if (flags & GETVAR_END_IF_UNAVAILABLE)
return m_cast(REBVAL*, END); // only const callers should use
Expand Down
10 changes: 0 additions & 10 deletions src/include/sys-function.h
Expand Up @@ -287,16 +287,6 @@ inline static REBOOL IS_FUNCTION_HIJACKER(const RELVAL *v)
{ return LOGICAL(VAL_FUNC_DISPATCHER(v) == &Hijacker_Dispatcher); }


// !!! At the moment functions are "all durable" or "none durable" w.r.t. the
// survival of their arguments and locals after the call.
//
inline static REBOOL IS_FUNC_DURABLE(REBFUN *f) {
return LOGICAL(
FUNC_NUM_PARAMS(f) != 0
&& GET_VAL_FLAG(FUNC_PARAM(f, 1), TYPESET_FLAG_DURABLE)
);
}

// Native values are stored in an array at boot time. This is a convenience
// accessor for getting the "FUNC" portion of the native--e.g. the paramlist.
// It should compile to be as efficient as fetching any global pointer.
Expand Down
16 changes: 2 additions & 14 deletions src/include/sys-typeset.h
Expand Up @@ -185,18 +185,6 @@ enum Reb_Param_Class {
//
#define TYPESET_FLAG_UNBINDABLE TYPESET_FLAG(1)

// !!! <durable> is the working name for the property of a function
// argument or local to have its data survive after the call is over.
// Much of the groundwork has been laid to allow this to be specified
// individually for each argument, but the feature currently is "all
// or nothing"--and implementation-wise corresponds to what R3-Alpha
// called CLOSURE!, with the deep-copy-per-call that entails.
//
// Hence if this property is applied, it will be applied to *all* of
// a function's arguments.
//
#define TYPESET_FLAG_DURABLE TYPESET_FLAG(2)

// !!! This does not need to be on the typeset necessarily. See the
// VARARGS! type for what this is, which is a representation of the
// capture of an evaluation position. The type will also be checked but
Expand All @@ -206,7 +194,7 @@ enum Reb_Param_Class {
// a VARARGS! type are different things. (A function may accept a
// variadic number of VARARGS! values, for instance.)
//
#define TYPESET_FLAG_VARIADIC TYPESET_FLAG(3)
#define TYPESET_FLAG_VARIADIC TYPESET_FLAG(2)

// !!! In R3-Alpha, there were only 8 type-specific bits...with the
// remaining bits "reserved for future use". This goes over the line
Expand All @@ -219,7 +207,7 @@ enum Reb_Param_Class {
// ordinary argument hit the end (e.g. the trick used for `>> help` when
// the arity is 1 usually as `>> help foo`)
//
#define TYPESET_FLAG_ENDABLE TYPESET_FLAG(4)
#define TYPESET_FLAG_ENDABLE TYPESET_FLAG(3)

// Operations when typeset is done with a bitset (currently all typesets)

Expand Down
18 changes: 2 additions & 16 deletions src/mezz/base-funcs.r
Expand Up @@ -137,14 +137,6 @@ make-action: func [

;; dump [spec]

; Insert <durable> into the spec. This is based on the belief that
; indefinite duration is a fair user expectation without having to ask.
; Consider the legitimacy of:
;
; foo: function [x] [y: x * 2 | return func [z] [x + y + z]
;
append new-spec <durable>

; Gather the SET-WORD!s in the body, excluding the collected ANY-WORD!s
; that should not be considered. Note that COLLECT is not defined by
; this point in the bootstrap.
Expand Down Expand Up @@ -181,9 +173,7 @@ make-action: func [
)
|
(var: void) ;-- everything below this line clears var
fail ;-- failing here means rolling over to next rule (<durable>)
|
<durable> ;-- don't add to new-spec as we already added it
fail ;-- failing here means rolling over to next rule
|
<local>
any [set var: word! (other: _) opt set other: group! (
Expand Down Expand Up @@ -222,11 +212,7 @@ make-action: func [
string! ;-- skip over as commentary
]
|
; While <static> is a well-known computer science term, it is an
; un-intuitive word. <has> is Ren-C's preference in mezzanine or
; official code, relating it to the HAS object constructor.
;
[<has> | <static>] (
<static> (
unless statics [
statics: copy []
]
Expand Down
41 changes: 11 additions & 30 deletions src/mezz/mezz-legacy.r
Expand Up @@ -262,6 +262,17 @@ op?: func [dummy:] [
] 'dummy
]

clos: closure: func [dummy:] [
fail/where [
{One feature of R3-Alpha's CLOSURE! is now available in all FUNCTION!}
{which is to specifically distinguish variables in recursions. The}
{other feature of indefinite lifetime of "leaked" args and locals is}
{under review. If one wishes to create an OBJECT! on each function}
{call and bind the body into that object, that is still possible--but}
{specialized support for the feature is not implemented at present.}
] 'dummy
]


; The legacy PRIN construct is replaced by PRINT/ONLY SPACED
;
Expand Down Expand Up @@ -430,36 +441,6 @@ apply: adapt 'apply [
]
]


; In Ren-C, FUNCTION's variables have indefinite extent (aka <durable>), and
; the body is specifically bound to those variables. (There is no dynamic
; binding in Ren-C)
;
closure: func [
return: [function!]
spec
body
][
function compose [
return: [<opt> any-value!]
(spec)
] body
]

; FUNC variables are not durable by default, it must be specified explicitly.
;
clos: func [
"Defines a closure function."

return: [function!]
spec [block!]
{Help string (opt) followed by arg words (and opt type and string)}
body [block!]
"The body block of the function"
][
func compose [<durable> (spec)] body
]

closure!: :function!
closure?: :function?

Expand Down
2 changes: 1 addition & 1 deletion src/mezz/mezz-math.r
Expand Up @@ -161,7 +161,7 @@ math: function [
; binding information into something that's not the function body itself
; isn't implemented.

<has>
<static>

slash (to-lit-word first [ / ])

Expand Down
2 changes: 1 addition & 1 deletion src/mezz/mezz-secure.r
Expand Up @@ -17,7 +17,7 @@ secure: function [
'policy [<opt> word! lit-word! block!]
"Set single or multiple policies (or HELP)"

<has>
<static>

; Permanent values and sub-functions of SECURE:

Expand Down
2 changes: 1 addition & 1 deletion src/mezz/mezz-series.r
Expand Up @@ -220,7 +220,7 @@ reword: function [
output [any-string! binary!]
"The buffer series (modified)"

<has>
<static>

; Note: this list should be the same as above with delimiters, with
; BLOCK! excluded.
Expand Down
10 changes: 5 additions & 5 deletions src/mezz/prot-tls.r
Expand Up @@ -79,7 +79,7 @@ make-tls-error: func [
parse-asn: function [
data [binary!]

<has>
<static>

universal-tags ([
eoc
Expand Down Expand Up @@ -203,7 +203,7 @@ parse-asn: function [
get-next-read-state: function [
ctx [object!]

<has>
<static>

read-proto-states ([
client-hello [server-hello]
Expand All @@ -226,7 +226,7 @@ get-next-read-state: function [
get-next-write-state: function [
ctx [object!]

<has>
<static>

write-proto-states ([
server-hello-done [client-key-exchange]
Expand Down Expand Up @@ -525,7 +525,7 @@ decrypt-data: function [
parse-protocol: function [
data [binary!]

<has>
<static>

protocol-types ([
20 change-cipher-spec
Expand All @@ -550,7 +550,7 @@ parse-messages: function [
ctx [object!]
proto [object!]

<has>
<static>

message-types ([
0 hello-request
Expand Down
4 changes: 2 additions & 2 deletions src/mezz/sys-load.r
Expand Up @@ -105,7 +105,7 @@ load-header: function [
/required
"Script header is required"

<has>
<static>
non-ws (make bitset! [not 1 - 32])
][
; This function decodes the script header from the script body.
Expand Down Expand Up @@ -534,7 +534,7 @@ load-ext-module: function [
/body
code [block!]
"Equivalent rebol code"
<has>
<static>
index (-1)
] compose [
index: index + 1
Expand Down
4 changes: 2 additions & 2 deletions src/os/host-console.r
Expand Up @@ -45,7 +45,7 @@ echo: procedure [
'instruction [file! string! block! word!]
{File or template with * substitution, or command: [ON OFF RESET].}

<has>
<static>
target ([%echo * %.txt])
form-target
sub ("")
Expand Down Expand Up @@ -171,7 +171,7 @@ host-console: function [
focus-frame [blank! frame!]
{If at a breakpoint, the function frame where the breakpoint was hit}

<has>
<static>

RE_SCAN_INVALID (2000)
RE_SCAN_MISSING (2001)
Expand Down
2 changes: 1 addition & 1 deletion src/os/host-start.r
Expand Up @@ -238,7 +238,7 @@ host-start: function [
boot-exts [block! blank!]
{Extensions (modules) loaded at boot}
<with> host-prot
<has>
<static>
o (system/options) ;-- shorthand since options are often read/written
][
; Currently there is just one monolithic "initialize all schemes", e.g.
Expand Down

0 comments on commit d470f4c

Please sign in to comment.