Mylalang
Mylalang is a LISP implemented in Rust.
TODO: Document head and tail
Content
- Content
- Usage
- Types
- Arithmetic Operators
- Relational Operators
- Comments
- Binding values
- Functions
- Aliasing
- Inspecting
- The
do
Function - Conditionals
- Files
- Recursion and Loops
- The
cons
Function - The
list
Function - The
&
Operator and theapply
Function - Evaluating Code
- Native functions
Table of contents generated with markdown-toc
Usage
Firstly, clone the repository to a local directory
Mylalang comes with two binaries: an interactive REPL and a interpreter.
Interactive REPL
The REPL can be started by running:
1cargo run --bin imy
Interpreter
The REPL can be started by running:
1cargo run --bin imy
Types
Only primitive types exist in Mylalang. Constructing types does not exist.
Integer
s written like1
or1042
.Float
s written like89.353
.String
s written like “This is a string!” or “Hello, world.”Null
written asnil
.Array
s written as[]
contain other types, written like[2 23 88]
or["mixed list" 823.45 "containing different" 90 "types" nil]
List
s written as()
contain other types and form the syntax of the language. Can be constructed using thelist
function.Function
s written as(fn [arg1 arg2 ...argN])
. More about this under the functions section.Boolean
values are natural booleans, written astrue
orfalse
Arithmetic Operators
Addition
Subtraction
Multiplication
Division
List Processing
All operators work against their arguments as lists, reducing to a result:
Relational Operators
Equality
Greater than
Less than
Greater than or equal to
Less then or equal to
Comments
Comments start with ;
1; This is a comment!
2
3; This code does not execute
4; (def a "123")
5
6a
7
8thread 'main' panicked at '"a" does not exist within this scope
9
10; oops :)
Binding values
The def
keyword allows bind a value (and functions but more on that later) to a name:
Functions
In Mylalang, functions are a primitive type. They can be bound to a name and used as a value. Through this, the language supports both anonymous and named functions.
Named Functions
The def
keyword can be used to bind a function to a name
Functions can take multiple parameters
1(def greet (fn [firstname lastname] (+ "Hello " firstname " " lastname)))
2=> <#def "greet">
3
4(greet "John" "Travolta")
5=> Hello John Travolta
Anonymous Functions
Anonymous functions are called without binding it to a name. An example makes this easier to explain
The function below doubles a given number:
The return value is a function. Like this, it’s a bit useless but it provides the foundation of using functions as values. They can be called immediately or passed as a function:
Executing an anonymous function
Comparing to its named counterpart
1(def favfruit (fn [fruit] (+ "My favorite fruit is " fruit)))
2=> <#def "favfruit">
3
4(favfruit "Banana")
5=> My favorite fruit is Banana
Passing functions as values
1(def double (fn [a] (* a 2)))
2=> <#def "double">
3
4(def applyfunc (fn [f a] (+ "The value of the applied function is: " (f a)))
5=> <#def "applyfunc">
6
7(applyfunc double 21)
8=> The value of the applied function is: 42
Local Binding
It is possible to bind local scoped variables to a function using the let
keyword. This keyword functions the same as def
but can only be used within a function.
let
is a function which takes multiple arguments. The first is list
of name and value pairs and binds the name in the first position to a value in the second position of each pair in the list.
The remaining arguments can be function calls with the last function to return a value. This is used when you want to execute side effects or inspect a result.
1# This example needs to be compressed into a single line to execute correctly in the REPL.
2
3(def shifter (fn [i]
4 (let [x (* i i)
5 y (+ i i)
6 z (if (> x 50) (- x y) (+ x y))]
7 (+ "Final value is " z))))
8
9=> <#def "shifter">
In the example above, x
equals the value if i
multiplied by i
.
Function Overloading
It’s possible to bind multiple functions to the same name if they accept different parameters.
1(def greeter (fn [name]
2 (let [greeting (+ "Hello " name)]
3 (inspect greeting))))
4=> <#def "greeter">
5
6(def greeter (fn [first_name last_name]
7 (let [greeting (+ "Hello " first_name " " last_name)]
8 (inspect greeting))))
9=> <#def "greeter">
10
11(def greeter (fn [salutation first_name last_name]
12 (let [greeting (+ "Hello " salutation ". " first_name " " last_name)]
13 (inspect greeting))))
14=> <#def "greeter">
15
16(greeter "Alan")
17=> Hello Alan
18
19(greeter "Alan" "Turing")
20=> Hello Alan Turing
21
22(greeter "Dr" "Alan" "Turing")
23=> Hello Dr. Alan Turing
This allows to build boundaries in a very convenient way, particularly with recursion.
Aliasing
Because all types - including functions - are just values, it is possible to bind a name to another name using def
.
1(def hey (fn [] (inspect "Hey")))
2=> <#def "hey">
3
4(def sayhey hey)
5=> <#def "sayhey">
6
7(sayhey)
8Hey
9=> Hey
10
11(def no_i_made_this "A very fancy string")
12=> <#def "no_i_made_this">
13
14(inspect no_i_made_this)
15=> "A very fancy string"
Inspecting
Mylalang lets you inspect a value and pass it through to calling functions.
Inspecting simple results
The output in a REPL shows a printed result and then shows the value of the executed statement.
Using inspect inside a function
1(def suspiciousfunction (fn [a]
2 (let [r (* a a)]
3 (inspect (+ "Result is strange... " r))
4 r)))
The do
Function
The do
function lets you execute multiple functions without the need of a let
function. It’s used when you want to execute side effects but have no need for let
local bindings.
1(def saygarbage (fn [a]
2 (do (inspect "Hello world")
3 (inspect "foo bar")
4 a)))
5
6=> <#def "saygarbage">
7(saygarbage "123")
8Hello world
9foo bar
10=> 123
Conditionals
There are two conditional statements:
if
, which when given a true statement returns (or executes) the left value. When false, the right value.unless
, which when given a false statement returns (or executes) the left value. When true, the right value.
Files
Reading Files
The readfile
function will read the contents of a file. The value returned is a string.
Importing
Importing named valued (bound through def
) in another file is possible using the import
function, which binds the imported functions to named in the local scope.
1# math.my
2(def double (fn [a] (* a 2)))
3
4# main.my
5(import "math.my")
6
7=> <#def "double">
8
9(double 99)
10
11=> 198
Recursion and Loops
Recursion is used for looping in Mylang.
Example of a function using recursion create a loop with an exit condition
1(def start 0)
2
3(def inc (fn [i]
4 (+ i 1)))
5
6(def loop (fn [i]
7 (if (<= i 10)
8 (loop (inc (inspect i)))
9 (inspect "finished"))))
10
11(loop start)
The cons
Function
The cons
operator is function which constructs a list from another value. An example of its usage is to append a value to a list.
The list
Function
The ()
syntax is literally a list which executes a context. The context being the first item in the list.
For this reason, it is difficult for Mylalang to understand if you want to construct a list or execute a some context, such as a bound function.
The list
function solves this issue by creating a list for you.
Lists can be operated on like any other primitive type.
The real power of lists comes out when used with recursion where they are applied as a list of arguments. See the apply
function.
:
Operator
The short form of cons is the :
operator. It is used the same as above.
The &
Operator and the apply
Function
The apply
function is a powerful function that allows us to expand a list as arguments for a function. It accepts the function as the first argument and a list of values-as-parameters as the second argument.
An example using the +
operator.
In most cases, we want to operate on value individually. The &
(capture) operator allows us to capture all remaining unnamed arguments as a list.
1(def cap_example (fn [first & rest]
2 (do (inspect "First arg")
3 (inspect first)
4 (inspect "The rest of the args")
5 (inspect rest))))
6
7=> <#def "cap_example">
8
9(cap_example 1 2 3 4 5) ; Calling the function
10First arg ; inspecting first argument
111
12The rest of the args ; inspecting the remaining arguments
13([2, 3, 4, 5])
14
15=> ([2, 3, 4, 5]) ; Final return result of the function
With recursion, it becomes even more powerful. The implementation of max
shows how recursion, cons, apply, and & can be used together.
Evaluating Code
The eval
function accepts a string and executes it as code. The return result is always nil
but any functions or values bound to a name will be bound to the local scope.
A simple example using inspect
:
Complex Example
A more complex example when you would want to eval code, is when the code is in another file or serialized into a string. The import
function is implemented in Mylalang itself using only eval
and readfile
.
Native functions
Most functions covered are implemented Mylalang itself. While there is no standard library, there are additional functions which come with Mylalang:
min
Returns the smallest value from a list.
max
Returns the largest value from a list.
double
Doubles a value! Originally implemented to provide a function to test with.
map
The map
function maps a function over a list. Accepts a function as its first argument and a list for the remaining arguments.
reduce
Reduces a list of values to the right. Accepts a function in it’s first position, accumulator in it’s second. The rest of the arguments are captured.
More functions exist in the language which can be found by checking out the native implementation files.