Scheme+ for Racket
#reader SRFI-105 | package: SRFI-105-for-Racket |
(require Scheme+) | package: Scheme-PLUS-for-Racket |
Scheme+ is an extension of the syntax of the Scheme language.
Scheme+ adds to Scheme a way to use also infix notation with a compatibility near 100% and not as a sub-system of Lisp syntax as it is often done but with a complete integration in the Scheme reader/parser system and also for Racket at REPL (Read Eval Print Loop).
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. As Scheme+ syntax is compatible with Scheme syntax you can mix the two syntaxes in the same program.
Scheme+ makes it easy the assignment of Scheme objects in infix (works also in prefix) notation with a few new operators <- (or: ← , :=),⥆ (or <+) for definitions.
Scheme+ makes it easy the access and assignment for arrays,strings,hash tables,etc by allowing the classic square brackets [ ] of other languages.
What Scheme+ is not: Scheme+ is not a DSL (Domain Specific Language) a contrario Scheme+ tends to be the more universal possible and to be applied in a lot of domains.
3 Scheme+ and Curly Infix SRFI-105 REPL (Read Eval Print Loop) |
4 A simple Hello World application in Scheme+ and Curly Infix |
1 Both prefix and infix expressions in the same world
{(3 + 1) * (2 * (+ 2 1) - (sin 0.3)) + ((* 2 5) - 5)} 27.817919173354642
(+ (* (+ 3 1) (- (* 2 (+ 2 1)) (sin 0.3))) (- (* 2 5) 5))
Other examples:
(define (fib n) (if {n < 2} n {(fib (n - 1)) + (fib (n - 2))} )) (fib 7) 13
{(- 7 (3 * (+ 2 4) - 1)) + 3} -7
SRFI 105 Curly Infix is an external parser necessary for the curly brackets { } syntax which is not in the base of scheme language.
Syntax transformers are sometimes used at a "compile" stage before the code run.
Parsing at runtime is rarely done but can be necessary when the parsed expression or some of her subexpressions remain ambiguous at prior parsing stages about being infix or prefix expressions.
Here the parsing process is activated by the encountering of curly parenthesis { } in the expressions (there exist also other ways to force the parsing):
(define (line-length x0 y0 x1 y1) (√ {(x1 - x0) ² + (y1 - y0) ²})) {(ksy / (√ 2)) * ((- x) + y)} (define (norm x y) {x ² + y ²})
2 Scheme+ Installation and Depandencies
Scheme+ can be installed via the Racket Package system or downloaded from Github.See the links at the top of this page. Scheme+ is designed to be used with the package SRFI-105 for Racket which is a curly infix reader also available in the same way described above.
3 Scheme+ and Curly Infix SRFI-105 REPL (Read Eval Print Loop)
As Scheme+ is designed to be used with the Curly infix syntax of SRFI-105, the latter one requires an external reader/parser and a specific REPL (Read Eval Print Loop). The REPL must be loaded in Racket GUI or invoked in the shell if you work in command line mode. The REPL file can be found in the source code of Scheme+ or SRFI-105 in the src/ sub-directory, his name is REPL-Scheme-PLUS.rkt.
#! /usr/bin/env -S racket --load REPL-Scheme-PLUS.rkt --repl ;; the line above is not mandatory,only for launching a script in command line ;; in CLI you must (require Scheme+) manually ;; but if you want to have syntax color in CLI start racket yourself and do: ;; (load "REPL-Scheme-PLUS.rkt") #lang reader SRFI-105 ; SRFI-105 Curly-infix-expressions (module repl racket (provide (all-defined-out)) (require Scheme+) ;; put your code here or simply use the REPL )
4 A simple Hello World application in Scheme+ and Curly Infix
#lang reader SRFI-105 ; SRFI-105 Curly-infix-expressions (module hello-world racket (provide (all-defined-out)) (require Scheme+) (display "Hello world") (newline) )
#reader SRFI-105 (provide (all-defined-out)) (require Scheme+) (display "Hello world") (newline)
5 Scheme+ Syntax and Conventions
5.1 Curly Infix notation with { }
In general Scheme+ use the same convention for infix expression than SRFI-105 Curly Infix that is an infix expression is between curly parenthesis { }. But infix sub-expressions are allowed to be between normal parenthesis ( ) like it is in mathematic notation. Infix or prefix is then autodetected.In case of ambiguities { } force infix mode. Inside curly infix expression surrounded by { } parenthesis associativity and operator precedence are applied.
#reader SRFI-105 (require Scheme+) > {3 * 5 + 2} ; the displayed symbol > is here only the Racket prompt (+ (* 3 5) 2) ; generated code displayed by REPL/parser 17 ; result
{3 * 7 + 2 - 12 / 3 + -2} 17
In the examples the display of the REPL/parser in prefix will generally be removed from examples for clarity, also the prompt > and the displayed #<eof> (end of file) printed by the SRFI-105 parser.
#lang reader SRFI-105 (require Scheme+) {3 · 5 + 2 ³} 23
Note that in the above example and in Scheme+ the operator · is *, also note that superscript chars are exposants.
In the following sections i will omit to rewrite the directives about lang or reader and the requirement in each example to keep the code and the web page compact.
An example of inner round brackets inside curly brackets in a full infix expression:
(define x {3 * (2 + 4) - 1}) (display x) 17
Example of a mixed infix and prefix expression autodetected:
{3 * (+ 2 4) - 1} 17
Other examples:
(define (fib n) (if {n < 2} n {(fib (n - 1)) + (fib (n - 2))} )) (fib 7) 13
{3 ² + 2 · 3 · 5 + 5 ²} 64
5.2 Square Bracket notation with [ ]
The square bracket notation can be used with any object that can be indexed or retrieved by a key: it works with vectors,strings of characters, arrays, hash tables,etc.
syntax
{T[k]}
{#(1 2 3 4)[2]} ($bracket-apply$ #(1 2 3 4) 2) ; code generated by the parser 3
I will not always display generated code by parser and end of file symbol as only the result is important for the programmer.
{#(1 2 3 4)[1]} 2
(define T (vector 1 2 3 4 5)) {T[3]} 4
{"hello"[4]} #\o
{"hello"[-4]} #\e
A slice notation with : is available.It allows the same features as the slicing of Python language and sometimes more.
{"elephant"[2 : 5]} "eph"
{#(1 2 3 4 5)[2 :]} '#(3 4 5)
{#(1 2 3 4 5 6 7)[2 * 5 - 8 : 3 * 5 - 10]} '#(3 4 5)
(define a 0) (define b 5) {"hello world"[a : b]} "hello"
syntax
{T[begin : end : step]}
{#(1 2 3 4 5 6 7 8 9)[3 : 8 : 2]} '#(4 6 8)
{#(1 2 3 4 5 6 7 8 9)[-1 : 5 : -1]} '#(9 8 7)
{#(1 2 3 4 5 6 7 8)[: : 3]} '#(1 4 7)
{#(1 2 3 4 5 6 7 8)[: : -2]} '#(8 6 4 2)
{"abcdefghijklmno"[3 : : 2]} "dfhjln"
The square bracket notation [ ] works also with multiple dimensions vectors:
{M <- #(#(1 2 3) #(4 5 6))} {M[1][2]} 6
You can use also this [ ] notation variant, like matrix notation, and compatible with arrays in case of you use them:
{M <- #(#(1 2 3) #(4 5 6))} {M[1 2]} 6
Examples:
{ᐁ[i][k] <- y[k] - z[i][k]}
5.3 File naming conventions
Generally a Scheme+ program file is named with a + symbol before the normal scheme extension, examples: SssDyna+.scm or SssDyna+.rkt.The Makefile (see section more below) i provide will automatically parse files named with this convention.
6 Scheme+ Reference
6.1 Declarations
(declare x) (when (identifier-binding #'x) "variable x exists") "variable x exists"
You can not both declare and define a variable in a program (module):
#reader SRFI-105 (provide (all-defined-out)) (require Scheme+) (declare x) (define x 3)
module: identifier already defined in: x
Note that declare is rarely used,instead assignment macros like <- are used, but it could be usefull in case a variable used in a block must be used outside the scope of the block too like in this example:
(declare x) {t <- 3} (when {t > 1} {x <- 7}) {x <- x + 1} x 8 ; result
By default the declared variables are set to NIL, that is ’()
(declare x) x '()
6.2 Definitions
syntax
{name <+ value}
{x <+ 7} x 7
Note: this is almost never used in Racket,because Scheme+ for Racket has a special feature of autodetecting if a variable needs to be defined before assignment and in this case it will define it for the programmer in the current block.
There is some alias of <+ :
syntax
Note: a lot of operators of Scheme+ that works left to right exist in the opposite sense, i will not document them as it is obvious,example:
syntax
{value ⥅ name}
Also a lot of Scheme+ operator have infix n-arity capability , i will not completely document them at the beginning of this document but in other parts later.
{name ⥆ name2 ⥆ 7} (list name name2) '(7 7)
6.3 Assignments
syntax
{name <- value}
{x <- 7} x 7
If the variable was not existing in the current environment it will be automatically declared,this is a special feature of the Scheme+ version for Racket that can rarely be reproduced in other scheme implementations.
There is some alias of <- :
syntax
{name := value}
{index := index + 1}
syntax
{z ← 1.13+1.765i}
syntax
{(name1 name2 ...) <- values}
{(x y z) <- (values 1 2 3)} (list x y z) '(1 2 3)
The assignment operator is a macro that works in taking in account the square bracket [ ] notation in the RHS (right hand side) of any expression:
(define T (make-vector 5)) {T[3] <- 7} {T[3]} 7
Remember :=,for Pascal language fans, is exactly the same as <-.
(require srfi/25) {a := (make-array (shape 0 5 0 3) 0)} {a[1 2] := 7} {a[1 2]} 7
Other example:
{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]}
Note in the example above the special neoteric expression available by SRFI-105 Curly Infix: მzⳆმz̃(z_output[j] z̃_output[j]) which is automatically parsed and transformed in the prefix expression : (მzⳆმz̃ z_output [j] z̃_output [j]), note also that მzⳆმz̃ is only a symbol.
Assignment and slicing works as in Python language or even better,allowing more possibilities.
Examples:
{v <+ (vector 1 2 3 4 5 6 7 8 9)} '#(1 2 3 4 5 6 7 8 9) {v[: : 2] <- (vector -1 -2 -3 -4 -5)} v '#(-1 2 -2 4 -3 6 -4 8 -5)
Note: remember <+ is for new definitions but in Racket i could have directly use <-.
{v <+ (vector 1 2 3 4 5 6 7 8 9)} {v[: : -2] := (vector -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13)[: : 2]} v '#(-9 2 -7 4 -5 6 -3 8 -1)
Remember := is exactly the same as <-.
{v <+ (vector 1 2 3 4 5 6 7 8 9)} {v[: : -2] <- "abcdefghijklmnop"[: : 2]} v '#(#\i 2 #\g 4 #\e 6 #\c 8 #\a)
{v <+ (vector 1 2 3 4)} {v[1 : 3] <- "abcdef"[2 : 4]} v '#(1 #\c #\d 4)
{v <+ (vector 1 2 3 4 5 6 7 8)} {v[3 : : 2] := (vector -1 -2 -3)} v '#(1 2 3 -1 5 -2 7 -3)
{v <+ (vector 1 2 3 4 5 6 7 8)} {v[5 : : -2] <- (vector -1 -2 -3)} v '#(1 -3 3 -2 5 -1 7 8)
{v <- (vector 1 2 3 4 5 6 7 8 9)} {v[7 : 2 : -2] <- (vector -1 -2 -3)} v '#(1 2 3 -3 5 -2 7 -1 9)
6.4 Procedure definitions
Example:
(def (foo L) (when (null? L) (return "empty list")) (cons "hello list :" L)) (foo '()) "empty list"
Note: return can only be used inside a def :
(return 7)
Another example:
;; this procedure check that we have a canonical infix expression ;; i call 'canonical' an expression such as 3 * 5 + 2 ;; in contrary an equivalent expression such as this one: - - 3 * 5 + 2 is not 'canonical',etc ;; conditions to be 'canonical' will be to have : ;; * at least 3 terms in expression ;; * odd number of terms ;; * operators between terms (def (infix-canonical? L) (define lgt (length L)) (when (or (< lgt 3) (not (odd? lgt))) (return #f)) (def (check-operators? L2) (when (null? L2) (return #t)) (if (not (operator-syntax? (car L2))) #f (check-operators? (cddr L2)))) (check-operators? (cdr L))) (infix-canonical? '(2 * 3 4)) #f (infix-canonical? '(2 * 3 + 4)) #t
syntax
(def name args args-optional ...)
(def t 3 * (+ 2 4) - 1) t 17 (def z (3 + 1) * (2 * (+ 2 1) - (sin 0.3)) + ((* 2 5) - 5)) z 27.817919173354642
syntax
(def name)
6.5 Control flow : Conditionals and Loops
(if #t then "True") "True"
(if #f then "You" "should" "not" "be" "there." else "I" "am" "here") "here"
Example from real code:
(if (singleton-set? sos) then ;; singleton (reverse acc) else ;; at least 2 elements in set of sets {mt-set1 <- (car sos)} ;; minterm set 1 {mt-set2 <- (cadr sos)} ;; minterm set 2 {mt-set2-to-mt-setn <- (cdr sos)} ;; minterm sets 2 to n {weight-mt-set1 <- (floor-bin-minterm-weight (car mt-set1))} ;; in a set all minterms have same weight {weight-mt-set2 <- (floor-bin-minterm-weight (car mt-set2))} {delta-weight <- {weight-mt-set2 - weight-mt-set1}} (if {delta-weight = 1} then ;; if minterms set are neighbours ;; unify neighbours minterms sets {unified-mt-set1-and-mt-set2 <- (funct-unify-minterms-set-1-unit-threads mt-set1 mt-set2)} (if (null? unified-mt-set1-and-mt-set2) (funct-unify-minterms-set-of-sets-rec-tail mt-set2-to-mt-setn acc) ;; the result will be the continuation with sets from 2 to n (funct-unify-minterms-set-of-sets-rec-tail mt-set2-to-mt-setn (insert unified-mt-set1-and-mt-set2 acc))) else (funct-unify-minterms-set-of-sets-rec-tail mt-set2-to-mt-setn acc))) ;; continue with sets from 2 to n
(define x 1) (condx ((= x 7) 'never) (exec (define y 3) (set! x 7)) ((= y 1) 'definitely_not) (exec (set! y 10) (define z 2)) ((= x 7) (+ x y z)) (else 'you_should_not_be_here)) 19
Note that scheme cond and scheme+ condx are a style of programming, you can use other style with same result, i think of using def and return with when and if that can replace them.
Example from real code of the subset sum problem from the file SssRec+.rkt in examples directory :
{c <- (first L)} {R <- (rest L)} (condx [ {c = t} (return #t (cons c s)) ] ;; c is the solution [ {c > t} (ssigma-solution-exact R t s) ] ;; c is to big to be a solution ;; c < t at this point ;; c is part of the solution [ exec {(c-sol sol) <- (ssigma-solution-exact R (t - c) s)} ] [ c-sol (return c-sol (append (cons c s) ; c is in the solution for t, we insert it in the previous partial solution sol)) ] ; we append with sol to get also the solutions of t - c,resulting in solutions of c + (t - c) = t ;; or c is not part of solution ;; or the two ! perheaps there exists 2 solutions using or not using c ! (in this case we got only the first found!) [ else (ssigma-solution-exact R t s) ] )
Note about the example above which mix condx and return because it is part of a procedure not displayed here, i encourage the reader to see the full source code SssRec+.rkt to understand it better.
{i := 5} (repeat (display i) (newline) {i := i - 1} until {i < 0}) 5 4 3 2 1 0
Note: above i used := which is exactly the same as <- or ←.
(for ({i := 0} {i < 3} {i := i + 1}) (display i) (newline)) 0 1 2
(for ({i <- 0} {i < n - 2} {i <- i + 1}) ;; create an array with 1 in front for the bias coefficient {z_1 <- #(1) + z[i]} ; + operator has been overloaded to append scheme vectors {z̃[i + 1] <- M[i] · z_1} ; z̃ = matrix * vector , return a vector {z[i + 1] <- vector-map(activation_function_hidden_layer z̃[i + 1])})
Example from matrix multiplication, compute element i,j of result:
(for ({k <- 0} {k < p1} {k <- k + 1}) {sum <- sum + M1[i][k] * M2[k][j]})
Note about Scheme+ for Racket: the for described above replace the original for of Racket, you should back up it as below before using that. Also Scheme+ define in-range and reversed as in Python language but they already exist in Racket Scheme.
(require (only-in racket/base [for for-racket])) ;; backup original Racket 'for' (for-racket ([i (reverse (range 1 i_output_layer))]) {nc <- vector-length(z[i])} {ns <- vector-length(z[i + 1])} (for-racket ([j (range nc)]) {ᐁ[i][j] <- (for/sum ([k (range ns)]) (მzⳆმz̃(z[i + 1][k] z̃[i + 1][k]) · M[i][k (j + 1)] · ᐁ[i + 1][k]))}) ; modification des poids de la matrice de transition de la couche i-1 à i {modification_des_poids(M[i - 1] ηₛ z[i - 1] z[i] z̃[i] ᐁ[i] მzⳆმz̃)})
6.6 Blocks
(if (compare-minterm-and-implicant {iepi[lin 0]} {iepi[0 col]}) ;; then ($> (incf cpt-mt) (when (= 1 cpt-mt) {lin-pos-epi ← lin}) ;; position of essential prime implicant {iepi[lin col] ← 1}) ;; else {iepi[lin col] ← 3})
(define-syntax $+> (syntax-rules () ((_ ev) (let () ev)) ;; there can be a <+ in it expanding with a 'define' ((_ ev ...) (let () ev ...))))
;; PHASE 0 : eliminate equivalence ;; a <=> b ----> (a => b) and (b => a) (define (elim-equivalence expr) (cond ((symbol? expr) expr) ((boolean? expr) expr) ((isNOT? expr) `(not ,(elim-equivalence (arg expr)))) ((isIMPLIC? expr) `(=> ,(elim-equivalence (arg1 expr)) ,(elim-equivalence (arg2 expr)))) ((isEQUIV? expr) ($+> ;; a <=> b ----> (a => b) and (b => a) {a <+ (arg1 expr)} ; definitions, as : (define a (arg1 expr)) {b <- (arg2 expr)} ; note: in Racket i could use also <- which auto-define the variable if necessary : {ae <- (elim-equivalence a)} {be <- (elim-equivalence b)} `(and (=> ,ae ,be) (=> ,be ,ae)))) (else `(,(operator expr) ,(elim-equivalence (arg1 expr)) ,(elim-equivalence (arg2 expr))))))
6.7 Operators and Procedures
Note: exponentiation can also be written with superscript numbers and expressions,see after in this document.
(√ 2) 1.4142135623730951
As being procedurals && and ∣∣ are not short-circuited.
{3 ≤ 4 < 5} #t
;; we plot only in the window (when {x0 ≥ 0 and x0 ≤ xws and x1 ≥ 0 and x1 ≤ xws and y0 ≥ 0 and y0 ≤ ywsp and y1 ≥ 0 and y1 ≤ ywsp} (send dc draw-line x0 y0 x1 y1))
Note: some Qi operators not documented here (see Qi documentation for them) are recognized, for example ~> should immediately work in infix.
6.8 Superscript
{2 ³} (** 2 3) ; parsed expression displayed 8
(define n 3) {3 ⁻²·⁽ⁿ⁻⁴⁾} 9
Note that alphabetic superscript characters and numeric superscript characters can display at different level of altitude in the browser font system, this is not the case in Emacs editor. Note also that not all alphabetic characters currently exists in superscript, here is the source code definitions:
(define superscript-string "⁻⁺⁰¹²³⁴⁵⁶⁷⁸⁹") ; super script numbers and signs (define superscript-only-string (string-append superscript-string ; numbers and signs "ᴬᵃᴮᵇᶜᴰᵈᴱᵉᶠᴳᵍᴴʰᴵᶦᴶʲᴷᵏᴸˡᴹᵐᴺⁿᴼᵒᴾᵖᴿʳˢᵀᵗᵁᵘⱽᵛᵂʷˣʸᶻ" ; note all letters are not available (ex:C,Q,q...) "⁽⁾" "⸱" ; will be used as decimal separator ;; "*" ; multiplication is ambiguous (both super and normal script) ;; "·" ; another multiplication symbol (but ambiguous too) "⸍"; division ))
Note under Linux and on a PC (french) keyboard superscript characters can be generated with the keystroke sequence: ^ n where n is a number or sign.(but this not works for alpha char as it only put some accent on the alpha char)
Superscript also works to write complex numbers exponents:
{3 ⁻²⁺³ᶦ} (** 3 (+ (- 2) 0+3i)) ; code generated by parser -0.10979199190468787-0.01707036982454185i
Note that unfortunately for superscript complex number the lower case superscript i does not exist and I is not really a mathematical convention for complex number but you can use the true electrical convention that use the symbol j:
{2 ⁻⁵⁺⁷ʲ} (** 2 (+ (- 5) 0+7i)) ; code generated by parser 0.004349621840375144-0.03094581215359732i
{2-3i ⁻⁵⁺⁷ʲ} (** 2-3i (+ (- 5) 0+7i)) ; code generated by parser 0.38835198150723493+1.5475077026949722i
6.9 Overloading procedures and operators
Overloading must be set at a module toplevel, it will not works in a procedure,and there is no need of that. You can provide an overloaded procedure or operator from the module where it is defined and overloaded to other modules.
Different macros and procedures are provided for overloading because of the various type of operator or procedure, operator can be only binary or be n-arity,operators have associativity properties that do not have procedures.Also operators or procedure can preexist in Scheme or you can overload one of your new procedure or operator.All that requires different macros and procedures for overloading that can not be fusioned in a generic macro.
first we define that a procedure or operator will be overloaded, this will result in creating a new procedure or operator that will be specialized in the next step.
second we overload a procedure or operator by providing a specialized function and some predicates that will be used to test the arguments at runtime.
With this schema we can have a same function (procedure or operator) that will works on different arguments.
Note that we can also extend square bracket [ ] to use different type of object, the same way it is done in the Python language for the angle bracket.This is done by overloading the underlaying procedure that do the bracket apply and the assignment macros,we will see that at the end of this section.
(define-overload-procedure area)
(define (area-square x) (* x x)) (overload-procedure area area-square (number?)) (define (area-rect x y) (* x y)) (overload-procedure area area-rect (number? number?)) (area 3) 9 (area 2 3) 6
Another example:
(define-overload-procedure uniform) ;; return a number in ]-1,1[ ;; the dummy parameter is needed by a flomat procedure (define (uniform-dummy dummy) {-1 + (random) * 2}) ; return a random number between [inf, sup] (define (uniform-interval inf sup) {gap <- sup - inf} {inf + gap * (random)}) (overload-procedure uniform uniform-dummy (number?)) (overload-procedure uniform uniform-interval (number? number?))
(define-overload-existing-procedure length)
(overload-existing-procedure length vector-length (vector?)) (overload-existing-procedure length string-length (string?)) (length #(1 2 3 4)) 4 (length '(1 2 3)) 3 (length "abcde") 5
Note that when no defined predicates matches the arguments, in the above example nor vector? nor string? will return true for a list then the overloading schema fall back to the original procedure definition that is here length that works with list.
(define-overload-existing-operator +)
(overload-existing-operator + vector-append (vector? vector?))
Now the + append vectors like in Python language:
{#(1 2 3) + #(4 5 6 7)} '#(1 2 3 4 5 6 7) {(vector 1 2 3) + (vector 4 5 6 7)} '#(1 2 3 4 5 6 7) ;; create an array with 1 in front for the bias coefficient {z_1 <- #(1) + z[i]} ; + operator has been overloaded to append scheme vectors
(define-overload-existing-n-arity-operator +)
(define (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)
Example for accessing and modifying a line of a matrix:
(struct matrix-vect (v)) ;; matrix based on vector of vectors (define (matrix-vect-line-ref M lin) {v <- (matrix-vect-v M)} {v[lin]}) (define (matrix-vect-line-set! M lin vect-line) {v <- (matrix-vect-v M)} {v[lin] <- vect-line}) (overload-square-brackets matrix-vect-line-ref matrix-vect-line-set! (matrix-vect? number?)) (define Mv (matrix-vect #(#(1 2 3) #(4 5 6)))) {Mv[1]} '#(4 5 6) ; define getter,setter (define (matrix-vect-ref M lin col) {v <- (matrix-vect-v M)} {v[lin][col]}) (define (matrix-vect-set! M lin col x) {v <- (matrix-vect-v M)} {v[lin][col] <- x}) (overload-square-brackets matrix-vect-ref matrix-vect-set! (matrix-vect? number? number?)) {Mv[1][0]} 4
See the full example at https://github.com/damien-mattei/Scheme-PLUS-for-Racket/blob/main/examples/racket/matrix-by-vectors+.rkt
7 Using Makefile and debugging Scheme+ program
The Racket GUI can not display the line of error in a Scheme+ program.The reason is because the Scheme+ program is pre-parsed by the SRFI 105 reader and the Racket compiler has not access to the original lines of code and even do not display the error line relative to the resulting parsed line of code.
If you have to debug your source code you must generate a Scheme file from your Scheme+ file with curly-infix2prefix4racket in SRFI 105 package and load the result file in Racket GUI. Then you will have all the debug information of Racket on the scheme file.
Another solution is to simply copy/paste the pure Racket code generated by the SRFI 105 Curly Infix parser and run it like a normal Racket program. Again you will have all the information about errors with the true line number displayed.
Another method is to use the Makefile provided:
https://github.com/damien-mattei/Scheme-PLUS-for-Racket/blob/main/examples/racket/Makefile
Put the Makefile in the same place of your Scheme+ program and type in the terminal make. The Makefile will parse all Scheme+ program in the same directory and generate the scheme version in MODULE_DIRECTORY=parsed_files_directory.You can then just load and run the parsed files in Racket GUI to use all the Racket features and tools (macro stepper,etc) for debugging.
Example:
mattei@acer:~/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/chaos$ ls -la |
total 28K |
drwxrwxr-x 3 mattei mattei 4,0K sept. 21 17:42 . |
drwxrwxr-x 7 mattei mattei 4,0K sept. 26 22:06 .. |
-rw-r--r-- 1 mattei mattei 12K sept. 21 17:42 chaos+.rkt |
-rw-rw-r-- 1 mattei mattei 1,5K nov. 23 2024 Makefile |
drwxrwxr-x 2 mattei mattei 4,0K déc. 16 2024 parsed_files_directory |
mattei@acer:~/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/chaos$ make |
PARSING chaos+.rkt : |
../../../../../SRFI-105-for-Racket/src/curly-infix2prefix4racket.rkt chaos+.rkt > parsed_files_directory/chaos+.rkt |
|
mattei@acer:~/Dropbox/git/Scheme-PLUS-for-Racket/main/Scheme-PLUS-for-Racket/examples/chaos$ ls -la parsed_files_directory/ |
total 16 |
drwxrwxr-x 2 mattei mattei 4096 déc. 16 2024 . |
drwxrwxr-x 3 mattei mattei 4096 sept. 21 17:42 .. |
-rw-r--r-- 1 mattei mattei 6032 sept. 26 22:23 chaos+.rkt |
8 Place to find Scheme+ programs and examples
More examples can be find in this directory and subdirectories:
https://github.com/damien-mattei/Scheme-PLUS-for-Racket/blob/main/examples/
9 Emacs configuration for syntax highligting of Scheme+
Add the line below in your .emacs configuration file:
(font-lock-add-keywords 'scheme-mode '(("\\<\\(define\\|define+\\|def\\|def+\\|return\\|return-rec\\|:=\\|<-\\|condx\\|then\\|else\\)\\>" . font-lock-keyword-face)))
10 Thoughts
I hope this documentation is not a write-only1 « APL is a write-only language. I can write programs in APL, but I can’t read any of them ». Roy Keir one and will be usefull for any reader.