(symbol-access) reminds me of the property list feature [0] of Chez Scheme and other implementations (presumably R7RS as well), which allow arbitrary data to be associated with any symbol "in the background" (as I visualize it). I've pondered using tags stored in the prop-list as a kind of poor man's run-time type system for polymorphic functions to check before performing the appropriate operation, but chose to use custom record types, which generate custom (is-type-x?) predicates, instead.
For the more knowledgeable, is (symbol-access) a complete general purpose primitive for reactive programming, like (delay) and (force) are for laziness?
Symbol property lists (like in CL: http://www.lispworks.com/documentation/lw70/CLHS/Body/t_symb...) are useful, but you have to take care about the difference between symbols and variables. The same symbol in a different environment can represent a variable of a different type. Apparently symbol-access manipulates the current lexical environment, ie. a specific binding of particular symbol.
> For the more knowledgeable, is (symbol-access) a complete general purpose primitive for reactive programming, like (delay) and (force) are for laziness?
As far as I know, yes, but careful about mutual references: if updating x when y changes triggers another update of y from x, you might never terminate.
First class environments is one of my favourite features of Scheme, and I still don't understand why I've never seen it elsewhere.
However, it does make Scheme uniquely suitable for an embedded language, or turning Scheme into a host language.
This [0] sort of demonstrates the magic that first class environments add, and how they make re-writing a language, whilst its running, a breeze.
It gives you safety in eval, and means that the kind of methods that other languages need to use to create, say a Flask-like API, are just plain overkill.
Lua 5.2 has getfenv/setfenv, which lets you view and modify the environment of functions. It even lets you set a metatable on the env, so you can do stuff like tracing variable gets or sets.
> and I still don't understand why I've never seen it elsewhere.
Because reflecting upon environments at run-time is a silly eval hack that defies compilation.
An environment that is exposed to the code at run-time cannot be efficiently treated by the compiler.
Lisp compilers throw away lexical environments when the dust settles.
An environment isn't something that is there, but you are blocked from accessing it by the evil Lisp implementation that doesn't provide an API. It in fact isn't there, except when the code is being inefficiently interpreted.
It can be ineffecient to generate and pass environments around, but it doesn't have to be, nor does it have to be a hack.
Compiling with continuations [0] gives you a very efficient output, and a lexical environment that is easily modified, and intuitive to exist. Combine that with Cheney on the MTA, and first class environments are no more costly than a lambda.
It is also possible to compile the initial environment [1], which makes it simpler to JIT any functions later linked to the structure.
I'm not sure why you think a compiler has to throw away the environment, its certainly not what MIT Scheme does, as environments are special forms that persist as you go down and up the stack. [2]
Symbol-functions, as much as I sometimes wish for them, aren't a great idea: They give macro-writers even less guarantees. However, wrapping datastructures in functions definitely is.
Also, first-class environments are amazing, and I wish my scheme-of-choice (CHICKEN) had them: It used to, but the code doesn't work with the new compiler, and the long-promised replacement hasn't been written yet.
Maybe I should just dive in and write it myself...
It's interesting to see people not doing computer music using s7. It was written at CCRMA [1] as their extension language of choice for various tools like Common Music [2], much like Guile was written to be the GNU project's extension language of choice. There's nothing really music-specific about it, I just haven't seen uptake outside that community before.
I've used Common Music to make some algorithmic compositions. It was a lot of fun.
Unfortunately, I can't get the latest version of CM to compile, and CM has been moving towards a GUI version, and away from teh command line, which I found kind of sad.
Still, CM is my preferred algorithmic composition environment, by far. It's a shame it's not better known.
Many of the nice features mentioned are R6RS things, or extensions of that line. However, some R5RS schemes have extensions to support this behaviour. I'll use my favourite, Chicken, as an example:
I included some first-class environment manipulation in TXR Lisp. It's an obvious feature. They are already first-class in the internals; you just have to expose the API:
The intent is to be able to prepare an object which can be passed as the optional environment parameter to eval and its ilk.
First class environments of this type are not incredibly useful; they encourage the developer to create a more sophisticated eval hairball.
(First class environments in macro-expansion are incredibly useful, on the other hand; but that is at code walking time, not run-time!)
Note that no provision is made to allow a form access the current environment as a first class object. That is deliberate; by doing that, you turn the language into NewLisp or Tcl, slamming the door on future efficient compilation. Code should be written based on the assumption that there is no current environment, as an object. The compiler decides how the abstract environment maps to the machine code, and it's usually not via some object that neatly associates symbols with value places.
It would literally be a five minute hack to provide a special operator form like `(my-env)` that just yields the environment object when evaluated, but the damage these five minutes would inflict on the language would be inestimable.
Agreed, but some of those features are quite useful. Allowing for tcl/lua-style "upvars" means that a whole class of macros no longer have to be written. I often wish it were more possible to extend the environment of an existing closure, as this would allow for real dynamic variables (as opposed to the hack that Scheme has now, which is actually terrible).
Naughty Dog still does so, just usually for game logic, tooling etc. with core engine in C++ (reasons are afaik language-unrelated). Some presentations about uncharted and other games they did recently describe how they use it
27 comments
[ 3.5 ms ] story [ 41.4 ms ] threadFor the more knowledgeable, is (symbol-access) a complete general purpose primitive for reactive programming, like (delay) and (force) are for laziness?
[0] http://www.scheme.com/csug8/objects.html
> For the more knowledgeable, is (symbol-access) a complete general purpose primitive for reactive programming, like (delay) and (force) are for laziness?
As far as I know, yes, but careful about mutual references: if updating x when y changes triggers another update of y from x, you might never terminate.
However, it does make Scheme uniquely suitable for an embedded language, or turning Scheme into a host language.
This [0] sort of demonstrates the magic that first class environments add, and how they make re-writing a language, whilst its running, a breeze.
It gives you safety in eval, and means that the kind of methods that other languages need to use to create, say a Flask-like API, are just plain overkill.
[0] http://www.cs.rpi.edu/academics/courses/fall00/ai/scheme/ref...
Checkout: http://lambda-the-ultimate.org/node/3861
(let ((a +)) (null-environment))
Edit: I do really want to know if there is a simple answer. Being able to embed Luajit for some things like this would be awesome.
Absolutely doable though.
[1] http://stackoverflow.com/a/6982080
https://cran.r-project.org/doc/manuals/r-release/R-lang.html...
Because reflecting upon environments at run-time is a silly eval hack that defies compilation.
An environment that is exposed to the code at run-time cannot be efficiently treated by the compiler.
Lisp compilers throw away lexical environments when the dust settles.
An environment isn't something that is there, but you are blocked from accessing it by the evil Lisp implementation that doesn't provide an API. It in fact isn't there, except when the code is being inefficiently interpreted.
Compiling with continuations [0] gives you a very efficient output, and a lexical environment that is easily modified, and intuitive to exist. Combine that with Cheney on the MTA, and first class environments are no more costly than a lambda.
It is also possible to compile the initial environment [1], which makes it simpler to JIT any functions later linked to the structure.
I'm not sure why you think a compiler has to throw away the environment, its certainly not what MIT Scheme does, as environments are special forms that persist as you go down and up the stack. [2]
[0] http://matt.might.net/articles/cps-conversion/
[1] https://github.com/gambit/gambit/commit/09284455ae7c7e8dff4a...
[2] https://groups.csail.mit.edu/mac/ftpdir/scheme-7.4/doc-html/...
Also, first-class environments are amazing, and I wish my scheme-of-choice (CHICKEN) had them: It used to, but the code doesn't work with the new compiler, and the long-promised replacement hasn't been written yet.
Maybe I should just dive in and write it myself...
[1] https://ccrma.stanford.edu/
[2] http://commonmusic.sourceforge.net/
Unfortunately, I can't get the latest version of CM to compile, and CM has been moving towards a GUI version, and away from teh command line, which I found kind of sad.
Still, CM is my preferred algorithmic composition environment, by far. It's a shame it's not better known.
* Applicative Syntax for Data Structures[0]
* First-class Environments[1]
0: http://wiki.call-cc.org/eggref/4/callable-data-structures
1: http://wiki.call-cc.org/eggref/4/environments
The API is very small, though:
http://www.nongnu.org/txr/txr-manpage.html#N-01144687
Just make-env, env-vbind and env-fbind.
The intent is to be able to prepare an object which can be passed as the optional environment parameter to eval and its ilk.
First class environments of this type are not incredibly useful; they encourage the developer to create a more sophisticated eval hairball.
(First class environments in macro-expansion are incredibly useful, on the other hand; but that is at code walking time, not run-time!)
Note that no provision is made to allow a form access the current environment as a first class object. That is deliberate; by doing that, you turn the language into NewLisp or Tcl, slamming the door on future efficient compilation. Code should be written based on the assumption that there is no current environment, as an object. The compiler decides how the abstract environment maps to the machine code, and it's usually not via some object that neatly associates symbols with value places.
It would literally be a five minute hack to provide a special operator form like `(my-env)` that just yields the environment object when evaluated, but the damage these five minutes would inflict on the language would be inestimable.
https://github.com/antrad/Abuse_1996/tree/master/data/lisp
[1] https://en.wikipedia.org/wiki/Game_Oriented_Assembly_Lisp