LispEx
A dialect of Lisp extended to support concurrent programming.
Overview
LispEx is another Lisp Interpreter implemented with Go. The syntax, semantics and library procedures are a subset of R5RS:
LispEx 0.1.0 (Saturday, 19-Jul-14 12:52:45 CST)
;; lambda expression
>>> ((lambda (x y . z) (+ x y (car z))) 1 2 5 11)
8
;; currying
>>> (define (curry func arg1) (lambda (arg) (apply func arg1 (list arg))))
>>> (map (curry + 2) '(1 2 3 4))
(3 4 5 6)
;; apply
>>> (apply + 1 2 '(3 4))
10
;; composite function
>>> (define ((compose f g) x) (f (g x)))
>>> (define caar (compose car car))
>>> (caar '((1 2) 3 4))
1
;; tail recursion
>>> (letrec
((even? (lambda (n) (if (= 0 n) #t (odd? (- n 1)))))
(odd? (lambda (n) (if (= 0 n) #f (even? (- n 1))))))
(even? 88))
#t
;; multiple nestings of quasiquote
;; (challenging to have a right implementation)
>>> `(1 `,(+ 1 ,(+ 2 3)) 4)
(1 `,(+ 1 5) 4)
>>> `(1 ```,,@,,@(list (+ 1 2)) 4)
(1 ```,,@,3 4)
;; lazy evaluation
>>> (define f (delay (+ 1)))
>>> (force f)
1
What's new, the Go-like concurrency features are introduced in LispEx. You can start new coroutines with go
statements, and use <-chan
or chan<-
connecting them. A ping-pong example is shown below:
; define channels
(define ping-chan (make-chan))
(define pong-chan (make-chan))
; define a buffered channel
(define sem (make-chan 2))
(define (ping n)
(if (> n 0)
(begin
(display (<-chan ping-chan))
(newline)
(chan<- pong-chan 'pong)
(ping (- n 1)))
(chan<- sem 'exit-ping)))
(define (pong n)
(if (> n 0)
(begin
(chan<- ping-chan 'ping)
(display (<-chan pong-chan))
(newline)
(pong (- n 1)))
(chan<- sem 'exit-pong)))
(go (ping 6)) ; start ping-routine
(go (pong 6)) ; start pong-routine
; implement semaphore with channel, waiting for ping-pong finishing
(<-chan sem) (newline)
(<-chan sem) (newline)
; should close channels if you don't need it
(close-chan sem)
(close-chan pong-chan)
(close-chan ping-chan)
; the output will be: ping pong ping pong ... exit-ping exit-pong
Furthermore, select
statement is also supported, which is necessary for you to select between multiple channels that working with concurrent routines. Just like Go, the code can be written like this:
(define chan-1 (make-chan))
(define chan-2 (make-chan))
(go (chan<- chan-1 'hello-chan-1))
(go (chan<- chan-2 'hello-chan-2))
(select
((<-chan chan-1))
((<-chan chan-2))
(default 'hello-default))
(close-chan chan-1)
(close-chan chan-2)
; the output will be: hello-default, as it will cost some CPU times when a coroutine is lanuched.
In this scenario, default
case is chosen since there is no ready data in chan-1
or chan-2
when select
statement is intepretered. But such scenario will be changed if we sleep
the main thread for a while:
(define chan-1 (make-chan))
(define chan-2 (make-chan))
(go (chan<- chan-1 'hello-chan-1))
(go (chan<- chan-2 'hello-chan-2))
; sleep for 20 millisecond
(sleep 20)
(select
((<-chan chan-1))
((<-chan chan-2))
(default 'hello-default))
(close-chan chan-1)
(close-chan chan-2)
; the output will be randomized: hello-chan-1 or hello-chan-2
For more interesting examples, please see files under tests folder.
Features
- Clean designed code, very easy for you to understand the principle, inspired from yin
- A concurrent design for lexical scanning, inspired from Rob Pike
- Builtin routines, channels and other necessary components for concurrent programming
- Give you a REPL
In developing
loop
in R5RS- tail call optimization
- type checker
Have a try
git clone https://github.com/kedebug/LispEx.git
cd LispEx
go build && ./LispEx
LispEx 0.1.0 (Saturday, 19-Jul-14 12:52:45 CST)
>>>
From here you can type in forms and you'll get the evaluated expressions back. To interpreter a file:
./LispEx filename.ss
Lisp is fun, go is fun, concurrency is fun. Hope you will have an extraordinary programming experience with LispEx.
License
MIT