Enhancing Scheme (and Lisp-like) language.
Damien Mattei
"Doubts are such tiny things. A mind with no room for doubts must have no room for thoughts either." -R. Patti
Scheme+ version 9.2 for Guile Scheme
Display options for viewing this documentation:
Click here for the light HTML page version of this
page.
Click
here for the dark Hacker Theme version of this page.
1. Scheme+ at a glance:
Scheme+ is an extension of the syntax of the Scheme language.
Scheme+ is to Scheme what a concept-car is to automobile.Scheme+ is a concept-language.It is ideally what should be a modern Scheme.
Scheme+ makes it easy the assignment of Scheme objects in
infix (works also in prefix) notation with a few
new operators ← (or
<-), [ ],⥆ (or <+) .
The idea of Scheme+ first come to my mind when comparing the
assignation syntax used in Clojure with Scheme and Lisp, and later
the easyness of creating and assigning variables in Python
completely convince me that the Scheme system have to be enhanced.
The idea of Scheme+ is also came from this : "Why is it so boring to define variables in Scheme with the traditionnal set of LET,LET*,LETREC,LET-VALUES special forms?" and "Is it possible to use a single mechanism for assignment in Scheme?"
It comes also from the ascertainment that "A computer language is created by ONE man, later, a community only bring a library to the language without self-questioning any more on the fundations of language itself."
Scheme+ brings syntax to classic Scheme language like those:
{x <- y} infix assignment of the value of y to the variable x.
The same thing with a new symbol ← : (unicode 2190 in hexadecimal that can be enter under Linux with Ctrl-Shift u 2190 Enter or Space bar)
{x ← y}
With operator precedence: {xp <- {x - v * t} / (sqrt {1 - v ** 2 / c ** 2})}
{M_i_o[j {i + 1}] <- M_i_o[j {i + 1}] + η * z_input[i] * მzⳆმz̃(z_output[j] z̃_output[j]) * ᐁ_i_o[j]}
Assignment from right to left : {y ← 7}
Assignment from left to right : {7 → y}
⥆ or <+ : Define and assign a new variable in the current environment ,example : {v ⥆ 7}
(unicode 2946 , on Mac OS use
CTRL+CMD+space to bring up the characters
popover,
then type in u+unicode (ex: u+2946) and hit Enter to
get it)
{T[k]} return value of vector or array T indexed by k.
{T[k] <- x} infix assignment of T[k] with x.
{T[k] <- T[{k +
1}]} in infix or the same with a little part of classic
prefix Scheme: {T[k] <- T[(+ k 1)]}
the same with multi dimensional arrays or
matrix:
{T[m n]} return the value of the multi dimension
array T indexed by m and n
{T[m n] <- T[m {n + 1}]}
{T[m n] ← T[m {n +
1}]}
compatible with strings:
{str <+ (string-copy "hello")}
"hello"
{str[4]}
#\o
{str[4] <- #\a}
#\a
str
"hella"
Slicing for vectors and strings as in Python:
{s[2 * 3 - 4 : 2 * 3 + 1 : 2 * 4 - 6] <- "0000"}
"ab0d0f0h"
with function definition:
(declare fact)
{fact <- (lambda (n) (if {n <> 0} {n * (fact {n - 1})}
1))}
or just:
{fact ⥆ (λ (n) (if {n <> 0} {n * (fact {n - 1})} 1))}
{fact(8)}
40320
no more need of LET,LET*,LETREC,LET-VALUES,...
← is an n-arity operator, example :
(declare x y z t)
{x ← y ← z ← t ← 7}
7
(list x y z t)
(7 7 7 7)
Another example which build an identity matrix:
(declare I)
{I ← (make-array 0 4 4)}
#2((0 0 0 0)
(0 0 0 0)
(0 0 0 0)
(0 0 0 0))
{I[0 0] ← I[1 1] ← I[2 2] ← I[3 3] ← 1}
1
I
#2((1 0 0 0)
(0 1 0 0)
(0 0 1 0)
(0 0 0 1))
defining new functions with def instead of the classic define we
can now use return to escape immediately from the current level of recursive call and return-rec to escape from the full stack of recursive calls:
(return)
or returning the value v:
The <+ operator works also for
defining multiple values :
{(a b c) <+ (values 7 8 9)}
7
8
9
(list a b c)
'(7 8 9)
{(x y z) <+ (u v w) <+ (a b
c) <+ (values 2 4 5)}
2
4
5
(list x y z u v w a b c)
'(2 4 5 2 4 5 2 4 5)
We can also assign multiple values on
the fly:
(declare x y z)
{(x y z) <v (values 2 4 5)}
2
4
5
(list x y z)
'(2 4 5)
(declare u v w)
{(x y z) <v (u v w) <v (values 2 4 5)}
2
4
5
(list x y z u v w)
'(2 4 5 2 4 5)
Here is an example of the previous features:
(subset-sum-dynamic L-init t-init) #t
Operator and function overloading:
A possibility of defining overloaded functions or operators (just add
'operator) is given with this syntax:
(define-overload-existing-n-arity-operator +)
(define (add-n-lists . vn-lst) (implementation-add-n-lists vn-lst))
(define (implementation-add-n-lists vn-lst) {map-args <+ (cons + vn-lst)} (apply map map-args)) (overload-existing-n-arity-operator + add-n-lists (list? list?)) {'(1 2 3) + '(4 5 6) + '(7 8 9)}
(12 15 18)
(define-overload-existing-operator *)
(define (mult-num-list k v) (map (λ (x) (* k x)) v))
(overload-existing-operator * mult-num-list (number? list?))
{3 * '(1 2 3) + '(4 5 6) + '(7 8 9)}
(14 19 24)
Note that when a function that overload an operator has more than 2 args (f a1 a2 a3 ...) and only (f a1 a2) is defined we do: (f a1 (f a2 a3 ...)) for operators.
A complete set of new procedures and macros:
define-overload-procedure overload-procedure define-overload-existing-procedure overload-existing-procedure define-overload-operator overload-operator define-overload-existing-operator overload-existing-operator define-overload-n-arity-operator overload-n-arity-operator define-overload-existing-n-arity-operator overload-existing-n-arity-operator overload-square-bracketsallow overloading of both functions and operators. Those macros call procedures written recursively or imperatively with hast tables depending of scheme implementation because all predicate must be test to find the matching procedure as Scheme is not a typed language.
Warnings about code highlighting tag due to bugs in Github Markdown
system:
-if you read this page on github.com the code examples display
encapsuled between tags 'highlight scheme' and 'endhighlight'
composed also with {} and % characters.
-if you read this page on github.io the code examples display
starting with 3 backquotes char , the tag 'scheme' and ending again
with 3 backquotes char. The curly brackets {} display in a
rectangular with red background because the Github Jekill rouge
theme (unfortunately the ony one available) does not understand the
infix notation of Scheme.This is,of course, not what i expected but i
can not find any issue to this problem.
Those weirds Markdown and tags should not be displayed and are not part of Scheme+ examples.
for a better view click here (or do not care of
highlighting tag and triple backquotes when code is displayed!)
Another example with Guile Scheme in the REPL (Read Eval Print Loop):
mattei@MBP-Touch-Bar ~ % cd Dropbox/git/Scheme-PLUS-for-Guile
mattei@MBP-Touch-Bar Scheme-PLUS-for-Guile % guile
GNU Guile 3.0.7
Copyright (C) 1995-2021 Free Software Foundation, Inc.
scheme@(guile-user)>
;;; note: source file ./Scheme+.scm
;;;
newer than compiled
;;; compiling ./Scheme+.scm
;;; compiled
scheme@(guile-user)> (declare L)
scheme@(guile-user)> {L <- '(1 2 3 4)}
$1 = (1 2 3 4)
scheme@(guile-user)> L
$2 = (1 2 3 4)
scheme@(guile-user)> {L <- (list 1 2 3 4 5)}
$3 = (1 2 3 4 5)
Hash Tables support :
(use-modules (srfi srfi-69)) ;; support for SRFI 69 Basic hash tables {my-hash-tbl <+ (make-hash-table)} ;; add an object in the current environment, here a hash table (hash-table? my-hash-tbl) #t
{my-hash-tbl["three"] <- 3} 3
{my-hash-tbl["three"]} 3
Note that infix programs needs to include the #!curly-infix statement if you have not configured your guile init file for curly infix.
curly-infix2prefix4guile.scm your_scheme_file_in_scheme+.scm > your_scheme_file_in_scheme.scm
for the rest read the documentation below and the online examples.
2.Download Scheme+:
The source code of the Scheme+, for Guile implementation of Scheme can be retrieved here:
>>> Download Scheme+ as .zip <<<
>>> Download Scheme+ as .tar.gz <<<
>>> https://github.com/damien-mattei/Scheme-PLUS-for-Guile
--> Code --> Download ZIP <<<
Including : (but not mandatory to use Scheme+)
The growable vector module class in the file growable-vector.scm.
The enhanced set of LET special forms in the file let.scm.
Many of the examples of this web page are in the source file SssDyna.scm.
The .guile configuration file i use with Scheme+.
3.Requirements:
Scheme+ needs a Scheme interpreter and will be adapted and released for
many Scheme (Guile,Racket,...), download Scheme Guile:
This version needs Guile Scheme version 3. It has been developed with
Guile 3.0.7 on Mac OS
and Guile 3.0.1 under Linux.
Guile offers infix notation that can be activated this way: for curly
infix notation put in your .guile file : (read-enable
'curly-infix)
Also Guile can be more easy to use with history on command line, completion, as example here is my .guile init file:
;; Guile config file ;; history (use-modules (ice-9 readline) (ice-9 history) (srfi srfi-43) ;; vector ;; guile object oriented programming system (oop goops) (oop goops describe)) (activate-readline) ;;(disable-value-history!) ;; curly infix as in srfi-105 (read-enable 'curly-infix) ;; set current path in load path (set! %load-path (reverse (cons "." (reverse %load-path)))) ;; other solution is to put this in shell: ;; export GUILE_LOAD_PATH="...:."
This .guile file is included in the Scheme+ repository.
4.Author and license:
Damien Mattei ; Scheme+ : Copyright 2021 Damien Mattei under GNU GENERAL PUBLIC LICENSE Version 3
Nice - France (commune de Carros)
contact : directory
e-mail
Guagno - Island of Corsica
To subit an issue, a bug or something else related with the Scheme+ code
use the Github issues system.
5.Installation:
documented here: https://www.gnu.org/software/guile/manual/html_node/Installing-Site-Packages.html
6. Idea and rationale:
The core language of Scheme and Lisp are based on syntaxic form that date back from '50 for Lisp and '70 for Scheme. Syntactic forms such as the set of LET,LET*,LETREC,... have since long ago been replaced in many other languages (C/C++,Java,Fortran,Pascal,Javascript,BASIC (the LET keyword can be ommited in Applesoft Basic),by simple assignment operator that frees the syntax and allow saving indentation space, number of parenthesis, readability of code.
The main idea of this project is to improve Scheme and Lisp like languages with syntaxic features,such as easy assignment for many object types (numbers,strings,vectors,arrays,etc),and also allowing an immediate 'return' mechanism to escape from procedure when result is known and others features that exist in others languages (Python,Java,C/C++,Pascal,BASIC,Javascript). Some new features can be and are better used with infix notations (SRFI 105 known as "Curly Infix"),so for some syntactic expressions infix will be promoted (but not mandatory).
A few new feature (see below) allow a great change in syntax and readability of Scheme programs. Less parenthesis are used,less indentation and it allows an enhanced programming style that modernize Scheme but also keep it 100% compatible with the original language.
Also vectors and arrays in Scheme are painfully accessed and modified by vector-ref and vector-set! , Scheme+ use a simplier syntax again with the <- operator and the [] syntax.
This intuitive notation works also with Hash Tables.
Scheme+ will remove the need for the classic LET set of special form, no
more need of LET,LET*,LETREC,LET-VALUES,... and will use
an unique infix (or not) assignment operator : <-. (also LET are
enhanced - the project starts historically by improving and simplifying LET, i
simplified it so much that i remove it now! - in other versions such as
let<-arrow requiring less brackets, even if their use is useless and no
more promoted, see extra features part)
7. Features:
The list of new features is subject to change and will grow by time, this version allows:
- use of infix SRFI 105 "Curly Infix" for some syntax forms
- Assignment operator : <- (can be used in infix syntax and from left to right and right to left)
- Square Bracket operator [] working with Scheme Vectors, Arrays,Multidimentional Arrays,Dynamic Arrays (my growable vector class),Hash Tables,Strings...
- combination of Assignment operator <- with [] operator for Vectors,Arrays,....
- RETURN : return keyword used in any procedure to return from procedure and return a value.
- declaration of variables
- declaration and assignment of variables with an infix operator.
- conditionals and execution of code in a new form : condx
- extra feature: new set of LET special forms,even if their use is completely discouraged in favor of assignment operator combined with declarative form (see declare).
8. First Examples:
The Fibonacci sequence:
This example is written in Scheme with and without infix notation just to allow the reader to be familiar with "curly infix" notation which is not part of Scheme+ but used by it:
Here is the definition in classic prefix Scheme:
And here is a definition using "Curly Infix" SRFI 105:
note that the last expression of fib: {(fib {n - 1}) + (fib {n - 2})} could also be expressed in curly infix Scheme like that : {fib({n - 1}) + fib({n - 2})} or even like that: {fib{n - 1} + fib{n - 2}} but i will not use them, preferring to use infix only where mathematical calculus are coded and for comparaison tests in conditionals.
Fibonacci is time consuming,even fib(47) can takes minutes to compute. We will write a faster dynamic version that memorize the results computed more than one time.
Here is a solution in classic Scheme:
In the example above we can notice that the array access and modification is not easy to read and in the else block i have to use a let special form to create a local variable to return the value computed and already stored in array,even if i have not used a local variable i then must have to access the result in array to return it...
Scheme+ allow to write code more readable and simpler than normal Scheme.
Prior to use Scheme+ for the implementation of Scheme named
Guile, the codemust be loaded,this is done by inserting the
statement
(use-modules (Scheme+))
at the beginning of the Scheme
source file.
Below is the version of the above code written in Scheme+ :
The reader can notice the introduction of one new assignment operator <- and also <+, the latter being simply an infix define of Scheme. The important point of the new assignment operator <- of Scheme+ is that it will work like the classic set! of Scheme on variables but also will work on more complex object such as element of vectors,multidimensional arrays (as defined in SRFIs),growable vectors (my class),... and much more in the next versions of Scheme+. (we will see another more complex example with multidimentional array later)
So {x <- 7} simply assign 7 to the variable x but {m[3] <- 7} will store 7 in the array m at the position indexed by 3. Access an element of array is also simpler in Scheme+ than in Scheme: {m[3]}. And this syntax is 100% compatible with Scheme, you can mix both the syntaxes if you want. This syntax is also more natural and readable and much like the mathematical notation, it is also a syntax used for arrays in other languages such as C/C++,Python,Java,Pascal,Javascript...
Also you will notice that the main computation in the else block is now shorter and it is partly due to the fact that the assignment operator <- return not NIL but the affected value making it available for other calculus or as a final result value.
Here is now the same solution in a declarative form, in the part called "History of project" i explain why there exist two solutions.
Instead of declare and assign the variables with the define nested in the macro <+ we also can declare them and assign the variables later with the universal <- operator:
9.Short documentation
Square bracket operator [] is used for vectors,arrays,growable vectors,hash tables,etc.
example: {T[k]} return value of vector or array T indexed by k.
Assignment operator <- can be used only on existing single variables or vector,arrays,etc . Note that vector and arrays must be defined the usual way they are in Scheme. The operator <- works also with multidimensional arrays.
examples :
- {x <- 7}
- {x <- y}
- {m[3] <- 7}
- {T[k] <- x}
- {T[k] <- T[{k + 1}]}
- {T[m n] <- T[m {n + 1}]}
- {my-hash-tbl["three"] <- 3}
Definition and assignment of variables is made with the <+ operator but can also be done with the classic Scheme define.
example: {size <+ 1000}
Declaration of variable can also be used to declare one or many variables. (for explanation why there exist still <+ and declare and not only <- read the part history of project)
example: (declare x y z t)
How to load a Scheme+ program:
Scheme+ programs are loaded the usual way,example:
(load "start-λογικι-guile+.scm")
(infix-symb-min-dnf '{ {(not a) and (not b) and (not c) and (not d)} or {(not a) and (not b) and (not c) and d} or {(not a) and (not b) and c and (not d)} or {(not a) and b and (not c) and d} or {(not a) and b and c and (not d)} or {(not a) and b and c and d} or {a and (not b) and (not c) and (not d)} or {a and (not b) and (not c) and d} or {a and (not b) and c and (not d)} or {c and (not d)} } ) disj-norm-form = (or (and c (not d)) (and (not a) (not b) (not c) (not d)) (and (not a) (not b) (not c) d) (and (not a) b (not c) d) (and (not a) b c d) (and a (not b) (not c) (not d)) (and a (not b) (not c) d))
((!b ^ !c) v (c ^ !d) v (!a ^ b ^ d))
We can use a convention to name the Scheme+ programs and function with a
+ at end, keeping the .scm extension unchanged for compatibility.
10.Advanced examples:
Here is another example, from the Subset Sum Problem that show the use of <+ and <- (due to the impossibility to have easily a single operator :-( ) :
scheme@(guile-user)> (subset-sum-guile L-init t-init)
#t
note: in the example above condx is a macro that allow execution of arbitrary code between conditionals clauses,general syntax is:
In classic Scheme the code would be like that which is longer than in Scheme+:
If you want to use a single assignment operator <- it is possible using a declarative programming style with the declare macro:
It is also possible to use return keyword in function definition by def macro and have again another programming style with if then else that looks like traditionals language such as Python,Javascript,C/C++,etc... :
This example mix many Scheme+ Style and illustrate the use of return in a Python/C++ style:
11. History of project:
First i developped a new set of LET special forms with less bracket use and after i decided to use another assignment scheme based on infix operator for a little part,at some point the new assignment scheme was became so perfect that we no more need the set of LET special forms.But i also released the new set of LET special forms even if i consider those useless.Unfornunately Scheme do not allow to use only a single assignment operator because of declaration (define) of variables and set! are different and it does not exist a define-or-set! function. So i only had the choice to use 2 assignment operators, one acting as define (<+) and the other as set! (<-) or a single assignment operator <- and a declarative macro called declare which should be interesting for typed Scheme implementations too.
12.Additional documentation:
condx is a macro that allow execution of arbitrary code between conditionals clauses,syntax is :
condx is not a major feature of Scheme+
but it can replace a lot of 'if then elif', i dislike and still
misunderstand sometimes, 'else if' since BASIC !
defining new functions with def instead of the classic define we can now use return to escape immediately returning the value v:
(return v)
$ is a macro, it is a shortcut for begin.
But begin is dual in Scheme, it expands differently in expression context and definition context. See this for more information:
https://www.gnu.org/software/guile/manual/html_node/begin.html
For this reason it is sometimes necessary to
use a classic let to create a block,for example when defining a clozure.
As example consider those two definitions where
the first - foo - is erroneous:
error: define: not allowed in an expression
context in: (define x 1)
for this reason i provide a $+> macro
that is defined with a let as this:
We can now define the previous bar function in Scheme+ like this:
13.Caveats:
Inherent to Scheme language it as not been possible to simplify more assignment operator in a single one case. This is due to the fact that it can not be written a macro that do define-or-set! and that define can not be placed any where. See discussion in Guile devel mailing list archive in September 2021 For the place of define, by chance,some Scheme implementation allow define to be placed almost anywhere.
14.Implementation:
Mainly with Scheme macros which are not recursive (except in some obsolete features),so expansion is fast and code also. It also allows a great portabilty from one Scheme implementation to another one.
15.Future:
Scheme+ will be implemented for other Scheme. Racket (former DrScheme) first,....
16.Extra features:
The growable vector class which is not specific to Scheme+ is included in Scheme+ because it is intrinsic with <- operator of Scheme+.
"Growable vectors" are vectors with size growing dynamically when necessary. The documentation and examples are in the source code header.
Usage: (use-modules (growable-vector))
Not using the module will just show a warning like "possibly undefined function growable-vector?" as it is used in assignment.scm.No problem till you do not create a growable vector, and when you will do it you will have loaded the growable module class before and the warning message will vanish.This is the force of Scheme on other language:you can have an undefined function in your source code and it can run without problem (till the function is not called of course! which is the case in my code if no growable vector exist in your code too)
Other features include a part of my personal functional library when functions are not existing already in some SRFI (Scheme Request For Implementations).
17.Obsolete features:
As mentioned early the project idea was initially to enhance the set of LET special form. The use of the LET set is no more need with the new assignment operator but i release anyway those set of enhanced and simplified LETs, i just give some examples as names are self-explanatory.
The way to be able to use the new set of LET special form is to load the file let.scm which is available in the Scheme+ directory:
(load "let.scm")
Examples:
and here is the source code of this recursive macro defined with an accumulator:
a few others special forms simplifying the LETs (but still obsolete in my opinion):
let-arrow* :
letrec-arrow* , the same as let-arrow* but for recursive functions:
18.Comments:
Github web site technology force me to convert this page in Github Markup
Language, perheaps instead of HTML should i have make it directly in GML
but i did not know it existed before doing it. I did not want to make this
documentation so long and graphically complex to set up. Well it's done! i
hope you get interest in reading at least 10% :-) . And get as
pleasure to read and use than i take making and writing it.
This documentation is at end and it is quite impressive how long it
takes to document things! Even if i suppose i could have
forget things to talk about.
19. Special characters (for special people):
This is about the optional replacement symbols for some operators:
The hexadécimal unicode can be entered under Linux with : Ctrl-Shift u following with the hexadecimal unicode and Enter or Space bar, on Mac OS use CTRL+CMD+space to bring up the characters popover, then type in u + unicode and hit Enter to get it.
- <- : ← , unicode 2190 (this doesn't looks like but it is an hexadecimal number !) This operator assignment was used in APL
- -> : → ,unicode 2192
- <+ : ⥆ , unicode 2946 (Add a variable in the current environment)
- +> : ⥅ , unicode 2945
- <> : ≠ , unicode 2260
Example:
scheme@(guile-user)> {v ⥆ 7} scheme@(guile-user)> v 7
20. Scheme+ in action:
Example based on the Petrick method algorithm:
Example based on the Quine–McCluskey algorithm:
21. Contributors:
Maxime DevosRebecca Turner
Scheme+ by Damien Mattei