mirror of
				https://github.com/Dummi26/mers.git
				synced 2025-10-31 11:46:15 +01:00 
			
		
		
		
	improve README
This commit is contained in:
		
							parent
							
								
									6b2ae08731
								
							
						
					
					
						commit
						5d663d8d39
					
				
							
								
								
									
										67
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								README.md
									
									
									
									
									
								
							| @ -1,70 +1,3 @@ | ||||
| # mers | ||||
| 
 | ||||
| See [the mers readme](mers/README.md) for more info. | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ``` | ||||
| "Hello, World!".println | ||||
| ``` | ||||
| 
 | ||||
| > `Hello, World!` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ``` | ||||
| my_var := "Hello, Variable!" | ||||
| my_var.println | ||||
| ``` | ||||
| 
 | ||||
| > `Hello, Variable!` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ``` | ||||
| (1, 2, 3, 4).sum.println | ||||
| ``` | ||||
| 
 | ||||
| > `10` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ``` | ||||
| (1, "2", 3, 4).sum.println | ||||
| ``` | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ``` | ||||
| (1, 2, 3, 4).as_list.debug | ||||
| ``` | ||||
| 
 | ||||
| > `List<Int> :: [1, 2, 3, 4]` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ``` | ||||
| (1.0, 2.0).as_list.debug | ||||
| ``` | ||||
| 
 | ||||
| > `List<Float> :: [1, 2]` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ``` | ||||
| (1, 2, 3.5).as_list.debug | ||||
| ``` | ||||
| 
 | ||||
| > `List<Int/Float> :: [1, 2, 3.5]` | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ``` | ||||
| int_list := (1, 2, 3).as_list | ||||
| float_list := (4.5, 6.0).as_list | ||||
| int_list.chain(float_list).as_list.debug | ||||
| ``` | ||||
| 
 | ||||
| > `List<Int/Float> :: [1, 2, 3, 4.5, 6]` | ||||
|  | ||||
							
								
								
									
										391
									
								
								mers/README.md
									
									
									
									
									
								
							
							
						
						
									
										391
									
								
								mers/README.md
									
									
									
									
									
								
							| @ -1,228 +1,215 @@ | ||||
| # mers | ||||
| 
 | ||||
| Mers is a simple, safe programming language. | ||||
| 
 | ||||
| ```sh | ||||
| cargo install mers | ||||
| ``` | ||||
| 
 | ||||
| Mers is a simple, safe programming language. | ||||
| ## safety & type system | ||||
| 
 | ||||
| ## features | ||||
| Mers is type-checked, which guarantees | ||||
| that a valid mers program will not crash | ||||
| unless `exit` or `panic` is called. | ||||
| 
 | ||||
| - mers' syntax is simple and concise | ||||
| - mers is type-checked, but behaves almost like a dynamically typed language | ||||
| - it has no nulls or exceptions | ||||
| - references in mers are explicit: `&var` vs. just `var` | ||||
| - no `goto`s (or `break`s or `return`s) | ||||
| - locking (useful for multithreading, any reference can be locked) | ||||
| The type system is kept simple on purpose. | ||||
| A variable's type is decided where it is declared, | ||||
| there is no type inference. Each type of expression | ||||
| has a defined way of finding the type of values it | ||||
| produces, for example: | ||||
| 
 | ||||
| - tuples and objects produce tuple and object types | ||||
| - a block produces the same type of value as the last expression it contains | ||||
| - an if-statement produces either the type of the first expression (if the condition was true), or the type of the second expression (which is `()` if there is no `else`) | ||||
| - type hint expressions produce the type specified in square brackets | ||||
| - ... | ||||
| 
 | ||||
| Mers can represent sum- and product-types: | ||||
| 
 | ||||
| - product types are tuples or objects: `(A, B)` or `{ a: A, b: B }` | ||||
| - sum types are just two types mixed together: `A/B` | ||||
| 
 | ||||
| An example of product types: | ||||
| 
 | ||||
