All Projects → akkartik → Wart

akkartik / Wart

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

Programming Languages

lisp
113 projects

Projects that are alternatives of or similar to Wart

jisp
Small Lisp expression interpreter made in Java
Stars: ✭ 19 (-85.04%)
Mutual labels:  lisp-interpreter
Mal
mal - Make a Lisp
Stars: ✭ 8,287 (+6425.2%)
Mutual labels:  lisp-interpreter
Golisp
Lisp Interpreter
Stars: ✭ 107 (-15.75%)
Mutual labels:  lisp-interpreter
liyad
Liyad (Lisp yet another DSL interpreter) is very small Lisp interpreter written in JavaScript.
Stars: ✭ 27 (-78.74%)
Mutual labels:  lisp-interpreter
Pampy.js
Pampy.js: Pattern Matching for JavaScript
Stars: ✭ 544 (+328.35%)
Mutual labels:  lisp-interpreter
Seax
A VM-based runtime environment for functional programming languages
Stars: ✭ 36 (-71.65%)
Mutual labels:  lisp-interpreter
genyris
Genyris presents a new programming paradigm. Objects can belong to multiple classes independent from construction allowing data to be classified into types after creation.
Stars: ✭ 14 (-88.98%)
Mutual labels:  lisp-interpreter
Lice
A multi-paradigm programming language running on JVM
Stars: ✭ 120 (-5.51%)
Mutual labels:  lisp-interpreter
Blispr
Lisp-in-progress
Stars: ✭ 27 (-78.74%)
Mutual labels:  lisp-interpreter
Mumbler
My experimental programming language using Truffle
Stars: ✭ 100 (-21.26%)
Mutual labels:  lisp-interpreter
Pampy
Pampy: The Pattern Matching for Python you always dreamed of.
Stars: ✭ 3,419 (+2592.13%)
Mutual labels:  lisp-interpreter
Glisp
A Lisp-based Design Tool Bridging Graphic Design and Computational Arts
Stars: ✭ 519 (+308.66%)
Mutual labels:  lisp-interpreter
Tox
misc parsers in rust
Stars: ✭ 40 (-68.5%)
Mutual labels:  lisp-interpreter
parens
Parens is a highly flexible and embeddable LISP toolkit. 💻
Stars: ✭ 32 (-74.8%)
Mutual labels:  lisp-interpreter
Yoctolisp
Tiny Scheme-like Lisp interpreter written in a weekend
Stars: ✭ 117 (-7.87%)
Mutual labels:  lisp-interpreter
PureLISP.sh
A Pure LISP interpreter written in shell script conformed to POSIX shell
Stars: ✭ 30 (-76.38%)
Mutual labels:  lisp-interpreter
Yascm
Yet Another Scheme Interpreter using flex and bison
Stars: ✭ 36 (-71.65%)
Mutual labels:  lisp-interpreter
Lips
Scheme based powerful lisp interpreter written in JavaScript
Stars: ✭ 120 (-5.51%)
Mutual labels:  lisp-interpreter
Cisp
A Common Lisp Interpreter Built in COBOL
Stars: ✭ 120 (-5.51%)
Mutual labels:  lisp-interpreter
Mal Zh
The Make-A-Lisp Process 中文翻译,如何写一个Lisp解释器
Stars: ✭ 100 (-21.26%)
Mutual labels:  lisp-interpreter

Wart is an experimental, dynamic, batshit-liberal (http://plus.google.com/110981030061712822816/posts/KaSKeg4vQtz) language designed to eventually be used by 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: [email protected]

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: [email protected]. 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

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].