4 ms·
Last part unless you have any questions and I will expand I mentioned all types in Kernel are disjoint, but didn't mention that they are encapsulated. All prim
by sparkie 6y ago
Last part unless you have any questions and I will expand
I mentioned all types in Kernel are disjoint, but didn't mention that they are encapsulated. All primitive types are encapsulated, but you can also create your own encapsulations. There is no bolts-attached object or records libraries like in lisp or scheme to achieve this: just one simple primitive: (make-encapsulation-type), which takes no arguments.
The primitive returns a 3-tuple consisting of a constructor, a predicate to test if an object is of the encapsulated type, and an elimination function to access the value with which it was constructed. Generally, you would expose the predicate by binding it to a symbol in an accessible environment, but you would not reveal the constructor and eliminator - you would use them only inside the static environment where defining your encapsulation.
To give and example, lets use the compound operative type I referred to above - which is essentially a 4-tuple. I used the functions `get-static-env`, `get-parameter-tree` etc without defining them earlier. I will define them here.
($provide!
($vau
get-static-env
get-parameter-tree
get-symbol-for-dynamic-env
get-body
operative?)
($define! (make-operative operative? eliminate-operative) (make-encapsulation-type))
($define! $vau
($vau-primitive (formals symbol body) static-env
(make-operative (list formals symbol body static-env))))
($define! get-static-env
($lambda (operative)
($let ((#ignore #ignore #ignore static-env) (eliminate-operative operative))
static-env)))
($define! get-parameter-tree
($lambda (operative)
($let ((formals #ignore #ignore #ignore) (eliminate-operative operative))
formals)))
($define! get-symbol-for-dynamic-env
($lambda (operative)
($let ((#ignore symbol #ignore #ignore) (eliminate-operative operative))
symbol)))
($define! get-body
($lambda (operative)
($let ((#ignore #ignore body #ignore) (eliminate-operative) operative))
body)))
$provide! here binds the list of symbols in its first operand into the dynamic environment where $provide! was called. The remaining operands to $provide! mutate the local environment of $provide! but not the dynamic environment from which $provide! was called. This means that the definitions of 'make-operative' and 'eliminate-operative' are only accessible from within the local scope of provide, but are no longer accessible directly once $provide! has been evaluated. Note that $provide! itself is not a language primitive, but a function in the standard library. It is a compound operative implemented using $define!
The user of this 'library' can only use the 6 combiners provided at the top. The details of how operatives are implemented (as a 4-tuple) are hidden from any use of these combiners.
This clever way in which environments, encapsulations and operatives work together is truly a gem. The decision to forbid mutating parent environments, and the ability to encapsulate objects fixes the problems of old fexprs, which were very unintuitive to work with.
- oxymoron 6y agoThis is excellent. Thanks for putting in the time of writing it up!