Introduction to Scheme: Difference between revisions

From MemCP
Jump to navigation Jump to search
(Refresh MemCP documentation: accuracy, operational guidance, performance profile and maintained API reference)
Line 1: Line 1:
When you run <code>./memcp</code>, you will be dropped at a scheme shell like this:
<!-- Copyright (C) 2026 Carl-Philip Haensch -->
memcp Copyright (C) 2023, 2024  Carl-Philip Hänsch
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
    This program comes with ABSOLUTELY NO WARRANTY;
= Introduction to Scheme =
    This is free software, and you are welcome to redistribute it
    under certain conditions;
loading storage /tmp/x/system/bdf64a22-6315-463c-bbf7-329317ad50ed-id of type 10
loading storage /tmp/x/system/bdf64a22-6315-463c-bbf7-329317ad50ed-username of type 20
loading storage /tmp/x/system/bdf64a22-6315-463c-bbf7-329317ad50ed-password of type 20
Welcome to memcp
performing unit tests ...
finished unit tests
test result: 15/15
all tests succeeded.
Initializing SQL frontend
MySQL server listening on port 3307 (connect with `mysql -P 3307 -u root -p` using password 'admin')
listening on <nowiki>http://localhost:4321</nowiki>
    Type (help) to show help
Scheme is a functional programming language and a subset of LISP. Every expression is either a primitive value or a list.


To understand the semantics of Scheme, take a look at the following Scheme REPL session:
MemCP embeds a small Scheme dialect used by the SQL parsers, query planner, modules, and application endpoints. Start the interactive console with <code>./memcp lib/main.scm</code>; background services use <code>--no-repl</code>.
> 12
= 12
> "hi"
= "hi"
> (+ 1 2)
= 3
> (+ 1 2 3)
= 6
> (+ 2 (* 2 2))
= 6
> (+ 2 (* 2 4))
= 10
> (concat "Hello" "World")
= "HelloWorld"


== Syntax ==
Scheme represents calls and code with the same list syntax. That makes the dialect well suited to MemCP's query compiler: the SQL planner constructs Scheme programs as data, optimizes them, and evaluates or compiles them.


=== Function Call ===
The console prints each expression result, which makes the evaluation model easy to explore:
A function call has the following format:
(functionname param1 param2 param3 ...)
Function calls start with <code>(</code>, contain one function and zero or more parameters separated by space  .


Some basic functions are <code>+ - * / print concat</code>. For more consult the manual with <code>(help)</code>.
<pre>&gt; 12
= 12
&gt; (+ 1 2 3)
= 6
&gt; (+ 2 (* 2 4))
= 10
&gt; (concat "Hello " "World")
= "Hello World"</pre>


=== Literals ===
Startup also loads the configured data directory and Scheme modules before opening the SQL/HTTP listeners. The exact banner and number of startup tests change between releases, so scripts should not parse that text.
The following literals are allowed:


* Number Literals: <code>1 2 3 56 7.5 4.3e20 -6.123e-3</code>
== Values and calls ==
* String Literals: <code>"Hello World" "First Line\nSecond Line" "he said: \"what?\" and smiled"</code>
* Function names: <code>print</code>
* Symbol literals <code>'print</code>
* List literals <code>'(1 2 3) '("a" "b" "c")</code>
* Associative Array literals <code>'("key" "value" "size" 45 "name" "Peter" "sublist" '(1 2 3))</code> - associative arrays are lists with key value pairs flattened down to a one dimensional list of an even number of items.
* Lambdas: <code>(lambda (param1 param2 ...) body)</code>


=== Lambda Functions ===
<syntaxhighlight lang="scheme">
Scheme allows for creating lambda functions which enclose their own scope:
(+ 1 2)
(define decrease-by-one (lambda (number) (- number 1)))
(concat "hello " "world")
The function can now be called from scheme:
(map '(1 2 3) (lambda (x) (* x x)))
> (decrease-by-one 5)
</syntaxhighlight>
= 4
Lambda functions can be passed to other functions as values. They enclose your original scope, so you can use variables from the outside inside your function.


== Deeper Topics into Scheme ==
Numbers, booleans, strings, symbols, lists, functions, sessions, and storage objects are ordinary values. A list in expression position is a call: its first item is the procedure and the rest are arguments. Whitespace separates forms; parentheses define nesting.
You can deep-dive into the following topcis:


* [[Full SCM API documentation]]
{| class="wikitable"
* [[Lists and Objects]]
! Form !! Meaning
* [[Pattern Matching]]
|-
* [[Parsers]]
| <code>42</code>, <code>3.5</code>, <code>true</code>, <code>"text"</code> || Literal values
|-
| <code>(+ 1 2)</code> || Call <code>+</code> with two arguments
|-
| <code>'name</code> || The symbol <code>name</code>, not the value bound to it
|-
| <code>'(1 2 3)</code> || Construct a list value instead of calling <code>1</code>
|-
| <code>'("name" "Ada" "active" true)</code> || Flat key/value list used as an associative object
|}
 
== Quoting and generated code ==
 
Use quoting to build code or data: <code>'(+ 4 5)</code> constructs delayed code, while <code>(eval '(+ 4 5))</code> evaluates it. Values that should be computed now can be embedded into a quoted outer form; procedure symbols whose execution is delayed remain quoted. This distinction matters throughout the query planner.
 
