diff options
author | Joel Kronqvist <joel.kronqvist@iki.fi> | 2025-08-23 22:36:05 +0300 |
---|---|---|
committer | Joel Kronqvist <joel.kronqvist@iki.fi> | 2025-08-23 22:36:05 +0300 |
commit | 1c0645c559d19d9c7ceb1db51d68e052a180709f (patch) | |
tree | 857d4295ebeb3e8e8f8050d8b5beac80f2b52ab7 | |
parent | ef4eb90eac9d99c1f677baca5ddaca2afbc1627f (diff) | |
download | myslip-devel.tar.gz myslip-devel.zip |
-rw-r--r-- | README.md | 82 | ||||
-rw-r--r-- | TUTORIAL.md | 201 |
2 files changed, 149 insertions, 134 deletions
@@ -4,24 +4,24 @@ myslip README ============= Below is a short introduction to this programming language -and instructions on setting it up. -`TUTORIAL.md` may help getting familiar with the language. +and instructions on setting it up. `TUTORIAL.md` may help +getting familiar with the language. Language introduction --------------------- Myslip is a functional, symbolically interpreted programming -language. It's syntax is inspired by lisp and polish -notation. The author hasn't programmed much in lisp-variants -or in (non-general-purpose) functional languages, so the -language might include some slips or oddities, hence -the name. But the language is coherent within itself, -so learning it shouldn't be too hard. +language. It's syntax is inspired by lisp and polish notation. +The author hasn't programmed much in lisp-variants or in +(non-general-purpose) functional languages, so the language +might include some slips or oddities, hence the name. But the +language is coherent within itself, so learning it shouldn't +be too hard. -Valid myslip s-expressions may be atoms of different types -or lists of them, though not all syntactically valid -myslip expressions are ones that can be evaluated. +Valid myslip s-expressions may be atoms of different types or +lists of them, though not all syntactically valid myslip +expressions are ones that can be evaluated. The following is s-expressions explained intuitively in something that looks a bit like a BNF: @@ -29,22 +29,20 @@ something that looks a bit like a BNF: S-expression := Atom | List Atom := Operator | Value | Variable ... List := (S-expression ... S-expression) -``` Don't take it to be exact. For example lists are actually implemented as linked lists. -The myslip interpreter performs type checking on -s-expressions and evaluates them according to polish -notation, ie. the first S-expression of a list is taken to -be the operator that is passed the rest of the expressions -as arguments. +The myslip interpreter performs type checking on s-expressions +and evaluates them according to polish notation, ie. the first +S-expression of a list is taken to be the operator that is +passed the rest of the expressions as arguments. Running myslip -------------- -myslip can be built using cargo 1.88.0. Instructions to -install cargo can be found online: +myslip can be built using Cargo versions 1.88.0 and 1.89.0. +Instructions to install cargo can be found online: https://doc.rust-lang.org/cargo/getting-started/installation.html If you are having trouble due to having a different version @@ -52,21 +50,45 @@ and you used rustup for your cargo installation, you can follow these instructions on how to change the rust version https://rust-lang.github.io/rustup/overrides.html -TODO: system requirements? try it out on debian -So, for example +So, for example on some linux systems featuring the `apt` +package manager, the following works (tested on Debian 13)`: ```console -$ sudo apt install curl git +$ sudo apt install curl git builb-essential $ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +$ . "$HOME/.cargo/env" $ git clone 'https://git.cron4.fi/myslip.git' $ cd myslip ``` -and then myslip can be accessed either via `cargo run`, -or alternatively you can add it to your `$PATH`: +and then myslip can be accessed either via `cargo run`, or +alternatively you can add it to your `$PATH`: ```console $ cargo build +$ mkdir -p ~/.local/bin +$ export PATH="$PATH:~/.local/bin" +$ echo 'export PATH="$PATH:~/.local/bin"' >> ~/.bashrc +$ ln "$(pwd)/target/debug/myslip" ~/.local/bin/myslip +$ myslip +``` + +Installation has been tested on Aalto SSH servers in addition +to debian/13 in case you prefer that. Here is how to install +and compile the project there: +```console +$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +$ ~/.cargo/bin/cargo --version +$ git clone 'https://git.cron4.fi/myslip.git' +$ cd myslip/ +$ ~/.cargo/bin/cargo build +$ mkdir -p ~/.local/bin $ ln "$(pwd)/target/debug/myslip" ~/.local/bin/myslip +$ export PATH="$PATH:~/.local/bin" $ myslip ``` +Of course you must update your path next time when you open +your shell too, so you might want to add +`export PATH=$PATH:~/.local/bin` to your `~/.bashrc` in case +you don't have a personal preference on how to manage your +`$PATH` and other environment variables. Exit the REPL by pressing CTRL+C, CTRL+D or entering "exit". @@ -79,15 +101,15 @@ Running `myslip filename.slip` tries to read a file named `filename.slip` as an s-expression and evaluates it. On the other hand, running `myslip` without specifying an -executable file opens a REPL, in which you can enter -one s-expression per line for evaluation. Variables bound -in earlier expressions can be used. +executable file opens a REPL, in which you can enter one +s-expression per line for evaluation. Variables bound in +earlier expressions can be used. If you wish to load a set of definitions from a file, say `header.slip`, use the `--load`-argument (`-l` for short): ```console $ myslip -l header.slip ``` -The standard library, found in `stdlib.slip`, is loaded -in an equivalent way by default. If you wish to disable -it, you can use the option `--no-stdlib`. +The standard library, found in `stdlib.slip`, is loaded in an +equivalent way by default. If you wish to disable it, you can +use the option `--no-stdlib`. diff --git a/TUTORIAL.md b/TUTORIAL.md index df2eea7..701839b 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -3,9 +3,8 @@ Tutorial ======== -This tutorial consists of a language tour -and a set of exercises, in the order of -mentioning. +This tutorial consists of a language tour and a +set of exercises, in the order of mentioning. Tour @@ -15,8 +14,8 @@ Tour Arithmetic operations --------------------- -Addition, multiplication, subtraction and division -are supported with +, *, - and / respectively. +Addition, multiplication, subtraction and division are +supported with `+`, `*`, `-` and `/` respectively. ```console > (+ 1 1) 2 : Int @@ -28,9 +27,9 @@ are supported with +, *, - and / respectively. 2 : Int ``` -As addition and multiplication are associative, -multiple arguments can be used (even just one, -even though that doesn't really make sense): +As addition and multiplication are associative, multiple +arguments can be used (even just one, even though that +doesn't really make sense): ```console > (+ 1 2 3) 6 : Int @@ -57,8 +56,8 @@ standard library: Booleans -------- -Myslip supports booleans and `and`, `or`, `xor` and `not` -for their comparisons. +Myslip supports booleans and `and`, `or`, +`xor` and `not` for their comparisons. ```console > true true : Bool @@ -74,8 +73,8 @@ false : Bool false : Bool ``` -If-expressions are provided by the standard library in the -form `(if [condition] [iftrue] [iffalse])`: +If-expressions are provided by the standard library +in the form `(if [condition] [iftrue] [iffalse])`: ```console > (if true 1 0) 1 : Int @@ -121,10 +120,9 @@ x saved > x 1 : Int ``` -The REPL interprets this as `((let x 1) x)`, which you -could also type but would make a more cumbersome REPLing -experience (and would make loading definitions from files -nigh impossible). +The REPL interprets this as `((let x 1) x)`, which you could +also type but would make a more cumbersome REPLing experience +(and would make loading definitions from files nigh impossible). Shadowing works as expected: ```console @@ -139,8 +137,8 @@ and after it it is too, while in the middle term where Types ----- -Types are written as seen in the return types in the REPL. -The base types of myslip are +Types are written as seen in the return types +in the REPL. The base types of myslip are * `Int` for integer * `Bool` for boolean * `Nil` for `()` @@ -149,25 +147,25 @@ The base types of myslip are * `Quote`, `Vector` and `Sum` for use in data structures. The types used in data structures will be covered later. -These can be combined using the arrow type, list type and -vector type. +These can be combined using the arrow +type, list type and vector type. -`(A -> B)` denotes a function with arguments of type `A` -and a return value of type `B`. +`(A -> B)` denotes a function with arguments +of type `A` and a return value of type `B`. `(A B C D)` denotes a 4-length list with the respective types -at respective positions. Though if it is an actual list not -to be interpreted, in most cases a `Quote` will be present in -the type: `(Quote (A B C D))`. Note that the length of a list -can be any nonnegative integer (within memory constraints), -but it needs to be fixed. +at respective positions. Though if it is an actual list not to +be interpreted, in most cases a `Quote` will be present in the +type: `(Quote (A B C D))`. Note that the length of a list can +be any nonnegative integer (within memory constraints), but it +needs to be fixed. `(A ...)` denotes a list the length of which is not known and whose each element is of type `A`. If this is in a vector structure, `Vector` is prepended as such: `(Vector (A ...))`. -`(Sum A B)` denotes the type of a coproduct, the left type of -which is `A` and right type `B`. +`(Sum A B)` denotes the type of a coproduct, the +left type of which is `A` and right type `B`. Generics have limited support in myslip. They are written in uppercase. @@ -177,13 +175,16 @@ Functions --------- Functions are written in the form of -`(fn [argument list] [argument type list] [return type] [function body])`. -They don't have names of themselves, but they can be bound -using `let`. - -The following example features a simple increment function -and a function for checking if a integer is between two -others. +```myslip +(fn [argument list] [argument type list] [return type] + [function body] +) +```. +They don't have names of themselves, +but they can be bound using `let`. + +The following example features a simple increment function and +a function for checking if a integer is between two others. ```console > (let ++ (fn a Int Int (+ a 1))) ++ saved @@ -210,19 +211,16 @@ In principle, ```myslip (1 2 3 4 5) ``` -is a valid list, but it is evaluated by the interpreter, -which assumes that the first term, `1`, is an operator. -That's why constructing a list requires the operator -`quote`: +is a valid list, but it is evaluated by the interpreter, which +assumes that the first term, `1`, is an operator. That's why +constructing a list requires the operator `quote`: ```console -> quote -quote : (T -> (Quote T)) > (quote 1 2 3 4 5) (quote 1 2 3 4 5) : (Quote (Int Int Int Int Int)) ``` -In contrast from many other lisp-variants, in myslip -sub-expressions are simplified. +In contrast from many other lisp-variants, +in myslip sub-expressions are simplified. ```console > (quote (+ 1 1) (+ 2 2)) (quote 2 4) : (Quote (Int Int)) @@ -231,19 +229,17 @@ sub-expressions are simplified. The elements of a list can of course be of different types: ```console > (quote - 0 (quote 1 2)) -(quote - 0 (quote 1 2)) : (Quote ((Int Int) -> Int) Int (Quote (Int Int))) +(quote - 0 (quote 1 2)) : (Quote (((Int Int) -> Int) Int (Quote (Int Int)))) ``` Vectors ------- -Vectors behave roughly the same as lists, except -their length is not specified on type-level, and -their elements can be only of one type. +Vectors behave roughly the same as lists, except their length +is not specified on type-level, and their elements can be only +of one type. ```console -> vector -vector : ((T ...) -> (Vector (T ...))) > (vector 1 2 3 4) (vector 1 2 3 4) : (Vector (Int ...)) ``` @@ -260,32 +256,31 @@ Two vectors can be concatenated using the `<>`-operator: (vector 1 2 3 4) : (Vector (Int ...)) ``` -The standard library provides the function `sum` to -calculate the sum of a vector of integers. Through -type conversions, this works for lists too. +The standard library provides the function `sum` to calculate +the sum of a vector of integers. Through type conversions, +this works for lists too. Coproducts ---------- -Coproducts ("either-or -type") are instantiated with the -`coprod` operator: +Coproducts ("either-or -type") are +instantiated with the `coprod` operator: ```console > (coprod (+ 1 2) Bool) (coprod 3 Bool) : (Sum Int Bool) > (coprod Int +) (coprod Int +) : (Sum Int ((Int ...) -> Int)) ``` -They can be destructured with pattern matching as covered in -the next section. +They can be destructured with pattern +matching as covered in the next section. Pattern matching ---------------- -Values can be inspected using pattern matching. -For basic lists, the `case`-operator can be used. -It's syntax is: +Values can be inspected using pattern matching. For basic +lists, the `case`-operator can be used. It's syntax is: ```myslip (case [condition] ([pattern 1] [value 1]) @@ -293,8 +288,8 @@ It's syntax is: ...) ``` -Just note that every case expression needs to -have one wildcard pattern, to ensure exhaustiveness. +Just note that every case expression needs to have +one wildcard pattern, to ensure exhaustiveness. Here's a very basic example: ```console @@ -306,23 +301,22 @@ x saved 1 : Int ``` -The list can be destructured into parts. If there -is a `..[variable name]` at the end of a pattern, -it matches the rest of the list. +The list can be destructured into parts. If there is a +`..[variable name]` at the end of a pattern, it matches the +rest of the list. ```console > (case (quote 1 2 3 4 5) ((h1 h2 ..tail) (+ h1 h2)) ((h ..t) h) (_ 0)) 3 : Int ``` -Nesting in pattern matching works fine too. +Nesting of lists in pattern matching works fine too: ```console > (case (quote 1 2 (quote 3 4) 5) ((1 2 (quote 3 4) 5) true) (_ false)) true : Bool ``` -Pattern matching can be done on vectors too. -Just remember that the rest pattern `..r` needs to be at -the end of the whole pattern. +Pattern matching can be done on vectors too. Just remember +that the rest pattern `..r` needs to be at the end of the whole pattern. ```console > (let myvec (vector 1 2 3 4 5)) myvec saved @@ -332,13 +326,15 @@ myvec saved Coproducts are destructured with `inl` and `inr`: ```console +> (let myprod (coprod 2 Bool)) +myprod saved > (case myprod ((inl x) x) ((inr b) (if b 1 0)) (_ -1)) 2 : Int > (case myprod ((inl 2) true) (_ false)) true : Bool ``` -Even if when both an `inl` and `inr` arm are supplied, -a wildcard pattern is required as myslip doesn't feature an +Even if when both an `inl` and `inr` arm are supplied, a +wildcard pattern is required as myslip doesn't feature an exhaustiveness checker. That is the tradeoff for having nesting: ```console @@ -353,11 +349,10 @@ fairly trivial, but that won't happen this summer) General recursion ----------------- -General recursion in myslip is achieved through the -fixed point operator `fix`. It works the same as in -the course materials: it takes a function of type -`(T -> T) -> (T -> T)` as an argument and uses that -for recursive calls. +General recursion in myslip is achieved through the fixed +point operator `fix`. It works the same as in the course +materials: it takes a function of type `(T -> T) -> (T -> T)` +as an argument and uses that for recursive calls. The factorial function could be implemented as such: ```myslip @@ -377,12 +372,11 @@ The factorial function could be implemented as such: Printing -------- -This is quite a useless feature as the repl already -echoes all results. But if you fancy, you can print -anything using the `print` operator, which is -behaviorally equivalent to Nil as long as its argument -is valid myslip. For example, with the standard library -enabled: +This is quite a useless feature as the repl already echoes +all results. But if you fancy, you can print anything using +the `print` operator, which is behaviorally equivalent to Nil +as long as its argument is valid myslip. For example, with +the standard library enabled: ```console > (let x 1) x saved @@ -399,8 +393,8 @@ checker look at you weird, the standard library offers the convenience function `discard`, which takes anything as arguments and returns nil. -Here's an example that shows why you might want to use -discard: +Here's an example that shows why +you might want to use discard: ```console > ((print 1) (print 2)) Type error: invalid operator: '(print 1)' @@ -411,8 +405,8 @@ found: 'Nil' 2 (quote () ()) : (Quote (Nil Nil)) ``` -As you can see, the return type of the latter try at using -print twice looks horrible. +As you can see, the return type of the latter +try at using print twice looks horrible. ```console > (discard (print 1) (print 2)) 1 @@ -426,15 +420,15 @@ Loops ===== Loops are quite worthless in a functional language, but I -might get points for them. The standard library provides the -functions `repeat` and `map-i->i`. +might get points for them. The standard library provides +the functions `repeat` and `map-i->i`. The former one has the syntax -`(repeat [operation] [number of times])` which quite -intuitively repeats the given operation the given number of -times. The operation should be a function that takes an -integer as an argument and returns Nil, so this function is -quite useless for anything but printing. +`(repeat [operation] [number of times])`, +which quite intuitively repeats the given operation the given +number of times. The operation should be a function that takes +an integer as an argument and returns Nil, so this function is +useless for anything but printing. ```console > (repeat (fn x Int Nil (print x)) 10) 1 @@ -506,11 +500,11 @@ may want to try out the `--babysteps` feature Exercise 3: Fibonacci sequence ------------------------------ -Write a function that calculates the n:th fibonacci number, -when n is given as the sole argument. +Write a function that calculates the n:th fibonacci +number, when n is given as the sole argument. -This should make you familiar with function definitions and -fixed point recursion. +This should make you familiar with function +definitions and fixed point recursion. Example of desired behaviour: ```console @@ -532,12 +526,13 @@ This should make you familiar with typing function definitions that contain generics. -Exercise 5: Map left / right (integers) ---------------------------------------- +Exercise 5: Left / right or +--------------------------- -Write functions `map-left` and `map-right` that take a -coproduct and a function and map the corresponding side of -the given coproduct using the given function. +Write functions `left-or` and `right-or` that take a coproduct +and a default value of the type of the corresponding hand of the +coproduct, and if the coproduct is of the left type, returns its +value, otherwise returns the default value. Note: the solution is not in the same directory as the rest, but you can find implementations for these in the standard @@ -545,5 +540,3 @@ library. This exercise will train your abilities to use generics more than the last one. You will get familiar with coproducts. - - |