Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

An experimental, small, readable lisp with thorough unit tests and extensible functions/macros.

branch: master

3427 - support overflowing bounds in slices

http://arclanguage.org/item?id=18451

This slight reorg caused enq/deq to break because they were
unintentionally overriding 'len which they now suddenly found themselves
needing to call. First time I've regretted the lisp-1 design decision.
latest commit ba51807859
Kartik Agaram authored April 19, 2014
Octocat-spinner-32 alternatives 3411 March 04, 2014
Octocat-spinner-32 literate 3360 January 20, 2014
Octocat-spinner-32 000test.cc 3100 July 08, 2013
Octocat-spinner-32 001trace.cc 3426 - clear trace analysis cruft April 19, 2014
Octocat-spinner-32 001trace.test.cc 3137 July 13, 2013
Octocat-spinner-32 002main.cc 3414 March 04, 2014
Octocat-spinner-32 003tokenize.cc 3162 - tange replace{} directive for ranges of lines July 22, 2013
Octocat-spinner-32 003tokenize.test.cc 3347 - simpler messages on test failures January 09, 2014
Octocat-spinner-32 004optional_parens 2444 December 16, 2012
Octocat-spinner-32 004parenthesize.cc 3223 - bugfix: unbalanced parens shouldn't break repl September 27, 2013
Octocat-spinner-32 004parenthesize.test.cc 3064 - macros for checking warnings July 04, 2013
Octocat-spinner-32 005parse.cc 3138 July 13, 2013
Octocat-spinner-32 005parse.test.cc 3138 July 13, 2013
Octocat-spinner-32 006infix 2358 November 05, 2012
Octocat-spinner-32 006infix.cc 3225 September 27, 2013
Octocat-spinner-32 006infix.test.cc 3224 - bugfix: prefix negation without spaces September 27, 2013
Octocat-spinner-32 010cell.cc 3287 November 08, 2013
Octocat-spinner-32 010cell.test.cc 3145 July 14, 2013
Octocat-spinner-32 010memory 3418 March 13, 2014
Octocat-spinner-32 011types.cc 3284 - optimize false like other common syms November 07, 2013
Octocat-spinner-32 011types.test.cc 3218 - 40% speed boost August 27, 2013
Octocat-spinner-32 012compiledfn.cc 3142 July 13, 2013
Octocat-spinner-32 013object.cc 3291 - macroexpand now working reliably November 09, 2013
Octocat-spinner-32 013object.test.cc 3291 - macroexpand now working reliably November 09, 2013
Octocat-spinner-32 014build.cc 3138 July 13, 2013
Octocat-spinner-32 014build.test.cc 3139 - bugfix in gc tests July 13, 2013
Octocat-spinner-32 015$var.cc 3054 June 30, 2013
Octocat-spinner-32 015$var.test.cc 2847 - The great rename of 2013 May 28, 2013
Octocat-spinner-32 020lookup.cc 3385 February 24, 2014
Octocat-spinner-32 020lookup.test.cc 3218 - 40% speed boost August 27, 2013
Octocat-spinner-32 021eval.cc 3426 - clear trace analysis cruft April 19, 2014
Octocat-spinner-32 021eval.test.cc 3426 - clear trace analysis cruft April 19, 2014
Octocat-spinner-32 021keyword_args 2291 October 30, 2012
Octocat-spinner-32 022primitives.cc 3343 January 08, 2014
Octocat-spinner-32 022primitives.test.cc 3284 - optimize false like other common syms November 07, 2013
Octocat-spinner-32 023list.cc 3317 - two bugs in sort December 08, 2013
Octocat-spinner-32 023list.test.cc 3139 - bugfix in gc tests July 13, 2013
Octocat-spinner-32 024string.cc 3284 - optimize false like other common syms November 07, 2013
Octocat-spinner-32 024string.test.cc 3283 - predicates return false, comparisons short-circuit false November 07, 2013
Octocat-spinner-32 025num.cc 3288 November 08, 2013
Octocat-spinner-32 025num.test.cc 3288 November 08, 2013
Octocat-spinner-32 026table.cc 3142 July 13, 2013
Octocat-spinner-32 027stream.cc 3138 July 13, 2013
Octocat-spinner-32 028io.cc 3426 - clear trace analysis cruft April 19, 2014
Octocat-spinner-32 029posix.cc 3138 July 13, 2013
Octocat-spinner-32 030tangle.cc 3361 - clearer commandline parsing January 28, 2014
Octocat-spinner-32 030tangle.test.cc 3174 - replace{} can now access :OLD_CONTENTS July 25, 2013
Octocat-spinner-32 038tools.cc 3276 November 06, 2013
Octocat-spinner-32 039load.cc 3404 - undo 3401 March 04, 2014
Octocat-spinner-32 040.test 3305 - all progress messages are now consistently in main.cc November 15, 2013
Octocat-spinner-32 040.wart 3406 March 04, 2014
Octocat-spinner-32 041test.test 3277 November 06, 2013
Octocat-spinner-32 041test.wart 3408 March 04, 2014
Octocat-spinner-32 042fn.test 3282 - experiment: a new false sym November 07, 2013
Octocat-spinner-32 042fn.wart 3295 November 12, 2013
Octocat-spinner-32 043list.test 2595 February 20, 2013
Octocat-spinner-32 043list.wart 3286 November 07, 2013
Octocat-spinner-32 044bind.test 3424 - loop/recur April 05, 2014
Octocat-spinner-32 044bind.wart 3424 - loop/recur April 05, 2014
Octocat-spinner-32 045check.test 3409 March 04, 2014
Octocat-spinner-32 045check.wart 3346 - warning: wart is broken January 09, 2014
Octocat-spinner-32 046type.wart 3257 October 28, 2013
Octocat-spinner-32 047generic.test 3271 October 31, 2013
Octocat-spinner-32 047generic.wart 3407 March 04, 2014
Octocat-spinner-32 048control.test 3282 - experiment: a new false sym November 07, 2013
Octocat-spinner-32 048control.wart 3297 November 12, 2013
Octocat-spinner-32 049assign.test 3266 October 30, 2013
Octocat-spinner-32 049assign.wart 3266 October 30, 2013
Octocat-spinner-32 050list2.test 3427 - support overflowing bounds in slices April 19, 2014
Octocat-spinner-32 050list2.wart 3427 - support overflowing bounds in slices April 19, 2014
Octocat-spinner-32 051mutate.test 3272 November 01, 2013
Octocat-spinner-32 051mutate.wart 3272 November 01, 2013
Octocat-spinner-32 052num.test 3317 - two bugs in sort December 08, 2013
Octocat-spinner-32 052num.wart 3319 - don't break a future function comparator December 08, 2013
Octocat-spinner-32 053string.test 3427 - support overflowing bounds in slices April 19, 2014
Octocat-spinner-32 053string.wart 3427 - support overflowing bounds in slices April 19, 2014
Octocat-spinner-32 054table.test 3282 - experiment: a new false sym November 07, 2013
Octocat-spinner-32 054table.wart 2596 - use '+' for compose instead of ':' February 21, 2013
Octocat-spinner-32 055queue.test 3282 - experiment: a new false sym November 07, 2013
Octocat-spinner-32 055queue.wart 3427 - support overflowing bounds in slices April 19, 2014
Octocat-spinner-32 056collect.test 2596 - use '+' for compose instead of ':' February 21, 2013
Octocat-spinner-32 056collect.wart 2679 - undo 2678 April 07, 2013
Octocat-spinner-32 057sym.test 3318 December 08, 2013
Octocat-spinner-32 057sym.wart 3317 - two bugs in sort December 08, 2013
Octocat-spinner-32 058with.test 2593 February 20, 2013
Octocat-spinner-32 058with.wart 2596 - use '+' for compose instead of ':' February 21, 2013
Octocat-spinner-32 059stream.test 3227 - fix several eof bugs with stream input September 27, 2013
Octocat-spinner-32 059stream.wart 3247 - prrn October 12, 2013
Octocat-spinner-32 060tools.wart 2632 March 23, 2013
Octocat-spinner-32 061patmatch.test 3282 - experiment: a new false sym November 07, 2013
Octocat-spinner-32 061patmatch.wart 2613 - the one true layout for 'if' February 23, 2013
Octocat-spinner-32 062converge.test 2593 February 20, 2013
Octocat-spinner-32 062converge.wart 2593 February 20, 2013
Octocat-spinner-32 063stack.test 3259 October 29, 2013
Octocat-spinner-32 063stack.wart 3259 October 29, 2013
Octocat-spinner-32 070fork.wart 2847 - The great rename of 2013 May 28, 2013
Octocat-spinner-32 071http_server.wart 2847 - The great rename of 2013 May 28, 2013
Octocat-spinner-32 072url.test 2752 - url parsing May 11, 2013
Octocat-spinner-32 072url.wart 2752 - url parsing May 11, 2013
Octocat-spinner-32 073http_get.wart 2757 May 12, 2013
Octocat-spinner-32 080if2.test 2847 - The great rename of 2013 May 28, 2013
Octocat-spinner-32 080if2.wart 2847 - The great rename of 2013 May 28, 2013
Octocat-spinner-32 081inline.test 3331 - eval_bind_all now working without quote_all December 28, 2013
Octocat-spinner-32 081inline.wart 3331 - eval_bind_all now working without quote_all December 28, 2013
Octocat-spinner-32 082macex.test 3314 December 08, 2013
Octocat-spinner-32 082macex.wart 3417 March 13, 2014
Octocat-spinner-32 Readme 3416 March 04, 2014
Octocat-spinner-32 boot.cc 3076 July 05, 2013
Octocat-spinner-32 denum 2260 October 21, 2012
Octocat-spinner-32 log.html.head 2695 - logging UX tweaks April 16, 2013
Octocat-spinner-32 makefile 3302 November 15, 2013
Octocat-spinner-32 manual_tests 3223 - bugfix: unbalanced parens shouldn't break repl September 27, 2013
Octocat-spinner-32 organization 2926 - tracified tests in top-level version June 02, 2013
Octocat-spinner-32 relayout 2248 October 18, 2012
Octocat-spinner-32 wart 3305 - all progress messages are now consistently in main.cc November 15, 2013
Octocat-spinner-32 wart.vim 3425 April 19, 2014
Readme
Wart is an experimental, dynamic, batshit-liberal (http://plus.google.com/110981030061712822816/posts/KaSKeg4vQtz)
language designed for small teams of intrinsically-motivated (http://en.wikipedia.org/wiki/Motivation#Intrinsic_and_extrinsic_motivation)
programmers who want more mastery over their entire software stack. The
primary goal is to be easy to understand and modify, so that you can be more
empowered.

  $ git clone http://github.com/akkartik/wart
  $ cd wart
  $ ./wart      # you'll need gcc and unix
  ready! type in an expression, then hit enter twice. ctrl-d exits.
  1+1
  => 2

## Small core

The best way to be readable is to minimize code (http://www.paulgraham.com/power.html).
To this end, Wart is based on lisp (http://paulgraham.com/lispfaq1.html),
because it seems the most powerful and extensible core for the lowest
implementation cost. But that's just the core; read on.

  (+ 1 1)
  => 2

  '(1 2 3)      # lists look like function calls
  => (1 2 3)    # 'quoting' prevents treating them like calls

  (def (foo x)  # define functions using def
    (+ 1 x))
  (foo 3)       # call functions using parens
  => 4

## Clean syntax

The crown jewels of lisp are its macros (http://www.paulgraham.com/progbot.html),
a massive tool for maximizing density in a readable way. Wart tries to
question all of lisp's design without compromising the power of macros. It
can guess parens from indentation (https://github.com/akkartik/wart/blob/master/004optional_parens)
to be more accessible to non-lispers.

  if (odd? n)
    prn "odd"   # prn = print to screen

Used tastefully, taking out parens can make code markedly more readable. My
heuristic is to skip parens when I want to highlight side-effects and
control-flow, and to insert them when I want to highlight functional
computation.

In order to provide macros, lisp uniformly uses prefix everywhere, including
for arithmetic. Wart provides infix operators (https://github.com/akkartik/wart/blob/master/006infix)
in an elegant way without compromising macros.

  4 * 3         # same as (* 4 3)
  => 12

This has historically been hard to do because of the burden of providing
precedence rules for nested expressions that are both intuitive and
extensible. Wart's infix operators have only one hard-coded precedence rule:
operators without whitespace are evaluated before operators with whitespace.

  n * n-1       # does what you expect

(The catch: you can't use infix characters like dashes in variable names,
something lispers are used to.)

Infix is most useful in arithmetic-heavy code
(https://gist.github.com/akkartik/4320819) for graphics and so on. But it has
uses beyond arithmetic. Sometimes the clearest way to describe a
sub-computation is as a pipeline of operations (http://rosettacode.org/wiki/Rot-13):

  (c -> downcase -> rot13 -> upcase)

There's a final convenience beyond indentation-sensitivity and infix: All
function arguments can be referred to by keyword. Individual calls can decide
to pass all their arguments in order, or to insert keywords to reorder or
emphasize specific args.

  4-3
  => 1

  def (subtract a|from b)   # 'from' is an *alias* for the first arg
    a-b

  subtract 4 3
  => 1
  subtract 3 :from 4        # refer to 'a' with its alias
  => 1

Argument aliases can also be used for pattern matching, like haskell's
as-expressions:

  def (foo (a | (b c)))     # 'b' and 'c' are aliases for parts of list 'a'
    (list a b c)

  (foo '(1 2))
  => ((1 2) 1 2)

I love feedback on these features: wart@akkartik.com

## Names

After succinctness, the biggest lever for readability is a coherent *system*
of names. It's not enough for names to be well-chosen in isolation, they also
need to work well together (http://akkartik.name/blog/readable-bad). To
maintain coherence, Wart allows all names to be extended and
overloaded in arbitrary ways. So you never have to define words like
'my_malloc' or 'append2' or 'queue_length'.

  def (len x) :case (isa queue x)
    ..

Such extensions to len can be made at any time, and might be arbitrarily far
from each other. This makes it easier for all queue-related code to stay
together, and so on.

Wart's primitives themselves pervasively use this technique. We first build
def to define simple functions, and support for :case is added later. The 'if'
primitive at first chooses between just two branches, and is later extended to
multiple branches.

## Organization

You should be able to do just about everything you want in Wart. However,
Wart's internals are also intended to be extremely readable and hackable if
you ever need to get into them. Its directory organization (https://github.com/akkartik/wart/blob/master/organization)
is designed to provide a high-level tour of the codebase. Just start reading
at the first file and skim the first few files to orient yourself. You don't
have to read each file to the end; important stuff is at the top and internal
details further down. Start a new file with a numeric prefix, and it'll be
loaded when Wart starts up. Reimplement a primitive from xxx.wart to xxx.cc
for speed, and the new version will be picked up when you restart.

## Tests

Wart is *thoroughly* unit tested.

  $ ./wart test

The tests can be a valuable tool to interactively improve your understanding.
Make a tweak, see if existing tests continue to pass. See a line you don't
understand, or something that seems redundant? Change it, see if the tests
pass.

## Speed

Wart is currently orders of magnitude slower than anything you're used to.
I've had some limited success using it for prototyping and then gradually
rewriting key pieces in C. But even this currently only takes you so far. I'm
playing with experimental primitives in the core interpreter that will make
partial evaluation as easy as passing code through eval multiple times. But
they're still in development.

## Where I came from, where I'm going

Wart started out motivated by an observation: good programmers build clean and
elegant programs, but programs grow less elegant over time. We start out clean,
but invariably end up making a mess of things. Requirements shift over time,
but reorganization is not convenient enough to keep organization in sync.
Historical accidents don't get cleaned up. Newcomers don't have the
comprehensive big-picture view of the original authors, and their changes
gradually dilute the original design. As coherence decays, so does sense of
ownership. The big picture becomes a neglected commons, too hard to defend and
nobody's direct responsibility. (More info: http://arclanguage.org/item?id=17598)

How to fight this tendency? Perhaps we can better preserve sense of ownership
by minimizing constraints.

  * Make creating new files easy, so there are *no* constraints in how a file
  can be organized. This will hopefully improve the odds that file
  organization will continue to reflect reality as requirements shift and the
  architecture evolves.

  * Keep features self-contained in files, so a feature can be dropped simply
  by deleting its files, and the rest of the project will continue to build
  and pass its tests. (There's a new version of this project in
  https://github.com/akkartik/wart/tree/master/literate that tries to take
  this idea to the limit.)

  * Backwards compatibility is a huge source of constraints that can bleed
  ownership (http://akkartik.name/blog/libraries2). What would a language
  ecosystem be like without any backwards-compatibility guarantees, super easy
  to change and fork promiscuously? Wart has no version numbers, and new
  versions and forks are free to change all semantics to their hearts' desire.
  Instead of a spec or frozen interfaces, we rely on automated tests. Wart
  programs are expected to be as thoroughly tested as Wart itself, and to buy
  in to the investment of hacking when upgrading. The hope is that upgrade
  effort might be higher than the best-case scenario in other languages, but
  the worst-case time will remain tightly bounded because clients of libraries
  will be more likely to understand how they're implemented, and so be more
  empowered.

The hypotheses are a) that making the global organization easy for outsiders
to understand will help preserve the big picture, b) that making
reorganizations more convenient by eliminating constraints will help keep the
big picture in sync with reality, and c) that understandability and
rewrite-friendliness are related in a virtuous cycle. Understandable codebases
help outsiders contribute better organizations, and rewrite-friendly codebases
help newcomers hone their understanding by attempting lots of reorganizations
in sandboxes.

These hypotheses are all hard to verify, I don't expected the job to be done
anytime soon. I'd love feedback from people unafraid to bounce between
language and implementation. If that's you, check it out and tell me what you
think: wart@akkartik.com. There are many more code samples here and at Rosetta
Code (http://rosettacode.org/wiki/Category:Wart#mw-pages).

## Credits

As an experiment in designing software, Wart was inspired by:
  Christopher Alexander (http://www.amazon.com/Notes-Synthesis-Form-Harvard-Paperbacks/dp/0674627512)
  David Parnas (http://www.cs.umd.edu/class/spring2003/cmsc838p/Design/criteria.pdf)
  Peter Naur (http://alistair.cockburn.us/ASD+book+extract%3A+%22Naur,+Ehn,+Musashi%22)

As a language, Wart was inspired by Arc, a dialect of lisp:
  http://www.paulgraham.com/arc.html
Discussions on the arc forum helped develop many of its features:
  "The wart atop the mountain": http://arclanguage.org/item?id=12814
  Generic functions: http://arclanguage.org/item?id=11779, http://arclanguage.org/item?id=13790
  Python-style keyword args: http://arclanguage.org/item?id=12657
  Why wart has no modules: http://arclanguage.org/item?id=12777
  Why wart has just one kind of equality: http://arclanguage.org/item?id=13690
  In praise of late binding: http://arclanguage.org/item?id=15655
  Libraries suck: http://www.arclanguage.org/item?id=13283
  Making macros even more first-class: http://arclanguage.org/item?id=16378
  Infix: http://arclanguage.org/item?id=16775

Wart is distributed under the Perl Foundation's artistic license 2.0:
  http://dev.perl.org/licenses/artistic.html
Something went wrong with that request. Please try again.