<syntaxhighlight lang="scheme">
(set delayed '(+ 4 5))
(eval delayed)                /* 9 */
(map '(1 2 3) (lambda (x) (* x x)))
</syntaxhighlight>
 
Do not add or remove parentheses by visual guesswork in large generated expressions. Repository Scheme files are formatted and checked with <code>python3 tools/lint_scm.py</code>.
 
== Functions and scope ==
 
Lambdas have exactly a parameter list and one body. Use <code>begin</code> when that body needs several forms:
 
<syntaxhighlight lang="scheme">
(define greet (lambda (name) (begin
(print "greeting " name)
(concat "Hello, " name))))
 
(greet "Ada")
</syntaxhighlight>
 
Functions are closures and may be passed to <code>map</code>, reducers, scanners, parsers, and handlers. <code>define</code> and <code>set</code> create a binding in the current scope in this dialect; <code>set</code> is not an imperative mutation of an outer lexical variable.
 
== Pattern matching and modules ==
 
<code>match</code> is the usual way to express alternatives and destructure lists, strings, or regular-expression results. Modules are loaded with <code>import</code>; application entrypoints typically import the required library files and then install handlers or start a server.
 
<syntaxhighlight lang="scheme">
(match value
'(x y) (+ x y)
(regex "^/users/([0-9]+)$" path id) id
false)
</syntaxhighlight>
 
See [[Parsers]], [[Lists and Objects]], and the matching examples in the repository for the exact pattern forms.
 
== State and concurrency ==
 
The dialect is functional by default. <code>set</code> defines a binding in the current scope; it does not imperatively mutate an outer binding. Use <code>(newsession)</code> when code intentionally needs a thread-safe mutable key/value context shared across parallel work.
 
<syntaxhighlight lang="scheme">
(set request_state (newsession))
(request_state "user_id" 42)
(request_state "user_id")      /* 42 */
</syntaxhighlight>
 
Independent functional work may run in parallel. Supported hot procedures can be compiled by the native x86-64 JIT; unsupported shapes remain interpreted without changing language semantics. See [[Full SCM API documentation]], [[Parallel Computing]], and [[In-Database WebApps and REST Services]].
 
== Where to continue ==
 
* [[Full SCM API documentation]] lists generated chapters and signatures.
* [[Lists and Objects]] explains lists, associative objects, mapping, and reduction.
* [[IO]] covers files, HTTP request/response objects, and servers.
* [[Storage]] documents the low-level table and scan interface; application code normally starts with SQL.
* [[JIT Compilation]] and [[Parallel Computing]] explain runtime optimization boundaries.

Revision as of 11:59, 28 August 2026

Introduction to Scheme

MemCP embeds a small Scheme dialect used by the SQL parsers, query planner, modules, and application endpoints. Start the interactive console with ./memcp lib/main.scm; background services use --no-repl.

Scheme represents calls and code with the same list syntax. That makes the dialect well suited to MemCP's query compiler: the SQL planner constructs Scheme programs as data, optimizes them, and evaluates or compiles them.

The console prints each expression result, which makes the evaluation model easy to explore:

> 12
= 12
> (+ 1 2 3)
= 6
> (+ 2 (* 2 4))
= 10
> (concat "Hello " "World")
= "Hello World"

Startup also loads the configured data directory and Scheme modules before opening the SQL/HTTP listeners. The exact banner and number of startup tests change between releases, so scripts should not parse that text.

Values and calls

<syntaxhighlight lang="scheme"> (+ 1 2) (concat "hello " "world") (map '(1 2 3) (lambda (x) (* x x))) </syntaxhighlight>

Numbers, booleans, strings, symbols, lists, functions, sessions, and storage objects are ordinary values. A list in expression position is a call: its first item is the procedure and the rest are arguments. Whitespace separates forms; parentheses define nesting.

Form Meaning
42, 3.5, true, "text" Literal values
(+ 1 2) Call + with two arguments
'name The symbol name, not the value bound to it
'(1 2 3) Construct a list value instead of calling 1
'("name" "Ada" "active" true) Flat key/value list used as an associative object

Quoting and generated code

Use quoting to build code or data: '(+ 4 5) constructs delayed code, while (eval '(+ 4 5)) evaluates it. Values that should be computed now can be embedded into a quoted outer form; procedure symbols whose execution is delayed remain quoted. This distinction matters throughout the query planner.

<syntaxhighlight lang="scheme"> (set delayed '(+ 4 5)) (eval delayed) /* 9 */ (map '(1 2 3) (lambda (x) (* x x))) </syntaxhighlight>

Do not add or remove parentheses by visual guesswork in large generated expressions. Repository Scheme files are formatted and checked with python3 tools/lint_scm.py.

Functions and scope

Lambdas have exactly a parameter list and one body. Use begin when that body needs several forms:

<syntaxhighlight lang="scheme"> (define greet (lambda (name) (begin (print "greeting " name) (concat "Hello, " name))))

(greet "Ada") </syntaxhighlight>

Functions are closures and may be passed to map, reducers, scanners, parsers, and handlers. define and set create a binding in the current scope in this dialect; set is not an imperative mutation of an outer lexical variable.

Pattern matching and modules

match is the usual way to express alternatives and destructure lists, strings, or regular-expression results. Modules are loaded with import; application entrypoints typically import the required library files and then install handlers or start a server.

<syntaxhighlight lang="scheme"> (match value '(x y) (+ x y) (regex "^/users/([0-9]+)$" path id) id false) </syntaxhighlight>

See Parsers, Lists and Objects, and the matching examples in the repository for the exact pattern forms.

State and concurrency

The dialect is functional by default. set defines a binding in the current scope; it does not imperatively mutate an outer binding. Use (newsession) when code intentionally needs a thread-safe mutable key/value context shared across parallel work.

<syntaxhighlight lang="scheme"> (set request_state (newsession)) (request_state "user_id" 42) (request_state "user_id") /* 42 */ </syntaxhighlight>

Independent functional work may run in parallel. Supported hot procedures can be compiled by the native x86-64 JIT; unsupported shapes remain interpreted without changing language semantics. See Full SCM API documentation, Parallel Computing, and In-Database WebApps and REST Services.

Where to continue