| ``` | ||||
| // this is an Int | ||||
| some_number := 5 | ||||
| // these are Strings | ||||
| some_string := "five" | ||||
| some_number_as_string := (some_number).concat | ||||
| // this is a { base10: String, human: String } | ||||
| some_object := { base10: some_number_as_string, human: some_string } | ||||
| // this is a (Int, { base10: String, human: String }) | ||||
| some_tuple := (some_number, some_object) | ||||
| ``` | ||||
| 
 | ||||
| An example of a sum type: | ||||
| 
 | ||||
| ``` | ||||
| some_number := 5 | ||||
| some_string := "five" | ||||
| some_number_as_string := (some_number).concat | ||||
| // this is an Int/String | ||||
| some_value := if some_string.eq(some_number_as_string) { some_number } else { some_string } | ||||
| ``` | ||||
| 
 | ||||
| ## simplicity | ||||
| 
 | ||||
| mers only has a few different expressions: | ||||
| 
 | ||||
| - literals: `4`, `-1.5`, `"hello"` | ||||
| - tuples and objects: `(a, b, c)`, `{ a: 1, b: 2 }` | ||||
| - variable declarations: `var :=` | ||||
| - variables: `var` (get the value) or `&var` (get a reference to the value) | ||||
| - reference assignments: `ref =` (usually used as `&var =`) | ||||
| - blocks: `{ a, b, c }` | ||||
| - functions: `arg -> expression` | ||||
| - function calls: `arg.func` or `a.func(b, c)`, which becomes `(a, b, c).func` | ||||
| - `if condition expression` and `if condition expression_1 else expression_2` | ||||
| - `loop expression` | ||||
| - type hints `[Int] 5` | ||||
| - type definitions `[[Number] Int/Float]` or `[[TypeOfX] := x]`, which can also be used as a type check: `[[_] := expression]` checks that the expression is type-correct | ||||
| - try: mers' switch/match: `x.try(num -> num.div(2), _ -> 0)` | ||||
| 
 | ||||
| mers treats everything as call-by-value by default: | ||||
| 
 | ||||
| ``` | ||||
| modify := list -> { | ||||
|   &list.insert(1, "new value") | ||||
|   list.debug | ||||
| } | ||||
| 
 | ||||
| list := ("a", "b").as_list | ||||
| list.modify | ||||
| list.debug | ||||
| ``` | ||||
| 
 | ||||
| When `modify` is called, it changes its copy of `list` to be `[a, new value, b]`. | ||||
| But when `modify` is done, the original `list` is still `[a, b]`. | ||||
| 
 | ||||
| If you wanted list to be changed, you would have return the new list | ||||
| 
 | ||||
| ``` | ||||
| modify := list -> { | ||||
|   &list.insert(1, "new value") | ||||
|   list.debug | ||||
| } | ||||
| 
 | ||||
| list := ("a", "b").as_list | ||||
| &list = list.modify | ||||
| list.debug | ||||
| ``` | ||||
| 
 | ||||
| or give `modify` a reference to your list | ||||
| 
 | ||||
| ``` | ||||
| modify := list -> { | ||||
|   list.insert(1, "new value") | ||||
|   list.deref.debug | ||||
| } | ||||
| 
 | ||||
| list := ("a", "b").as_list | ||||
| &list.modify | ||||
| list.debug | ||||
| ``` | ||||
| 
 | ||||
| <small>To make this slightly less inefficient, mers | ||||
| uses a copy-on-write system, so that you | ||||
| can give copies of large values to functions | ||||
| without copying the entire value. | ||||
| When a copy of a value is changed, it is (at | ||||
| least partially) copied before mers changes it.</small> | ||||
| 
 | ||||
| # examples | ||||
| 
 | ||||
| ## Hello, World! | ||||
| 
 | ||||
|  | ||||
| ``` | ||||
| "Hello, World!".println | ||||
| ``` | ||||
| 
 | ||||
