Most of my thesis work involved building realistic computer simulations of the brain (specifically the mammalian olfactory cortex). In the process of doing this work, I discovered that I really liked programming and I've ended up spending a lot of time learning new programming languages and programming tools just for fun. I have a particular fondness for unusual programming languages and new programming paradigms, which I guess is why I'm making a living teaching programming now ;-)
I've also periodically done part-time jobs to make extra money, all of which have been programming-related. I describe my computer skills and the jobs I've worked on below.
NOTE: Some parts of this document are obsolete and need an overhaul. What this means in practice is that I actually know and have worked on more stuff than is in this document, though I try to keep it reasonably up to date. The HTML also needs to be cleaned up.
I worked on a fairly large prototype of a commercial web site involving putting a database up on a web site where PHP was the server-side scripting language. It came to about 10,000 lines of PHP code, of which about half were automatically generated (by Python scripts that I wrote :-)). Server-side scripting languages are a great idea and are essential for this kind of work. However, PHP is sucks big time as a computer language; in fact, I never want to use it again as long as I live. The way arrays are implemented is particularly hilarious. (Maybe the language has improved now; I don't know.) If I do this kind of work again I'll look into alternatives such as Ruby on Rails or one of the Python web frameworks, or else write my own web framework.
The GENESIS neural simulator I worked with for my Ph.D. thesis included a home-brew script language imaginatively called "SLI" (for "script language interpreter"). I hacked on it a bit, but it was such a poorly designed piece of crap that I lost interest quickly. It served as a useful illustration of how not to design a language.
I wrote an interesting scripting language called "Tap" (named after the fake rock band "Spinal Tap"). I designed it myself and wrote it entirely from scratch in ocaml. The idea behind Tap was to create a hybrid of Forth and Scheme. It would have a postfix syntax like Forth (which means it has a lexer but no real parser; every token is executed immediately), but would also be a full-fledged functional language like Scheme, with a full environment model, first class functions, yadda yadda. I was interested to see if merging the Forth approach to programming with the Scheme approach would be interesting. And, in fact, it was interesting. Here is some sample Tap code:
# Factorial, the mother of all examples. { dup 0= { drop 1 } { dup 1- factorial * } if-else } 'factorial define 10 factorial p # Tail-recursive factorial. { { dup 0= { drop } { tuck * swap 1- iter } if-else } 'iter define 1 swap iter } 'tr-factorial define # The applicative-order Y combinator. Pretty intuitive, wouldn't you say? { { { dup apply apply } block-push @ apply } block-put { dup apply } apply } 'y define # Factorial defined using the Y combinator. { 'fact define { dup 0 = { drop 1 } { dup 1- fact * } if-else } } y 'y-factorial define 100 y-factorial p # Prints: 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 # (nearly instantaneously). # Another way to do factorial, using the "linrec" combinator stolen from # the Joy language: { # Here's how you do local variables in tap: [ if-clause then-clause rec1-clause rec2-clause ] locals { dup if-clause { then-clause } { rec1-clause f rec2-clause } if-else } 'f define `f } 'linrec define { 0= } { 1+ } { dup 1- } { * } linrec 'lr-factorial define # Testing the partial quote capability. Full quoting (using the single quote # character) just puts the name being quoted on the stack. Partial quoting # (using the backtick character) looks up the name and puts the resulting # value on the stack, even if it's a function. The normal execution model is # to look up a name, execute it if it's a function, and otherwise put it on # the stack. So partial quoting is useful for treating functions as data. { dup * } 'squared define 10 squared p # Prints 100. 10 `squared apply p # Also prints 100. # Creating a new control structure. "repeat" is actually built-in. { [ block count ] locals { count 0 > } { block count 1- 'count set! } while } 'repeat define # Drop an arbitrary number of elements from the stack. { `drop swap repeat } 'ndrop define # Finding the roots of a quadratic equation. { [ a b c ] locals b dup * 4.0 a c * * - sqrt 'd define b ~ d + 2.0 a * / # ~ is the negation operator. b ~ d - 2.0 a * / } 'solve-quadratic-equation define # While loop. "while" is actually built-in. { [ test block ] locals { test { block iter } if } 'iter define iter } 'while define # Closures. { 'n define { n + } } 'add-n define 10 add-n 'add-10 define # Another way to do closures. { { + } block-push } 'add-n define 10 add-n 'add-10b define # Quicksort. Not as nice as the Haskell version :-( # I could probably do better than this. { dup list-empty? not { list-split [ pivot rest ] locals ( ) ( ) [ less greater ] locals rest { dup pivot < { less cons 'less set! } { greater cons 'greater set! } if-else } list-for-each less quicksort ( pivot ) list-append greater quicksort list-append } if } 'quicksort define
I learned a lot from working on Tap, even though I don't work on it or use the language any more. I found that I had to fight the tendency to make the language overly complex, because ocaml made it so easy to make big changes (even stupid ones). Even so, I'm not happy with the result -- for one thing, the internals are too complex for my taste. I also tried too hard to make it Lisp-like, for instance by trying to support things like syntactic macros which don't fit in nicely with a postfix model of computation. Similarly, I tried too hard to make the base syntax user-modifiable. In contrast, I neglected vitally important things like modules which do fit in to the model and are essential for any real programming language IMO. Also, using ocaml as the implementation language was a mixed blessing. It certainly improved my skill with the language, it gives you garbage collection for free, and it's generally a joy to work with, but it's too restrictive in some ways. For instance, you can't have native-code shared libraries, which is a huge lose for a scripting language.
Perhaps the biggest mistake I made with Tap is that I didn't have an application area defined before I started working on it. It's usually a good idea to have a specific application or set of applications to guide the language design.
Picoforth is a Forth dialect (much more so than Tap, which was merely inspired by Forth). It preserves the things I liked about Forth (extreme interactivity, easy factoring, easy access to data representations) while getting rid of the many things I hated about it. Its goal was to be a scripting language for applications or libraries written in C (much like most scripting languages) and it's was written in C. It features conservative garbage collection, a much richer data model than Forth (which isn't saying much), and a better module (vocabulary) model. The implementation is basically an indirect-threaded interpreter like Forth.
One of these days, though, I really have to sit down and write a Forth implementation from scratch in assembly language, if only to teach myself assembly language. That's how Chuck Moore did it originally, and if it's good enough for Chuck...
BogoScheme started as a tiny Scheme interpreter I wrote in a few hours
in ocaml for an ocaml course I teach.
There was nothing particularly interesting about the original version, aside
from the fact that it was a Turing-complete Scheme interpreter in about 700
lines of code, with first-class functions and correct lexical scoping. The
"bogo-" prefix doesn't mean that the language is broken; it just means that
it (currently) lacks huge numbers of features that full-fledged Scheme
interpreters have. Also, I liked the idea that source code files would have
names ending in ".bs"
. Truth in advertising!
Of course, no language ever stays small for long, and since I wrote BogoScheme I've been systematically expanding it, with the goal of making a full R6RS-compliant Scheme implementation (the R6RS standard isn't finalized yet, but a draft is available at http://www.r6rs.org). I also want to experiment with writing a Scheme compiler for the language, something I've been interested in ever since I read Abelson and Sussman's Structure and Interpretation of Computer Programs. And, of course, it's really fun.
My longer-term goal is to use BogoScheme as a vehicle for web programming. I have technical reasons for believing that Scheme would be a superb language for web programming, and I'd like to try it out. Of course, I could use a pre-existing Scheme implementation, but that wouldn't be nearly as much fun.
mosh is a Unix shell I've written in Ocaml. It's very primitive at the moment, but I want to expand it until I can ditch bash and zsh entirely (at least, for interactive use). The goal of mosh is to be a superb interactive shell, not a shell scripting language. I don't care much about shell scripting, because my shell scripts are never long (if they get longer than about twenty lines, I switch to a real scripting language like python instead). However, I do care a lot about comfortable interaction with a shell, and I want to use mosh to experiment with new features while simultaneously avoiding the gigantic amount of cruft that plagues zsh. Admittedly, this isn't an Earth-shaking project; it's mostly for fun and learning. The name "mosh" stands for "Mike's Ocaml SHell".
Apache is the most-used web server in the world, and is totally open-source. I've compiled and set up an Apache server for my database-backed web site project. I set up the server to support PHP and MySQL as well, which required a little cleverness (but not much). I like Apache; it seems to be robust and effective.
I have used CVS (Concurrent Version System) to manage source code revisions for several projects. It's not perfect, but it gets the job done. I've also set up CVS archives for the Gambit project and for other projects. Then I switched to Subversion, which I preferred, but not by much. Now I use Darcs (written in Haskell, by the way).
Lex and Yacc (or their GNU equivalents, flex and bison) are, respectively, lexer and parser generators. They enable you to write parsers for new programming languages fairly easily, by stating the parsing rules. I've done some work on Yacc for the Tspice project, and I used lex extensively for my cellreader library in GENESIS. I also know the Ocaml equivalents, ocamllex and ocamlyacc, quite well.
Go back to my home page. | Last updated April 11, 2017 |
Mike Vanier (mvanier@cs.caltech.edu)