*Progress Meter*
- *[WIP]* Knowledge-Representation Improvements
- *[DONE]* Better syntax, cleaner API & refactoring
- *[WIP]* Meta info in slots
- Advice
- Multi-Methods (value-based)
- Constraint Management Improvements
- *[DONE]* Better syntax & some refactoring
- Parameterized pointer variables
- Advice (for constraints)
- Store constraints as KR objects
- Async execution
- Change predicates
- 2D Graphics
- Linear Algebra and Geometry additions
- 2D API for geometry + Backend (via SDL)
- Event objects + Backend (via SDL)
- Text API + Backend (via Pango)
- Cells
- Configurations
- Some demo (after everything else)
---
TLDR This month was kind of slow, but I have reintegrated the new KR
code with the multi-garnet layer, so now the constraints function again
(and all the tests pass). I have also been adding some meta-slots, and
that's about half-way done.
I have also refactored the multi-garnet layer somewhat (mostly throwing
out the compatability layer for the old formulas), but there are a few
not-so-reassuring things left, like the usage of property lists for
tracking slots, instead of just using hash tables, so I will be looking
into that too quite soon.
Some syntactic improvements were made to the constraints macros as well,
but I won't go over these just yet: this are still likely to change.
---
In the previous report I mentioned that FSET could be a viable option as
a container for schemas: it's functional, so you could safely expose it
to the world as just another slot, and the user could use it for, say,
traversal, just as a hash map. /Access an object as a data structure/
type of deal. However, some quick tests have shown that FSET is 29 times
slower on writes with 14 times the memory consumption compared to the
standard hash tables. Worse, it's also 22 times slower on reads. I still
think that exposing a container is a good idea, so I am just going to be
using standard hash tables instead (as I am doing already, for the most
part). The worst that can happen is the user bypasses the constraint
mechanism if he writes to the hash table directly. But that's going to
be possible to do anyway, so it's not a problem.
Speaking of hash-tables, ~hash-table-args~ is a slot that can be
supplied by the user at object-creation time in order to create a
customized hash-table. For now, only the ~:test~ argument is supported,
and the standard hash-tables only support ~eq~, ~eql~, ~equal~ and
~equalp~ for tests. These should probably cover most cases, though (and
if not, this could be extended, I think). The use of this is
straightforward: now you can use, say, lists for keys, and not just
symbols.
In fact, it seems like letting the user use any kind of object as a key
is a rather sane thing to do. Previously, I was planning on restricting
the use of keywords to KR-only purposes (like ~:is-a~), but maybe this
isn't all too reasonable. So, keywords are too going to be allowed as
keys to the user, and ~is-a~ along with the rest of the kr-supplied
slots will be just regular symbols.
Speaking of meta-slots, the breakdown for these is as follows, more or
less:
- [X] name, prefix
- [X] hash-table-args
- [X] last-added, last-removed, last-updated, last-action
- [ ] container, slot-via, slot-parent
- [~] on-write
- [ ] types
- [X] docs
- [ ] local-only
- [ ] create-on-write
- [ ] advice
- [X] equality
- [?] drop, take, filter
I will be documenting these eventually, of course, but these slots
(along with some hidden constraints) will form the basic interface to
any given schema.
To get an idea of how this is going to work, let's document some slot:
(Ξ s)
(→ 'some-slot 42)
(→ s 'docs (Ξ))
(→ s 'docs 'some-slot "This slot contains the answer ...")
The third line assigns a schema where all the docstrings will be
held. Then the docstring for a slot is assigned. This is roughly
equivalent to doing this at object-creation time:
(Ξ s
(some-slot 42)
(docs (Ξ (some-slot "This slot contains the answer ...")))
One tricky thing here is to keep the inheritence relations intact. So,
if ~s~ were to inherit from ~q~, we would want the ~docs~ in ~s~ to
inherit from ~docs~ in ~q~. This will be accomplished quite simply by
setting up a constraint on the ~is-a~ slot of the ~docs~ to be computed
from all the schemas in the ~is-a~ of s. And the same thing can be done
for any nested schemas. However, it appears that parameterized pointer
variables will be best suited for this, and so this will have to wait
just a bit.
Also, with ~create-on-write~, it won't be necessary to create the ~docs~
schema manually, as it will simply be created when written to.
~name~ and ~prefix~ are for printing/debugging purposes.
~container~, ~slot-via~, ~slot-parent~ will expose the container and
some useful slot properties.
~advice~ is advice for functions called via ~↑~.
~local-only~ will list slots that don't have to be inherited.
~types~ will describe slot types for type checking and, later on, for
supplying type info to the compiler.
~equality~ specifies a function for a slot to allow early exit if a new
value is equal to the old on assignment
~drop~, ~take~, ~filter~: these I am still thinking about. Would allow
dropping/taking certain slots for inheritence, but on the other hand
they kind of break the meaning of is-a relationships. But so does
local-only, to a degree, if it shadows an inherited value. This could be
taken care of, but maybe there's no big need for these to begin with. We
will see.
~last-added~, ~last-removed~, ~last-updated~, ~last-action~ refer to
slots and allow meta usage if you constraint them as inputs. For
instance, with these, you could keep track of all slots in a schema that
have a certain type or that have a certain property. This will be very
useful for API-building.
*Further Thoughts*
- I have run some brute benchmarks, and it seems that writing to a slot
which is a constraint input is ~14x slower than writing to a
non-constrained slot. Writing to the root input of a chain of
constraints is only about ~6x slower (averaged). 1250 writes can be
done to 10-variable-deep chains per frame (1/60th of a second). This
doesn't sound too bad, but like I mentioned in the beginning, there's
some shady-looking code in multi-garnet, so it looks like this can be
made faster. Who knows, maybe even much faster, but I have a quite
incomplete picture of the constraint engine and even the code
responsible for the integration, so I can't judge fully. This should
be worth looking into, on general principle.
- For optimization purposes, such as when specifying slot types or doing
value-based dispatch, it appears that CL-ENVIRONMENTS [1] might be
just the library to use. It even allows defining custom
declarations. All it asks is to use its CL package. A fair trade, I
think.
- I wonder if the interface to defining value-based dispatch methods
could be done simply via the schematic interface. It appears possible
and quite sane, if a little peculiar, and would eliminate the need to
produce an extra batch of APIs just for dealing with these methods
(e.g. removal, advice, etc).
- I was told about TeXmacs (in the context of scientific usage) - a
pretty cool WYSIWYG editor for document typesetting, with some
structural philosophy to it. For those of you who are interested in my
thoughts on this, see [2].
---
I was probably a bit overly enthusiastic for my last estimate about
starting to work on graphics in May. This KR/Constraint business will
easily spill into June. Fine by me.
Till next time,
-- Dmitrii Korobeinikov
[1] https://alex-gutev.github.io/cl-environments
[2] https://project-mage.org/all-else-is-not-enough#texmacs