| In mers, `.function` is the syntax used to call functions. | ||||
| Everything before the `.` is the function's argument. | ||||
| In this case, our argument is the string containing *Hello, World!*, | ||||
| In this case, our argument is the string containing `Hello, World!`. | ||||
| 
 | ||||
| ## Variables | ||||
| --- | ||||
| 
 | ||||
|  | ||||
| ``` | ||||
| greeting := "Hello, World!" | ||||
| greeting.println | ||||
| ``` | ||||
| 
 | ||||
| We use `name := value` to declare a variable, in this case `my_var`. | ||||
| We can then simply write `my_var` whenever we want to use its value. | ||||
| 
 | ||||
| ## If | ||||
| --- | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| An `if` is used to conditionally execute code. | ||||
| Obviously, since our condition is always `true`, our code will always run. | ||||
| 
 | ||||
| The condition in an `if` has to be a bool, otherwise... | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ## Else | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| We can add `else` directly after an `if`. This is the code that will run if the condition was `false`. | ||||
| 
 | ||||
| ## Using If-Else to produce a value | ||||
| 
 | ||||
| Depending on the languages you're used to, you may want to write something like this: | ||||
| 
 | ||||
| ```js | ||||
| var result | ||||
| if (condition) { | ||||
|   result = "Yay" | ||||
| } else { | ||||
|   result = "Nay" | ||||
| } | ||||
| ``` | ||||
| say_hello := () -> "Hello, World!".println | ||||
| ().say_hello | ||||
| ``` | ||||
| 
 | ||||
| But in mers, an `if-else` can easily produce a value: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| We can shorten this even more by writing | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ## What if the branches don't have the same type? | ||||
| 
 | ||||
| Rust also allows us to return a value through `if-else` constructs, as long as they are of the same type: | ||||
| 
 | ||||
| ```rs | ||||
| if true { | ||||
|   "Yep" | ||||
| } else { | ||||
|   "Nay" | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| But as soon as we mix two different types, it no longer compiles: | ||||
| 
 | ||||
| ```rs | ||||
| if true { | ||||
|   "Yep" | ||||
| } else { | ||||
|   5 // Error! | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| In mers, this isn't an issue: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| The variable `result` is simply assigned the type `String/Int`, so mers always knows that it has to be one of those two. | ||||
| 
 | ||||
| We can see this if we add a type annotation: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| Obviously, the `if-else` doesn't always return an `Int`, which is why we get an error. | ||||
| 
 | ||||
| ## Using If without Else to produce a value | ||||
| 
 | ||||
| If there is no `else` branch, mers obviously has to show an error: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| Or so you thought... But no, mers doesn't care. If the condition is false, it just falls back to an empty tuple `()`: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ## Sum of numbers | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ## Sum of something else? | ||||
| 
 | ||||
| If not all of the elements in our `numbers` tuple are actually numbers, this won't work. | ||||
| Instead, we'll get a type-error: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ## Loops | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| This program asks the user for a number. if they type a valid number, it prints that number. | ||||
| If they don't type a valid number, they will be asked again. | ||||
| 
 | ||||
| This works because `parse_float` returns `()/(Float)`, which happens to align with how loops in `mers` work: | ||||
| 
 | ||||
| A `loop` will execute the code. If it is `()`, it will execute it again. | ||||
| If it is `(v)`, the loop stops and returns `v`: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| With this, we can loop forever: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| We can implement a while loop: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| Or a for loop: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| The `else (())` tells mers to exit the loop and return `()` once the condition returns `false`. | ||||
| 
 | ||||
| ## Functions | ||||
| 
 | ||||
| Functions are expressed as `arg -> something`, where `arg` is the function's argument and `something` is what the function should do. | ||||
| It's usually convenient to assign the function to a variable so we can easily use it: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| Since functions are just normal values, we can pass them to other functions, and we can return them from other functions: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| Here, `do_twice` is a function which, given a function, returns a new function which executes the original function twice. | ||||
| So, `add_one.do_twice` becomes a new function which could have been written as `x -> x.add_one.add_one`. | ||||
| 
 | ||||
| Of course, this doesn't compromise type-safety at all: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| Mers tells us that we can't call `add_two` with a `String`, | ||||
| because that would call the `func` defined in `do_twice` with that `String`, and that `func` is `add_one`, | ||||
| which would then call `sum` with that `String` and an `Int`, which doesn't work. | ||||
| 
 | ||||
| The error may be a bit long, but it tells us what went wrong. | ||||
| We could make it a bit more obvious by adding some type annotations to our functions: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ## Advanced variables | ||||
| 
 | ||||
| In mers, we can declare two variables with the same name: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| As long as the second variable is in scope, we can't access the first one anymore, because they have the same name. | ||||
| This is not the same as assigning a new value to x: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| The second `x` only exists inside the scope created by the code block (`{`), so, after it ends (`}`), `x` refers to the original variable again, whose value was not changed. | ||||
| 
 | ||||
| To assign a new value to the original x, we have to write `&x =`: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ## References | ||||
| 
 | ||||
| Writing `&var` returns a reference to `var`. | ||||
| We can then assign to that reference: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ... or: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| We aren't actually assigning to `ref` here, we are assigning to the variable to which `ref` is a reference. | ||||
| This works because the left side of an `=` doesn't have to be `&var`. As long as it returns a reference, we can assign to that reference: | ||||
| 
 | ||||
| This is used, for example, by the `get_mut` function: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| Here, we pass a reference to our list (`&list`) and the index `0` to `get_mut`. | ||||
| `get_mut` then returns a `()/(&{Int/String})` - either nothing (if the index is out of bounds) | ||||
| or a reference to an element of the list, an `Int/String`. If it is a reference, we can assign a new value to it, which changes the list. | ||||
| 
 | ||||
| ## Multithreading | ||||
| 
 | ||||
| (...) | ||||
| We create a function using the `->` syntax, then assign it | ||||
| to the `say_hello` variable. | ||||
| We then call the function with the `()` argument. | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| Note: all of the pictures are screenshots of Alacritty after running `clear; mers pretty-print file main.mers && echo $'\e[1;35mOutput:\e[0m' && mers run file main.mers`. | ||||
| ``` | ||||
| if "a".eq("b") { | ||||
|   "what?".println | ||||
| } | ||||
| 
 | ||||
| response := if "a".eq("b") { | ||||
|   "what?" | ||||
| } else { | ||||
|   "ok :)" | ||||
| } | ||||
| response.println | ||||
| ``` | ||||
| 
 | ||||
| An `if` is used to conditionally execute code. | ||||
| It can also produce values. | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ``` | ||||
| val := loop { | ||||
|   "> ".print | ||||
|   ().read_line.trim.parse_float | ||||
| } | ||||
| val.println | ||||
| ``` | ||||
| 
 | ||||
| This program asks the user for a number. | ||||
| If they type a valid number, it prints that number. | ||||
| If they don't type a valid number, they will be asked again. | ||||
| This works because `parse_float` returns `()/(Float)`, which happens to align with how loops in mers work: \ | ||||
| A `loop` will execute the code. If it is `()`, it will execute it again. If it is `(v)`, the loop stops and returns `v`. | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ``` | ||||
| val := if "a".eq("a") { | ||||
|   5 | ||||
| } else { | ||||
|   "five" | ||||
| } | ||||
| val.try( | ||||
|   // if the value is a number, print half of it | ||||
|   num -> num.div(2).println | ||||
|   // for any other value, print it directly | ||||
|   other -> other.println | ||||
| ) | ||||
| ``` | ||||
| 
 | ||||
| A `try` expression uses the first type-correct branch for the given value. | ||||
| In this case, for a number, we can do `num.div(2)`, so the first branch is taken. | ||||
| For non-number values, `.div(2)` would be a type error, so the second branch has to be taken. | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| ``` | ||||
| add_one := x -> x.add(1) | ||||
| do_twice := func -> x -> x.func.func | ||||
| add_two := add_one.do_twice | ||||
| 2.add_two | ||||
| ``` | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Mark
						Mark