Midwest.io 2015
Joel Martin
Press 's' to show speaker notes
Joel Martin (kanaka)
Principal Software Engineer at ViaSat, Inc
Clojure
Satellites!
The Best Languages for the Organization Task/Project System
It's a Polyglot World
Language learning is part of the job.
The typical process:
“Why will people pay for the privilege of working harder than they will work when they are paid?
-- The Game of Work: Charles Coonradt”
"Work" is not a thing, it's a state of mind.
...
function read_form(reader) {
var token = reader.peek();
switch (token) {
// reader macros/transforms
case ';': return null; // Ignore comments
case '\'': reader.next();
return [types._symbol('quote'), read_form(reader)];
case '`': reader.next();
return [types._symbol('quasiquote'), read_form(reader)];
case '~': reader.next();
return [types._symbol('unquote'), read_form(reader)];
...
...
define READ_FORM
$(and $(READER_DEBUG),$(info READ_FORM: $($(1))))
$(call READ_SPACES,$(1))
$(foreach ch,$(word 1,$($(1))),\
$(if $(filter $(SEMI),$(ch)),\
$(call DROP_UNTIL,$(1),$(_NL)),\
$(if $(filter $(SQUOTE),$(ch)),\
$(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\
$(call _list,$(call _symbol,quote) $(strip $(call READ_FORM,$(1)))),\
$(if $(filter $(QQUOTE),$(ch)),\
$(eval $(1) := $(wordlist 2,$(words $($(1))),$($(1))))\
$(call _list,$(call _symbol,quasiquote) $(strip $(call READ_FORM,$(1)))),\
$(if $(filter $(UNQUOTE),$(ch)),\
...
"Make Lisp"
...
READ_FORM () {
local token=${__reader_tokens[${__reader_idx}]}
case "${token}" in
\') __reader_idx=$(( __reader_idx + 1 ))
_symbol quote; local q="${r}"
READ_FORM; local f="${r}"
_list "${q}" "${f}" ;;
\`) __reader_idx=$(( __reader_idx + 1 ))
_symbol quasiquote; local q="${r}"
READ_FORM; local f="${r}"
_list "${q}" "${f}" ;;
...
...
MalVal *read_form(Reader *reader) {
char *token;
MalVal *form = NULL, *tmp;
token = reader_peek(reader);
if (!token) { return NULL; }
switch (token[0]) {
case ';':
abort("comments not yet implemented");
break;
case '\'':
reader_next(reader);
form = _listX(2, malval_new_symbol("quote"),
read_form(reader));
break;
...
...
def read_form(reader):
token = reader.peek()
# reader macros/transforms
if token[0] == ';':
reader.next()
return None
elif token == '\'':
reader.next()
return _list(_symbol('quote'), read_form(reader))
elif token == '`':
reader.next()
return _list(_symbol('quasiquote'), read_form(reader))
...
...
;; Override some tools.reader reader macros so that we can do our own
;; metadata and quasiquote handling
(alter-var-root #'r/macros
(fn [f]
(fn [ch]
(case ch
\` (wrap 'quasiquote)
\~ (fn [rdr comma]
(if-let [ch (rt/peek-char rdr)]
(if (identical? \@ ch)
((wrap 'splice-unquote) (doto rdr rt/read-char) \@)
((wrap 'unquote) rdr \~))))
...
...
function read_form($reader) {
$token = $reader->peek();
switch ($token) {
case '\'': $reader->next();
return _list(_symbol('quote'),
read_form($reader));
case '`': $reader->next();
return _list(_symbol('quasiquote'),
read_form($reader));
case '~': $reader->next();
return _list(_symbol('unquote'),
read_form($reader));
...
...
public static MalVal read_form(Reader rdr)
throws MalContinue, ParseError {
String token = rdr.peek();
if (token == null) { throw new MalContinue(); }
MalVal form;
switch (token.charAt(0)) {
case '\'': rdr.next();
return new MalList(new MalSymbol("quote"),
read_form(rdr));
case '`': rdr.next();
return new MalList(new MalSymbol("quasiquote"),
read_form(rdr));
...
"Make Lisp" became "Make-A-Lisp"
Demo Time
...
% read_form: read the next form from string start at idx
/read_form { 3 dict begin
read_spaces
/idx exch def
/str exch def
idx str length ge { null str idx }{ %if EOF
/ch str idx get def % current character
ch 39 eq { %if '\''
/idx idx 1 add def
str idx read_form
3 -1 roll /quote exch 2 _list 3 1 roll
...
...
public static MalVal read_form(Reader rdr) {
string token = rdr.peek();
if (token == null) { throw new MalContinue(); }
MalVal form = null;
switch (token) {
case "'": rdr.next();
return new MalList(new MalSymbol("quote"),
read_form(rdr));
case "`": rdr.next();
return new MalList(new MalSymbol("quasiquote"),
read_form(rdr));
...
...
def read_form(rdr)
return case rdr.peek
when ";" then nil
when "'" then rdr.next; List.new [:quote, read_form(rdr)]
when "`" then rdr.next; List.new [:quasiquote, read_form(rdr)]
when "~" then rdr.next; List.new [:unquote, read_form(rdr)]
when "~@" then rdr.next; List.new [:"splice-unquote", read_form(rdr)]
when "^" then rdr.next; meta = read_form(rdr);
List.new [:"with-meta", read_form(rdr), meta]
...
...
sub read_form {
my($rdr) = @_;
my $token = $rdr->peek();
given ($token) {
when("'") { $rdr->next(); List->new([Symbol->new('quote'),
read_form($rdr)]) }
when('`') { $rdr->next(); List->new([Symbol->new('quasiquote'),
read_form($rdr)]) }
when('~') { $rdr->next(); List->new([Symbol->new('unquote'),
read_form($rdr)]) }
...
...
func read_form(rdr Reader) (MalType, error) {
token := rdr.peek()
if token == nil {
return nil, errors.New("read_form underflow")
}
switch *token {
case `'`:
rdr.next()
form, e := read_form(rdr)
if e != nil {
return nil, e
}
return List{[]MalType{Symbol{"quote"}, form}, nil}, nil
...
...
fn read_form(rdr : &mut Reader) -> MalRet {
let otoken = rdr.peek();
let stoken = otoken.unwrap();
let token = &stoken[..];
match token {
"'" => {
let _ = rdr.next();
match read_form(rdr) {
Ok(f) => Ok(list(vec![symbol("quote"), f])),
Err(e) => Err(e),
}
},
...
...
read_form <- function(rdr) {
token <- Reader.peek(rdr)
if (token == "'") {
. <- Reader.next(rdr);
new.list(new.symbol("quote"), read_form(rdr))
} else if (token == "`") {
. <- Reader.next(rdr);
new.list(new.symbol("quasiquote"), read_form(rdr))
} else if (token == "~") {
. <- Reader.next(rdr);
new.list(new.symbol("unquote"), read_form(rdr))
...
...
read_form = (rdr) ->
token = rdr.peek()
switch token
when '\'' then [_symbol('quote'), read_form(rdr.skip())]
when '`' then [_symbol('quasiquote'), read_form(rdr.skip())]
when '~' then [_symbol('unquote'), read_form(rdr.skip())]
when '~@' then [_symbol('splice-unquote'), read_form(rdr.skip())]
when '^'
meta = read_form(rdr.skip())
[_symbol('with-meta'), read_form(rdr), meta]
when '@' then [_symbol('deref'), read_form(rdr.skip())]
...
...
Shared Function read_form(rdr As Reader) As MalVal
Dim token As String = rdr.peek()
If token Is Nothing Then
throw New MalContinue()
End If
Dim form As MalVal = Nothing
Select token
Case "'"
rdr.get_next()
return New MalList(New MalSymbol("quote"),
read_form(rdr))
...
...
def read_form(rdr: Reader): Any = {
return rdr.peek() match {
case "'" => { rdr.next; _list(Symbol("quote"), read_form(rdr)) }
case "`" => { rdr.next; _list(Symbol("quasiquote"), read_form(rdr)) }
case "~" => { rdr.next; _list(Symbol("unquote"), read_form(rdr)) }
case "~@" => { rdr.next; _list(Symbol("splice-unquote"), read_form(rdr)) }
case "^" => { rdr.next; val meta = read_form(rdr);
_list(Symbol("with-meta"), read_form(rdr), meta) }
case "@" => { rdr.next; _list(Symbol("deref"), read_form(rdr)) }
...
...
read_form :: Parser MalVal
read_form = do
ignored
x <- read_macro
<|> read_list
<|> read_vector
<|> read_hash_map
<|> read_atom
return $ x
read_str :: String -> IOThrows MalVal
read_str str = case parse read_form "Mal" str of
...
...
(define (read_form rdr)
(let ([token (send rdr peek)])
(if (null? token)
(raise (make-blank-exn "blank line" (current-continuation-marks)))
(cond
[(equal? "'" token) (send rdr next) (list 'quote (read_form rdr))]
[(equal? "`" token) (send rdr next) (list 'quasiquote (read_form rdr))]
[(equal? "~" token) (send rdr next) (list 'unquote (read_form rdr))]
[(equal? "~@" token) (send rdr next) (list 'splice-unquote (read_form rdr))]
[(equal? "^" token) (send rdr next)
(let ([meta (read_form rdr)])
(list 'with-meta (read_form rdr) meta))]
...
...
function M.read_form(rdr)
local token = rdr:peek()
if "'" == token then
rdr:next()
return List:new({Symbol:new('quote'), M.read_form(rdr)})
elseif '`' == token then
rdr:next()
return List:new({Symbol:new('quasiquote'), M.read_form(rdr)})
elseif '~' == token then
rdr:next()
return List:new({Symbol:new('unquote'), M.read_form(rdr)})
...
...
and read_form all_tokens =
match all_tokens with
| [] -> raise End_of_file;
| token :: tokens ->
match token with
| "'" -> read_quote "quote" tokens
| "`" -> read_quote "quasiquote" tokens
| "~" -> read_quote "unquote" tokens
| "~@" -> read_quote "splice-unquote" tokens
| "@" -> read_quote "deref" tokens
...
Buzzword alert: Gamification
Make-A-Lisp is gamification of language learning
Measure of progress towards goal
Freedom to choose how to succeed
make test^MY_IMPL
make test^MY_IMPL^stepX