Started work on binding functions. Not yet complete.

This commit is contained in:
Simon Brooke 2026-05-04 16:15:57 +01:00
parent f4303247b9
commit efa6a3246d
17 changed files with 321 additions and 41 deletions

View file

@ -24,7 +24,35 @@ The answer, dear reader, is that Emacs is determined to get in my way in every p
#### Eclipse #### Eclipse
When I finally switched away from using Emacs for everything When I finally switched away from using Emacs for everything, sometime around 2000, I tried a number of things and ended up with [Eclipse](https://eclipseide.org/), which was at the time a fairly simple but fairly solid Java oriented integrated development environment (IDE). I stayed with Eclipse then for about a decade; but when I moved to mainly developing in Clojure, Eclipse just didn't do Clojure very well, I switched back to Emacs for a while, was driven mad by it again, and found LightTable as a blissful release; which takes us back to the beginning of this section.
Last month, when I was searching for something to replace VSCodium and had realised once again how much I hate using Emacs for serious development, I tried Eclipse.
It's... not awful? It's become a very polished, very configurable IDE; it has excellent facilities for C development. But I found it intrusively over-helpful: its continual 'helpful' suggestions got in my way. I used it for about ten days. I wasn't enjoying it. But what made me give up on it was because it won't follow your configured desktop colour theme, and I wasn't able to find a dark-mode theme for it that worked for me: there are plenty of themes , but they are only applied to the editing panels, not to the chrome or to any of the other panels. I find white backgrounds really unpleasant on my eyes.
#### KDevelop and Gnome Builder
I know I tried [KDevelop](https://kdevelop.org/) at some stage in this process. I can't remember why I rejected it. There's probably a reason. I also tried [Gnome Builder](https://apps.gnome.org/en-GB/Builder/) and rejected it very quickly, again I can't remember why; having a wee play with it just now it feels quite nice, and I may have another try. However, the Debian package of Gnome Builder [does not include the help files](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1111418), and, without them, I haven't found out how to invoke the debugger.
#### Basic text editors
I obviously have a basic text editor, [gedit](https://gedit-text-editor.org/), on my system. It does C syntax highlighting very well, but doesn't do code completion, and doesn't have any integration with a build system or debugger. I have various debugger user interfaces — I like [seergdb](https://github.com/epasveer/seer) — but I do have it convenient to have a debugger integrated into my editor, rather than having to switch between two separate applications. Similarly, it's convenient to have a terminal integrated with the development environment, although it doesn't need to be. GEdit, plus seergdb, plus a terminal, plus some sort of a git browser, would work for me.
#### New editors
People online have suggested I try two new editors: [Zed](https://zed.dev/) and Gram: these are essentially the same editor, in fact. Zed proudly announces itself as
> a minimal code editor crafted for speed and collaboration with humans and AI
The Zed project seems to want to monetise their work by selling you AI tokens. Which LLM is behind their AI I don't know. Open Source development needs to be funded somehow; funding it through a tax on people who use AI is as good a way as any.
Dear reader, I do **not want** to collaborate with AI; I don't want any of that shit in my working environment. So that immediately got my back up. It also doesn't have a Debian installer. But I was able to build it from source, and have been using it consistently over the last couple of days, and it's very pleasant. There's a built in debugger, but I cannot get it to work. Beyond that, my build crashes occasionally — maybe once every two or three hours; but it doesn't seem to lose anything when it crashes, so this is not obnoxious. If I ignore the 'AI' features, the lack of a working debugger is the only mark against it.
[Gram](https://gram.liten.app/) is said to be a fork of Zed with the AI features removed. It has a proper Debian installation repository, which is a significant step up over Zed. Unfortunately, it won't run on my desktop machine, due to [a problem with the video card](https://codeberg.org/GramEditor/gram/issues/256). On my laptop, it runs fine, and seems generally usable — although, again, I can't get the debugger to work.
#### Conclusion for now
The conclusion for now is that I don't have a conclusion for now. Any of Gnome Builder, Zed and Gram are sort of good enough. Zed crashes, which is not desirable; Gram only launches on my laptop, but I mostly do serious development on my desktop; I can't yet work out how to launch the debugger on any of them. But none are annoying, none get in my way. I'll keep on evaluating.
## 20260503 ## 20260503

View file

@ -0,0 +1,175 @@
/**
* environment/function_bindings.c
*
* Post Scarcity Software Environment:
*
* Provide bindings for substrate functions. At least in theory, these
* bindings only need to be initialised on node zero.
* todo: they really ought to be in a namespace ::system:bootstrap, once I
* have namespaces and paths working.
*
* (c) 2026 Simon Brooke <simon@journeyman.cc>
* Licensed under GPL version 2.0, or, at your option, any later version.
*/
#include <stdbool.h>
#include <wchar.h>
#include "environment/privileged_keywords.h"
#include "memory/node.h"
#include "memory/pointer.h"
#include "memory/tags.h"
#include "ops/assoc.h"
#include "ops/bind.h"
#include "ops/cond.h"
#include "ops/eval_apply.h"
#include "ops/eq.h"
#include "ops/inspect.h"
#include "ops/stack_ops.h"
#include "ops/string_ops.h"
#include "payloads/cons.h"
#include "payloads/function.h"
#include "payloads/special.h"
/**
* Bind this compiled `executable` function, as a Lisp function, to
* this name in the `oblist`.
* \todo where a function is not compiled from source, we could cache
* the name on the source pointer. Would make stack frames potentially
* more readable and aid debugging generally.
*/
struct pso_pointer
bind_function(struct pso_pointer frame_pointer, wchar_t *name, wchar_t *doc,
struct pso_pointer (*executable)(struct pso_pointer)) {
struct pso_pointer result = fetch_env(frame_pointer);
struct pso_pointer n = c_string_to_lisp_symbol(frame_pointer, name);
struct pso_pointer d = c_string_to_lisp_string(frame_pointer, doc);
struct pso_pointer meta =
make_cons( frame_pointer, make_cons( frame_pointer, privileged_keyword_bootstrap, nil),
make_cons( frame_pointer, make_cons( frame_pointer, privileged_keyword_name, n),
make_cons( frame_pointer, make_cons( frame_pointer, privileged_keyword_documentation, d), nil)));
struct pso_pointer r = make_function(frame_pointer, meta, executable);
if (!exceptionp(r)) {
result = make_cons( frame_pointer, make_cons( frame_pointer, n, r), result);
}
return result;
}
/**
* Bind this compiled `executable` function, as a Lisp special form, to
* this `name` in the `oblist`.
*/
struct pso_pointer
bind_special(struct pso_pointer frame_pointer, wchar_t *name, wchar_t *doc,
struct pso_pointer (*executable)(struct pso_pointer)) {
struct pso_pointer result = fetch_env(frame_pointer);
struct pso_pointer n = c_string_to_lisp_symbol(frame_pointer, name);
struct pso_pointer d = c_string_to_lisp_string(frame_pointer, doc);
struct pso_pointer meta =
make_cons( frame_pointer, make_cons( frame_pointer, privileged_keyword_bootstrap, nil),
make_cons( frame_pointer, make_cons( frame_pointer, privileged_keyword_name, n),
make_cons( frame_pointer, make_cons( frame_pointer, privileged_keyword_documentation, d), nil)));
struct pso_pointer r = make_special(frame_pointer, meta, executable);
if (!exceptionp(r)) {
result = make_cons( frame_pointer, make_cons( frame_pointer, n, r), result);
}
return result;
}
struct function_data {
char32_t *name;
char32_t *documentation;
void* executable;
};
/* right, the problem with all those pretty '#ifdefs' which might allow us to
* simply switch functions on and off just by including or not including .h
* files is that the C compiler is too primitive to know how many items there
* are in an array. So this number must be edited manually, and must be right.
*/
#define N_FUNCTION_INITIALISERS 4
/** initialisers for functions */
struct function_data function_initialisers[] = {
#ifdef __psse_ops_assoc_h
{U"assoc",
U"(assoc key store): search `store` for the value associated with "
U"`key`.",
&assoc},
#endif
#ifdef __psse_ops_bind_h
{U"bind!",
U"(bind! key value store): bind `key` to `value` in this store, modifying "
U"the store if it is writable to the user, otherwise returning a new "
U"store",
&bind},
#endif
#ifdef __psse_ops_eq_h
{U"eq",
U"(eq args...): shallow, cheap equality; returns `t` if all `args...` "
U"are the same object, else `nil`.",
&eq},
{U"equal",
U"(equal a b): expensive, deep equality: returns `t` if objects `a` "
U"and `b` have recursively equal value.",
&equal},
#endif
#ifdef __psse_ops_eval_apply_h
// TODO: there's a lot of other stuff in eval_apply.c, which ought to be in
// other files but at present isn't.
{U"apply",
U"(apply fn args...): apply this `fn` to these `args...` and return "
U"their value.",
&lisp_apply},
{U"eval",
U"(eval expression): evaluate this `expression` and return its value",
&lisp_eval},
#endif
#ifdef __psse_ops_inspect_h
{
U"inspect",
U"(inspect expr), (inspect expr write-stream): inspect one complete "
U"lisp expression and return `nil`. If `write-stream` is specified and "
U"is a write stream, then print to that stream, else to the stream "
U"which is the value of `*out*` in the environment.",
&lisp_inspect
},
#endif
};
/* right, the problem with all those pretty '#ifdefs' which might allow us to
* simply switch functions on and off just by including or not including .h
* files is that the C compiler is too primitive to know how many items there
* are in an array */
#define N_SPECIAL_INITIALISERS 1
/** initialisers for special forms */
struct function_data special_initialisers[] = {
#ifdef __psse_ops_cond_h
{U"cond",
U"(cond clauses...): conditional. Each `clause` is expected to be a "
U"list; if the first item in such a list evaluates to non-nil, the "
U"remaining items in that list are evaluated in turn and the value of "
U"the last returned. If no arg `clause` has a first element which "
U"evaluates to non nil, then nil is returned",
&lisp_cond},
#endif
};
struct pso_pointer
initialise_function_bindings(struct pso_pointer frame_pointer) {
struct pso_pointer result = fetch_env(frame_pointer);
return result;
}

View file

View file

@ -23,6 +23,13 @@
#include "ops/string_ops.h" #include "ops/string_ops.h"
/**
* layer metadata for functions written in C
*/
struct pso_pointer privileged_keyword_bootstrap;
struct pso_pointer privileged_keyword_documentation;
/** /**
* location metadata for exceptions (and possibly location in other contexts). * location metadata for exceptions (and possibly location in other contexts).
*/ */
@ -37,7 +44,9 @@ struct pso_pointer privileged_keyword_name;
#define load_and_lock(var,val)var = lock_object(c_string_to_lisp_keyword(nil, val)) #define load_and_lock(var,val)var = lock_object(c_string_to_lisp_keyword(nil, val))
struct pso_pointer initialise_privileged_keywords( struct pso_pointer env){ struct pso_pointer initialise_privileged_keywords(struct pso_pointer env) {
load_and_lock(privileged_keyword_bootstrap, PK_BOOTSTRAP);
load_and_lock(privileged_keyword_documentation, PK_DOCUMENTATION);
load_and_lock(privileged_keyword_location, PK_LOCATION); load_and_lock(privileged_keyword_location, PK_LOCATION);
load_and_lock( privileged_keyword_name, PK_NAME); load_and_lock( privileged_keyword_name, PK_NAME);
} }

View file

@ -3,7 +3,7 @@
* *
* Post Scarcity Soctware Environment * Post Scarcity Soctware Environment
* *
* TODO: Edit purpose. * Keywords guaranteed to be present in the environment on each node.
* *
* Copyright (c): 27 Apr 2026 Simon Brooke <simon@journeyman.cc> * Copyright (c): 27 Apr 2026 Simon Brooke <simon@journeyman.cc>
* Licensed under GPL version 2.0, or, at your option, any later version. * Licensed under GPL version 2.0, or, at your option, any later version.
@ -11,11 +11,15 @@
#ifndef SRC_C_ENVIRONMENT_PRIVILEGED_KEYWORDS_H_ #ifndef SRC_C_ENVIRONMENT_PRIVILEGED_KEYWORDS_H_
#define SRC_C_ENVIRONMENT_PRIVILEGED_KEYWORDS_H_ #define SRC_C_ENVIRONMENT_PRIVILEGED_KEYWORDS_H_
#include "memory/pointer.h"
#define PK_BOOTSTRAP U"bootstrap"
#define PK_DOCUMENTATION U"documentation"
#define PK_LOCATION U"location" #define PK_LOCATION U"location"
#define PK_NAME U"name" #define PK_NAME U"name"
#include "memory/pointer.h" extern struct pso_pointer privileged_keyword_bootstrap;
extern struct pso_pointer privileged_keyword_documentation;
extern struct pso_pointer privileged_keyword_location; extern struct pso_pointer privileged_keyword_location;
extern struct pso_pointer privileged_keyword_name; extern struct pso_pointer privileged_keyword_name;

View file

@ -115,13 +115,8 @@ bool c_internedp( struct pso_pointer key, struct pso_pointer store ) {
* @return a pointer to the value of the key in the store, or nil if not found * @return a pointer to the value of the key in the store, or nil if not found
*/ */
struct pso_pointer assoc( struct pso_pointer assoc(
#ifndef MANAGED_POINTER_ONLY
struct pso4 *frame,
#endif
struct pso_pointer frame_pointer ) { struct pso_pointer frame_pointer ) {
#ifdef MANAGED_POINTER_ONLY
struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso4 *frame = pointer_to_pso4( frame_pointer );
#endif
struct pso_pointer key = fetch_arg( frame, 0 ); struct pso_pointer key = fetch_arg( frame, 0 );
struct pso_pointer store = or( make_frame( 2, frame_pointer, struct pso_pointer store = or( make_frame( 2, frame_pointer,
fetch_arg( frame, 1 ), fetch_arg( frame, 1 ),

View file

@ -16,6 +16,8 @@
#include "memory/pointer.h" #include "memory/pointer.h"
struct pso_pointer assoc(struct pso_pointer frame_pointer);
struct pso_pointer search( struct pso_pointer key, struct pso_pointer search( struct pso_pointer key,
struct pso_pointer store, bool return_key ); struct pso_pointer store, bool return_key );

View file

@ -8,6 +8,7 @@
#include "memory/node.h" #include "memory/node.h"
#include "memory/pointer.h" #include "memory/pointer.h"
#include "memory/pso2.h" #include "memory/pso2.h"
#include "memory/pso4.h"
#include "memory/tags.h" #include "memory/tags.h"
#include "ops/eval_apply.h" #include "ops/eval_apply.h"
@ -29,12 +30,12 @@
*/ */
struct pso_pointer eval_cond_clause( struct pso_pointer clause, struct pso_pointer eval_cond_clause( struct pso_pointer clause,
struct pso4 *frame, struct pso4 *frame,
struct pso_pointer frame_pointer, struct pso_pointer frame_pointer) {
struct pso_pointer env ) { struct pso_pointer result = nil;
struct pso_pointer result = nil; struct pso_pointer env = fetch_env(frame_pointer);
#ifdef DEBUG #ifdef DEBUG
debug_print( L"\n\tCond clause: ", DEBUG_EVAL, 0 ); debug_print( U"\n\tCond clause: ", DEBUG_EVAL, 0 );
debug_print_object( clause, DEBUG_EVAL, 0 ); debug_print_object( clause, DEBUG_EVAL, 0 );
debug_println( DEBUG_EVAL); debug_println( DEBUG_EVAL);
#endif #endif
@ -49,19 +50,19 @@ struct pso_pointer eval_cond_clause( struct pso_pointer clause,
c_progn( frame, frame_pointer, c_cdr( clause ), env ) ); c_progn( frame, frame_pointer, c_cdr( clause ), env ) );
#ifdef DEBUG #ifdef DEBUG
debug_print( L"\n\t\tCond clause ", DEBUG_EVAL, 0 ); debug_print( U"\n\t\tCond clause ", DEBUG_EVAL, 0 );
debug_print_object( clause, DEBUG_EVAL, 0 ); debug_print_object( clause, DEBUG_EVAL, 0 );
debug_print( L" succeeded; returning: ", DEBUG_EVAL, 0 ); debug_print( U" succeeded; returning: ", DEBUG_EVAL, 0 );
debug_print_object( result, DEBUG_EVAL, 0 ); debug_print_object( result, DEBUG_EVAL, 0 );
debug_println( DEBUG_EVAL); debug_println( DEBUG_EVAL);
} else { } else {
debug_print( L"\n\t\tCond clause ", DEBUG_EVAL, 0 ); debug_print( U"\n\t\tCond clause ", DEBUG_EVAL, 0 );
debug_print_object( clause, DEBUG_EVAL, 0 ); debug_print_object( clause, DEBUG_EVAL, 0 );
debug_print( L" failed.\n", DEBUG_EVAL, 0 ); debug_print( U" failed.\n", DEBUG_EVAL, 0 );
#endif #endif
} }
} else { } else {
result = throw_exception( c_string_to_lisp_symbol( frame_pointer, L"cond" ), result = throw_exception( c_string_to_lisp_symbol( frame_pointer, U"cond" ),
c_string_to_lisp_string c_string_to_lisp_string
(frame_pointer, L"Arguments to `cond` must be lists" ), (frame_pointer, L"Arguments to `cond` must be lists" ),
frame_pointer ); frame_pointer );
@ -78,14 +79,10 @@ struct pso_pointer eval_cond_clause( struct pso_pointer clause,
* *
* * (cond clauses...) * * (cond clauses...)
* *
* @param frame my stack frame.
* @param frame_pointer a pointer to my pso4.
* @param env the environment in which arguments will be evaluated.
* @return the value of the last expression of the first successful `clause`. * @return the value of the last expression of the first successful `clause`.
*/ */
struct pso_pointer struct pso_pointer lisp_cond(struct pso_pointer frame_pointer) {
lisp_cond( struct pso4 *frame, struct pso_pointer frame_pointer, struct pso4 *frame = pointer_to_pso4(frame_pointer);
struct pso_pointer env ) {
struct pso_pointer result = nil; struct pso_pointer result = nil;
bool done = false; bool done = false;
@ -97,7 +94,7 @@ lisp_cond( struct pso4 *frame, struct pso_pointer frame_pointer,
// to nil, the form still succeeded and we should still exit `cond`. // to nil, the form still succeeded and we should still exit `cond`.
// //
result = eval_cond_clause( clause_pointer, frame, frame_pointer, env ); result = eval_cond_clause( clause_pointer, frame, frame_pointer );
if ( !c_nilp( result ) && c_truep( c_car( result ) ) ) { if ( !c_nilp( result ) && c_truep( c_car( result ) ) ) {
result = c_cdr( result ); result = c_cdr( result );
@ -106,7 +103,7 @@ lisp_cond( struct pso4 *frame, struct pso_pointer frame_pointer,
} }
} }
#ifdef DEBUG #ifdef DEBUG
debug_print( L"\tCond returning: ", DEBUG_EVAL, 0 ); debug_print( U"\tCond returning: ", DEBUG_EVAL, 0 );
debug_print_object( result, DEBUG_EVAL, 0 ); debug_print_object( result, DEBUG_EVAL, 0 );
debug_println( DEBUG_EVAL); debug_println( DEBUG_EVAL);
#endif #endif

20
src/c/ops/cond.h Normal file
View file

@ -0,0 +1,20 @@
/**
* ops/cond.h
*
* Post Scarcity Software Environment: cond.
*
* cond a name to a value in a store.
*
* (c) 2026 Simon Brooke <simon@journeyman.cc>
* Licensed under GPL version 2.0, or, at your option, any later version.
*/
#ifndef __psse_ops_cond_h
#define __psse_ops_cond_h
#include "memory/pointer.h"
struct pso_pointer lisp_cond( struct pso_pointer frame_pointer );
#endif

View file

@ -131,7 +131,7 @@ struct pso_pointer eq(
* * symbols * * symbols
* * strings * * strings
* *
* * (equal? arg1 qrg2) * * (equal? arg1 arg2)
* *
* @return `t` if all args are pointers to the same object, else `nil`; * @return `t` if all args are pointers to the same object, else `nil`;
*/ */

View file

@ -260,8 +260,7 @@ struct pso_pointer eval_forms( struct pso_pointer frame_pointer ) {
* *
* This is experimental. It almost certainly WILL change. * This is experimental. It almost certainly WILL change.
*/ */
struct pso_pointer lisp_try( struct pso_pointer frame_pointer, struct pso_pointer lisp_try( struct pso_pointer frame_pointer) {
struct pso_pointer env ) {
struct pso_pointer result = nil; struct pso_pointer result = nil;
struct pso4 *frame = pointer_to_pso4( frame_pointer ); struct pso4 *frame = pointer_to_pso4( frame_pointer );
struct pso_pointer body_frame = struct pso_pointer body_frame =
@ -301,8 +300,7 @@ struct pso_pointer lisp_try( struct pso_pointer frame_pointer,
* @return the root namespace. * @return the root namespace.
*/ */
struct pso_pointer struct pso_pointer
lisp_oblist( struct pso4 *frame, struct pso_pointer frame_pointer, lisp_oblist( struct pso4 *frame, struct pso_pointer frame_pointer) {
struct pso_pointer env ) {
return oblist; return oblist;
} }
@ -750,9 +748,6 @@ struct pso_pointer lisp_apply( struct pso_pointer frame_pointer) {
* *
* * (eval expression) * * (eval expression)
* *
* @param frame my pso4.
* @param frame_pointer a pointer to my pso4.
* @param env my environment.
* @return * @return
* * If `expression` is a number, string, `nil`, or `t`, returns `expression`. * * If `expression` is a number, string, `nil`, or `t`, returns `expression`.
* * If `expression` is a symbol, returns the value that expression is bound * * If `expression` is a symbol, returns the value that expression is bound

View file

@ -21,10 +21,7 @@
#include "ops/stack_ops.h" #include "ops/stack_ops.h"
/** /**
* Function: dump/inspect one complete lisp expression and return nil. If * Function: dump/
* write-stream is specified and is a write stream, then print to that stream,
* else the stream which is the value of
* `*out*` in the environment.
* *
* * (inspect expr) * * (inspect expr)
* * (inspect expr write-stream) * * (inspect expr write-stream)

View file

@ -9,8 +9,8 @@
* Licensed under GPL version 2.0, or, at your option, any later version. * Licensed under GPL version 2.0, or, at your option, any later version.
*/ */
#ifndef psse_ops_inspect #ifndef psse_ops_inspect_h
#define psse_ops_inspect #define psse_ops_inspect_h
#include "memory/pointer.h" #include "memory/pointer.h"

25
src/c/payloads/function.c Normal file
View file

@ -0,0 +1,25 @@
/**
* payloads/function.c
*
* an ordinary Lisp function - one whose arguments are pre-evaluated.
*
* (c) 2026 Simon Brooke <simon@journeyman.cc>
* Licensed under GPL version 2.0, or, at your option, any later version.
*/
#include "memory/pointer.h"
#include "memory/pso.h"
#include "memory/pso2.h"
#include "memory/tags.h"
struct pso_pointer make_function(
struct pso_pointer frame_pointer, struct pso_pointer meta,
struct pso_pointer (*executable)(struct pso_pointer frame_pointer)) {
struct pso_pointer result = allocate(frame_pointer, FUNCTIONTAG, 2);
struct pso2 *object = pointer_to_object(result);
object->payload.function.meta = meta;
object->payload.function.executable = executable;
return result;
}

View file

@ -39,4 +39,8 @@ struct function_payload {
struct pso_pointer ( *executable ) ( struct pso_pointer frame_pointer ); struct pso_pointer ( *executable ) ( struct pso_pointer frame_pointer );
}; };
struct pso_pointer make_function(
struct pso_pointer frame_pointer, struct pso_pointer meta,
struct pso_pointer (*executable)(struct pso_pointer frame_pointer));
#endif #endif

25
src/c/payloads/special.c Normal file
View file

@ -0,0 +1,25 @@
/**
* payloads/special.c
*
* a special Lisp function - one whose arguments are **not** pre-evaluated.
*
* (c) 2026 Simon Brooke <simon@journeyman.cc>
* Licensed under GPL version 2.0, or, at your option, any later version.
*/
#include "memory/pointer.h"
#include "memory/pso.h"
#include "memory/pso2.h"
#include "memory/tags.h"
struct pso_pointer make_special(
struct pso_pointer frame_pointer, struct pso_pointer meta,
struct pso_pointer (*executable)(struct pso_pointer frame_pointer)) {
struct pso_pointer result = allocate(frame_pointer, SPECIALTAG, 2);
struct pso2 *object = pointer_to_object(result);
object->payload.special.meta = meta;
object->payload.special.executable = executable;
return result;
}

View file

@ -18,8 +18,12 @@
/** /**
* A special form - one whose arguments are not pre-evaluated but passed as * A special form - one whose arguments are not pre-evaluated but passed as
* provided. * provided. Shares payload with function, q.v.
* \see NLAMBDATAG. * \see NLAMBDATAG.
*/ */
struct pso_pointer make_special(
struct pso_pointer frame_pointer, struct pso_pointer meta,
struct pso_pointer (*executable)(struct pso_pointer frame_pointer));
#endif #endif