CSCM (programming language)
Paradigm | multi-paradigm, concurrent, functional |
---|---|
Designed by | Joseph Wayne Norton |
Developer | Joseph Wayne Norton |
First appeared | 2013 |
Stable release |
v0.5.4
/ 12 May 2014 |
Typing discipline | dynamic, strong |
Scope | lexical |
License | MIT License |
Filename extensions | .scm, .erl |
Website |
the-concurrent-schemer |
Influenced by | |
Erlang, Common Lisp, MACLISP, Scheme, Elixir, Clojure, Hy | |
Influenced | |
Joxa |
CSCM (The Concurrent Schemer) is an implementation of the Scheme programming language built on top of the Erlang virtual machine (BEAM). CSCM combines the sequential programming model of Scheme with the concurrent, distributed, and fault-tolerant programming model of Erlang.
By leveraging the Erlang language, CSCM can target a concise, precise specification of the Scheme language. Consequently, by leveraging the Erlang VM, CSCM can target a performant, robust implementation of the Scheme language. The default language is Scheme R7RS and the default virtual machine is Erlang/OTP 17 or higher. Scheme R5RS is available as a Scheme library.
CSCM builds on top of Erlang in order to provide a Scheme syntax for writing distributed, fault-tolerant, soft real-time, non-stop applications. CSCM also extends Erlang to support meta-programming with hygenic macros and an improved developer experience with a feature-rich REPL.[1]
History
Initial release
Joseph Wayne Norton announced the first release of CSCM on GitHub in March 2013. This release of CSCM was very limited: it did not handle recursive letrec
s, binary
s, receive
, or try
;.
Motivation
CSCM is aimed as an:
- Educational tool
- Well-established, high-quality implementation of concurrent Scheme
- Erlang reference project
Joseph Wayne Norton has stated that there were a number of reasons why he started the CSCM project:.[2]
- He had previous experience programming in Scheme and Erlang.
- Given his previous experience, he was interested in implementing his own Lisp.
- In particular, he wanted to implement a Lisp in Erlang: as he was curious to see how it would run on BEAM and able to fully interact with Erlang/OTP.
- Since helping to create the Erlang programming language, he had had the goal of making a Lisp which was specifically designed for running on the BEAM and able to fully interact with Erlang/OTP.
- He wanted to experiment with compiling another language on top of Erlang. As such, he saw CSCM as a means of exploring this by generating Core Erlang and plugging it into the backend of the Erlang compiler.
- He was not working with programming or Erlang at the time, so was looking for some interesting programming projects that were not too large to do in his spare time.
- He likes implementing languages.
- He also thought it would be a fun problem to solve, as a solution would have many parts and the problem space was quite open-ended.
Features
- A language targeting Erlang Virtual Machine (BEAM)
- Seamless Erlang integration: zero-penalty Erlang function calls (and vice versa)
- Meta programming via macros and the homoiconicity of a Lisp
- Shared-nothing concurrent programming via message passing (Actor model)
- Emphasis on recursion and higher-order functions instead of side-effect-based looping
- A full REPL for interactive development and testing (unlike Erlang's shell, the CSCM REPL supports function and macro definitions)
- Pattern matching
- Hot loading of code
- Java inter-operation via JInterface and Erjang
Syntax and semantics
Symbolic expressions (S-expressions)
Being a Scheme, CSCM is an expression-oriented language. Unlike non-homoiconic programming languages, Lisps make no or little syntactic distinction between "expressions" and "statements": all code and data are written as expressions. CSCM brought homoiconicity to the Erlang VM.
Lists
The cons function actually accepts any two values, not just a list for the second argument. When the second argument is not empty and not itself produced by cons, the result prints in a special way. The two values joined with cons are printed between parentheses, but with a dot (i.e., a period surrounded by whitespace) in between:
> (cons 1 2)
(1 . 2) > (cons "banana" "split")
("banana" . "split")
Thus, a value produced by cons is not always a list. In general, the result of cons is a pair. The more traditional name for the cons? function is pair?
Operators
The Erlang operators are used in the same way. The expression
(* (+ 1 2 3 4 5 6) 2)
evaluates to 42. Unlike functions in Erlang, arithmetic operators in scheme are variadic (or n-ary), able to take any number of arguments.
Lambda expressions and function definition
CSCM has lambda, just like scheme. It also, however, has lambda-match to account for Erlang's pattern-matching capabilities in anonymous function calls.
Start up
Start the Erlang shell.
erl -pa ./deps/parse-tools/ebin -pa ebin
Save the "hello word" program as an Erlang string.
Str = "(define hello-world (lambda () (display \"Hello World!\")))".
Create an empty Scheme environment.
Env = scmi_env:the_empty().
Create and register a native Erlang function as a simple implementation for the Scheme display library procedure. The Erlang function writes the given arguments to stdout as Erlang terms and simply returns a Scheme #false to the caller.
False = {boolean,undefined,false}. Proc = {nipv, 0, fun(Args) -> io:format("~p~n", [Args]), False end}. scmi_env:define_variable('display', Proc, Env).
Parse and evaluate the "hello world" program.
{ok, Exp} = scmd_parse:string(Str). scmi_eval:eval(Exp, Env).
Call the Scheme "hello-world" procedure and show the Scheme return value in the Erlang shell.
R = scmi_eval:eval(['hello-world'], Env). R.
This section does not represent a complete comparison between Erlang and CSCM, but should give a taste.
Pattern matching
Erlang:
1> {Len,Status,Msg} = {8,ok,"Trillian"}.
{8,ok,"Trillian"}
2> Msg.
"Trillian"
CSCM:
> (set (tuple len status msg) #(8 ok "Trillian"))
#(8 ok "Trillian")
> msg
"Trillian"
The basic form of pattern matching expression is:
(match exp [pat body] ...)
List comprehensions
Erlang:
1> [trunc(math:pow(3,X)) || X <- [0,1,2,3]].
[1,3,9,27]
CSCM:
> (list-comp
((<- x '(0 1 2 3)))
(trunc (math:pow 3 x)))
(1 3 9 27)
Or idiomatic functional style:
> (lists:map
(lambda (x) (trunc (math:pow 3 x)))
'(0 1 2 3))
(1 3 9 27)
Guards
Erlang:
right_number(X) when X == 42; X == 276709 ->
true;
right_number(_) ->
false.
CSCM:
(define right-number?
((x) (when (orelse (== x 42) (== x 276709)))
'true)
((_) 'false))
cons'ing in function heads
Erlang:
sum(L) -> sum(L,0).
sum([], Total) -> Total;
sum([H|T], Total) -> sum(T, H+Total).
CSCM:
(defun sum (l) (sum l 0))
(defun sum
(('() total) total)
(((cons h t) total) (sum t (+ h total))))
or using a ``cons`` literal instead of the constructor form:
(define sum (l) (sum l 0))
(define sum
(('() total) total)
((`(,h . ,t) total) (sum t (+ h total))))
Matching records in function heads
Erlang:
handle_info(ping, #state {remote_pid = undefined} = State) ->
gen_server:cast(self(), ping),
{noreply, State};
handle_info(ping, State) ->
{noreply, State};
CSCM:
(define handle_info
(('ping (= (match-state remote-pid 'undefined) state))
(gen_server:cast (self) 'ping)
`#(noreply ,state))
(('ping state)
`#(noreply ,state)))
Receiving messages
Erlang:
universal_server() ->
receive
{become, Func} ->
Func()
end.
CSCM:
(define universal-server ()
(receive
((tuple 'become func)
(funcall func))))
or CSCM:
(define universal-server ()
(receive
(`#(become ,func)
(funcall func))))
Examples
Erlang interoperability
Calls to Erlang functions take the form (<module>:<function> <arg1> ... <argn>):
(io:format "Hello, World!")
Functional paradigm
Using recursion to define the Ackermann function:
(define ackermann
((0 n) (+ n 1))
((m 0) (ackermann (- m 1) 1))
((m n) (ackermann (- m 1) (ackermann m (- n 1)))))
Composing functions:
(define compose (f g)
(lambda (x)
(funcall f
(funcall g x))))
(define check ()
(let* ((sin-asin (compose #'sin/1 #'asin/1))
(expected (sin (asin 0.5)))
(compose-result (funcall sin-asin 0.5)))
(io:format "Expected answer: ~p~n" (list expected))
(io:format "Answer with compose: ~p~n" (list compose-result))))
Concurrency
Message-passing with Erlang's light-weight "processes":
(defmodule messenger-back
(export (print-result 0) (send-message 2)))
(define print-result ()
(receive
((tuple pid msg)
(io:format "Received message: '~s'~n" (list msg))
(io:format "Sending message to process ~p ...~n" (list pid))
(! pid (tuple msg))
(print-result))))
(define send-message (calling-pid msg)
(let ((spawned-pid (spawn 'messenger-back 'print-result ())))
(! spawned-pid (tuple calling-pid msg))))
Multiple simultaneous HTTP requests:
(defun parse-args (flag)
"Given one or more command-line arguments, extract the passed values.
References
- ↑ "The Concurrent Schemer". Joseph Wayne Norton. Retrieved 2016-08-22.
- ↑ "CSCM". Rahul Thathoo. Retrieved 2015-01-17.
External links
- CSCM language website
- CSCM User Guide
- Scheme on Rosetta Code
- Erlang on Rosetta Code
- Joseph Wayne Norton
- JWNorton