| Crates.io | mers |
| lib.rs | mers |
| version | 0.9.23 |
| created_at | 2024-01-11 12:06:26.615493+00 |
| updated_at | 2025-07-15 12:56:39.454666+00 |
| description | dynamically typed but type-checked programming language |
| homepage | |
| repository | https://github.com/Dummi26/mers |
| max_upload_size | |
| id | 1096247 |
| size | 3,327,747 |
Mers is a simple, safe programming language.
cargo install mers
"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!.
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.
say_hello := () -> "Hello, World!".println
().say_hello
We create a function using the -> syntax, then assign it
to the say_hello variable.
We then call the function with the () argument.
Mers is type-checked, which guarantees
that a valid mers program will not crash
unless exit or panic is called.
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:
() if there is no else)Mers can represent sum- and product-types:
(A, B) or { a: A, b: B }A/BAn 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 }
mers only has a few different expressions:
4, -1.5, "hello"(a, b, c), { a: 1, b: 2 }var :=var (get the value) or &var (get a reference to the value)ref = (usually used as &var =){ a, b, c }arg -> expressionarg.func or a.func(b, c), which becomes (a, b, c).funcobj:func or obj:func(b, c), as above - obj must contain a function funcif condition expression and if condition expression_1 else expression_2loop expression[Int] 5[[Number] Int/Float] or [[TypeOfX] := x], which can also be used as a type check: [[_] := expression] checks that the expression is type-correctx.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
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.
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