I've been cited! (In a way ...)
Hee hee! This paper, on Scheme Program Documentation Tools, references docscm :-)
Hee hee! This paper, on Scheme Program Documentation Tools, references docscm :-)
It's almost impossible to follow the scheme-reports-wg1 Google Group, because
I'd be happy if there were
mailman-style mailing list for the group.The current setup really doesn't work well for me at all, which is disappointing, as lots of the discussions I've overcome the awkwardness of the interface to read are really very interesting.
I've just finished reading "Xoc, an Extension-Oriented Compiler for Systems Programming", by Russ Cox, Tom Bergany, Austin T. Clements, Frans Kaashoek, and Eddie Kohlery. It's a description of an interesting approach to extensible compilation, for C in this case, that refines a lot of ideas that have been explored previously, bringing them together in a more compositional style than previous extensible compilers have managed to achieve.
One of the key themes in the paper is how to control extension visibility. They describe a couple of approaches they take: one, a type system used to ensure that extensions to abstract syntax are only visible to other extensions when those extensions explicitly declare knowledge of them; and two, an approach from Warth et al.'s "Statically scoped object adaptation with expanders" paper for controlling visibility of extensions to core data structures. I wonder if the latter is used to underpin the former at all.
Regarding the type system approach they take, it's interesting how it lets the extension writer provide analyses directly in terms of extension abstract syntax, rather than forcing translation to a core language before performing any analysis. We did the latter in Highwire, and the error messages referred to core-language constructs which didn't map well to source-language syntax: the user was forced to unexpand the macro in their heads to make sense of the output of the compiler. The paper refers to this problem in section 5.2, where the authors write "Since macros cannot access compiler internals, they typically do not even type check their arguments, producing cryptic errors when invoked incorrectly."
Their compiler, xoc, is a source-to-source translator at the moment, which means it's not integrated with code generation and backend optimisations and rewrites. It'd be fascinating to build a complete compiler in this style. It'd also be neat to relate this approach to extensible semantics. At the moment the extensions look quite ad-hoc. How could this framework be used, for example, to extend Scheme or ML, where there's a real semantics underneath it?
One nice feature of xoc is that it tries to preserve hygiene during
rewriting. I'm curious as to whether it preserves hygiene to the
degree that, say, syntax-case does; their description of the feature
was a bit terse for me to be able to tell. In particular I'm
interested in how it treats keywords, and where "fresh names" come in
to the picture; the paper speaks simply of the code emitter renaming
variables "when two distinct variables share a name".
They use a GLR parser for converting concrete syntax to abstract syntax. One of the benefits of this is its ability to deal with ambiguity, which they exploit in a couple of ways, most ingeniously in their approach to type inference (if you like) for syntax pattern variables. I'd like to learn more about how xoc computes the type of a syntax pattern variable; I wonder if a similar system could be ginned up for use with OMeta?
The laziness of most of xoc's attribute computations is rather nifty. Code generated relatively late in a compilation, when earlier passes are complete for the rest of the code, can have necessary information filled in lazily on demand, running just those parts of the previous analyses that are required.
I'd like to see some of the pattern-matching and reconstruction features of xoc employed in the context of general purpose computing. It seems to me that using a GLR parser the way they do could effectively provide concrete domain-specific syntax for algebraic data types, not just abstract syntax trees. (The question arises: when is an AST an ADT? Always?) Being able to cons up syntax for new types as easily as Haskell and ML cons up the types themselves seems like a win it's worth experimenting with. OMeta goes a step in this direction; I hope to explore it myself sometime soon.
This article is an excellent, excellent introduction to Git. It covers the low-level basics, which are much simpler and more elegant than you might be expecting1, and then reaches up to the less elegant but very powerful world of the Git user interface. Highly recommended.
though of course, you might expect me to say that, given my own background! ↩
Most thinking about messaging seems to be framed in terms of process languages, like the π-calculus. In the π-calculus, processes evolve according to an internal clock, the ticks of which correspond to communication events between subprocesses. Many real programming languages based around messaging, like Erlang, are based around similar models, meaning that programmers write programs whose notion of time is implicit, rather than explicit. This can make it difficult to write programs which can reason about differing orders of events or cope with contradictory assertions about the state of the outside world.
An alternative way of dealing with messaging is to take an epistemic reasoning approach closely connected with Functional Reactive Programming (FRP): instead of writing programs that wait for the arrival of specific messages, one at a time, write programs that deal with a queryable monotonically increasing collection of all messages that have arrived or departed so far. Metadata about each message—the sender, the recipient, the time of receipt—are reified as queryable fields attached to each datum in the collection. The state of the program's world is directly derivable from the program itself and the collection of messages that have been exchanged thus far.
By letting programs step back from the low-level detail of the exchange of messages and take a broader overview of the whole conversation thus far, we let programs behave much more flexibly in the face of such common distributed-systems challenges as lost messages, duplicated messages, out-of-order messages, inconsistent data, and timeouts.
I've been off-and-on thinking about these timeless 'epistemic messaging' systems for a couple of years now, since a customer project let us investigate their potential suitability for a particular style of long-lived database; while I could write more, and hope to in future, I want to record an observation that occurred to me a couple of days ago about a connection between modeless user interfaces and timeless messaging systems.
In the same way that modal user interfaces constrain the interaction between the user and program into a sequential, question-and-answer style, π-calculus-style programs constrain the interaction between programs in a distributed system into a similar one-thing-at-a-time sequential style, which can lead to programs having a sensitive dependence on message arrival order. Modeless user interfaces, by contrast, make possible a more open-ended, non-linear style of interaction with the user, and epistemic messaging does the same thing for program-to-program interaction: because the state of each program (and thereby its next action) is derived from the collection of messages that have been exchanged, the program can more easily be written to cope with messages arriving in an unusual order. Epistemic-messaging programs can even be written to be able to re-plan (and maybe even re-execute) their actions if received messages were to be permitted to be replaced mid-run.
(Aside: I wonder if there's a connection here to the Curry-Howard isomorphism? A program operating epistemically over a collection of messages feels like a kind of type/formula, where a similar program in a π-calculus-like system feels like a program/proof.)
We make a few programs that we've developed available on this server. You can download them from our source-code repositories:
We're gradually switching away from darcs to mercurial or git. You can browse the mercurial-controlled code with hgwebdir at 80/20.
tonyg's projects:
ometa-scheme: An implementation of OMeta for scheme. Currently it runs in MzScheme, but it should be fairly portable, with dependencies only on a handful of commonly-implemented SRFIs. For more information, see the ometa-scheme blog post.
smalltalk-tng: An experimental language I'm developing.
While we generally prefer to use mercurial, git is more appropriate for some projects.
tonyg's projects:
Most of our older projects are currently held in these archives, though as we switch to Mercurial and Git, this will change. You can browse the darcs-controlled code with DarcsWeb at 80/20.
tonyg's archive: http://www.eighty-twenty.org/~tonyg/Darcs/
chicken-sdl: SDL bindings for Chicken Scheme
macromod:
A portable (re-)implementation of a syntax-case
Scheme macro-expander; eventually to sprout a portable implementation of
Matthew
Flatt's "macromod" module system
scheme-pg: Some small changes I made to the scheme-pg Scheme PostgreSQL bindings
mikeb's archive: http://www.eighty-twenty.org/~mikeb/Darcs/
chicken-cairo: Cairo bindings for Chicken Scheme
fuschia-rdf: A sketch of an RDF aggregating blog engine in Python
sticky-wiki: A half-arsed variation on TiddlyWiki that looks a bit like sticky notes.
zowie: An experimental foundation for a zooming user interface.
These repositories are obsolete, as we've mostly switched to using darcs.
tla register-archive http://www.eighty-twenty.org/archives/2004/tla register-archive http://www.eighty-twenty.org/archives/2005/Recently I've been teaching myself PowerPC assembly through porting JONESFORTH to PowerPC on Mac OS X. It struck me to run the same little fibonacci-sequence microbenchmark that I ran lo these many years past. The results were interesting:
| Language | Implementation Detail | Time (per (fib 29) call, in milliseconds) | Ops/s | Ratio (opt. C) | Ratio (unopt. C) |
|---|---|---|---|---|---|
| PPC assembly | - | 24 | 935983000 | 0.43 | 0.205 |
| FORTH | JONESFORTH ported to PPC | 277 | 81096000 | 4.95 | 2.37 |
The hand-coded assembly beats all the other entrants (perhaps unsurprisingly). The naive indirect-threaded FORTH is the fastest interpreted language, merely 5 times slower than fully optimised C.
Here's the JONESFORTH code:
: FIB DUP 2 >= IF 1- DUP RECURSE SWAP 1- RECURSE + ELSE DROP 1 THEN ;
and here's the PPC assembly (arg and result in r3):
_SFIB: cmpwi r3,2
bge 1f
li r3,1
blr
1: mflr r0
stw r0,-4(r1)
addi r3,r3,-1
stwu r3,-8(r1)
bl _SFIB
lwz r4,0(r1)
stw r3,0(r1)
addi r3,r4,-1
bl _SFIB
lwz r4,0(r1)
add r3,r3,r4
lwz r0,4(r1)
addi r1,r1,8
mtlr r0
blr
Gordon Weakliem writes about literal hash-table syntax, and touches on some of the problems that programmers run in to with floating-point numbers. He writes:
12 years ago, I spent half a day in the debugger trying to figure out why the computer would not believe that 0.0 == 0.0 [...]
This triggered two thoughts:
Regarding reflective access to floats, Java provides floatToIntBits
and intBitsToFloat,
and C provides (quasi-portable) pointer-based memory-aliasing
type-punning to the same effect: float f = 1.23; int i = *((int
*) &f);.
Submatching in a pattern, +var@pattern, looks
suspiciously similar to recursion in a
value, var=value. In each case, we expect a value which
is both named and processed, and in each case, a binding is introduced
into code. The processing in the submatch case is a further
pattern-match operation, and in the recursion case it is a nested
evaluation.
The main difference, besides the overall context, between the two
forms is the scope of the introduced binding: in the submatch, the
pattern variable to the left of the @ sign is bound only
in the right-hand-side of the overall pattern, at present, whereas in
the recursive value the variable to the left of the = is
bound in the right-hand-side of the recursion itself. Another
important difference is that in the submatch context,
the +var is clearly just a binding form, whereas in the
recursion context, var can be seen as acting both as a
binding form and a reference to the bound value. Another way of
looking at it, though, has the var acting solely as a
binding form, with the right-hand-side of the = being the
actual result of the expression — the fact that the left and
right-hand-sides are equivalent is not relevant, in this
interpretation.
What if the two forms were unified, based on their similarities,
using either var=pattern-or-value
or +var=pattern-or-value for both pieces of syntax? The
former seems out-of-place for submatches, since we want to clearly
mark bindings that will propagate their bound value into the code on
the right-hand-side of an object clause; and the latter seems
out-of-place in a recursive value, since +var implies a
binding object, which is a first-class datum in ThiNG.
Compare and contrast the following — a string-interleaving routine using the current syntax for both submatch and recursion:
sepList: [+s: loop=[(cons +x +more@(cons _ _)): (x ++ s ++ loop more)
(cons +x _): x
_: EmptyString]]
The same routine using +var=pattern-or-value for
both:
sepList: [+s: +loop=[(cons +x +more=(cons _ _)): (x ++ s ++ loop more)
(cons +x _): x
_: EmptyString]]
Finally, the same routine using var=pattern-or-value for
both:
sepList: [+s: loop=[(cons +x more=(cons _ _)): (x ++ s ++ loop more)
(cons +x _): x
_: EmptyString]]
Finally, there's the question of scope: if the same syntax is used for both submatch and for recursion, what happens if we adjust the scoping rules to be as similar as possible? It doesn't make sense for recursive-value's scoping rule to be changed to be the same as submatch's scoping rule — there's no right-hand-side in a value for the binding to be visible in! — but the other way around might be worth considering: let the binding introduced as part of a submatch be visible as part of the right-hand-side of the submatch. What happens then? Do we end up with infinite patterns? Do we instead end up with a kind of unification-constraint in which an object matching a submatch that is referred to within that same submatch is constrained to have the same recursive topology as the pattern itself?
[Update: Something I forgot to mention — with the scope rule for submatches as it stands, it's possible to evaluate and then compile patterns ahead of execution time. Changing the scope rule can make compilation of ThiNG code more difficult, forcing the system to be more late-bound than perhaps it needs to be.]
The other day, I noted that my generic-monad-in-Scheme implementation suffered from the flaw of not being able to type-check monad assemblies before any actions had been executed, and that in general such ahead-of-time checking is not possible without abstract interpretation of some kind, because the functional code in the right-hand-side of the bind operator can decide which action to splice into the monad next:
(define (goes-wrong)
(run-io (mlet* ((val mread))
(if val ;; if the user entered #f, we break, otherwise we're fine
(mlet* ((_ (mdisplay "Was true\n"))) (return 'apples))
'bananas)))) ;; Note: missing "(return ...)"!
Perhaps a partial-evaluator can play the part of a Haskell-type-system-like abstract interpretation well enough to drive the type-safety checks through the code at PE time (loosely equivalent to static-analysis time) for most cases (leaving the remaining cases for runtime-detection, as in the present implementation, and perhaps warning the user)? In order to find out, my next experiment in this direction will be marrying the monad code to the partial-evaluator I implemented some months ago.