aboutsummaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-03-02 21:48:30 +0100
committerdec05eba <dec05eba@protonmail.com>2020-07-25 14:36:46 +0200
commitaf74ddb119e3c5167a10bf5468af3960c8db42c0 (patch)
tree678034785d3127d2bdb41c5e27f8a3d58fa99340 /doc
parent5e240bdab90c45f935e7d2b33181de13295e7e6b (diff)
Use 'fn' to define closure to make parsing/reading the language easier
It caused issues when you have parentheses to surround math expression, for example: ((func() + 34) * 54) is easier to parse if closure has to begin with 'fn'. Also removed requirement for semicolons. Semicolons can't even be used optionally yet.
Diffstat (limited to 'doc')
-rw-r--r--doc/DESIGN.md192
1 files changed, 97 insertions, 95 deletions
diff --git a/doc/DESIGN.md b/doc/DESIGN.md
index b8693f1..e3d7bf1 100644
--- a/doc/DESIGN.md
+++ b/doc/DESIGN.md
@@ -4,62 +4,88 @@ All functions are closures. Assigning a closure to a variable is how you make re
## Hello world
```
-const main = () {
- stderr.writeln("hello, world!");
+const main = fn {
+ stderr.writeln("hello, world!")
}
```
## Conditions
```
-const main = () {
- var value = 23 + 50;
+const main = fn {
+ var value = 23 + 50
if value < 23
- stderr.writeln("less!");
+ stderr.writeln("less!")
else
- stderr.writeln("more!");
+ stderr.writeln("more!")
while value > 0 {
- stderr.writeln("value: {}", value);
- value -= 1;
+ stderr.writeln("value: {}", value)
+ value -= 1
}
}
```
+## Closure
+Parentheses after `fn` is only required when the closure has parameters or returns data
+```
+const apply = fn(func: () bool) {
+ const result = func()
+}
+
+const main = fn {
+ // Return type is automatically deduced. If function returns multiple different types at different points,
+ // then you get an error and are required to specify the return type
+ apply(fn {
+ return true
+ })
+
+ apply(fn() bool {
+ return true
+ })
+
+ // Or store in a variable and use it
+ const func = fn {
+ return true
+ }
+ apply(func)
+}
+```
+
## Data types
```
-const main = () !void {
- var v1: i32 = 50;
- var v2: u32 = 50;
+const main = fn() !void {
+ var v1: i32 = 50
+ var v2: u32 = 50
- v1 = v2; // error, v2 can't be implicitly cast to v1 because i32 can't represent the same values as u32
- v1 = @cast(i32, v2); // ok, explicitly cast u32 to i32
+ v1 = v2 // error, v2 can't be implicitly cast to v1 because i32 can't represent the same values as u32
+ v1 = @cast(i32, v2) // ok, explicitly cast u32 to i32
- var str1 = "hello";
- var str2 = "world";
- var str3 = try str1 + " " + str2;
- var str4 = try str1 + 20; // error, can't add number to string. Preferable use str.fmt or explicitly cast to string
- var str5 = try str1 + str(20); // ok, number explicitly cast to string
+ var str1 = "hello"
+ var str2 = "world"
+ var str3 = try str1 + " " + str2
+ var str4 = try str1 + 20 // error, can't add number to string. Preferable use str.fmt or explicitly cast to string
+ var str5 = try str1 + str(20) // ok, number explicitly cast to string
- const str6 = "hello";
- const str7 = "world";
- const str8 = str6 + " " + str7; // ok, all variable involved are const. They can be combined at compile-time
+ const str6 = "hello"
+ const str7 = "world"
+ const str8 = str6 + " " + str7 // ok, all variable involved are const. They can be combined at compile-time
- stderr.writeln("{}, {} | {}", str1, str2, str3); // prints hello world | hello world
+ stderr.writeln("{}, {} | {}", str1, str2, str3) // prints hello world | hello world
}
```
## Dynamic allocation (array)
```
-const ArrayList = @import("std.array.ArrayList");
+const ArrayList = @import("std.array.ArrayList")
-const main = () !void {
- var list = ArrayList(i32);
- try list.add(23);
- try list.add(50);
- var value = list.get(40);
+const main = fn() !void {
+ var list = ArrayList(i32)
+ try list.add(23)
+ try list.add(50)
+ var value = list.get(40)
for val in list {
- stdout.writeln("value: {}", val);
+ stdout.writeln("value: {}", val)
}
}
```
@@ -72,11 +98,11 @@ struct User {
level = 1 // default value is 1 and type is i32
}
-const levelUp = (self: &User) {
- self.level += 1;
+const levelUp = fn(self: &User) {
+ self.level += 1
}
-const main = () {
+const main = fn {
const user1 = User {
name: "John",
age: 24
@@ -88,10 +114,10 @@ const main = () {
level: 100
}
- levelUp(user2);
+ levelUp(user2)
// syntax sugar for calling a function with the first argument as
// the variable before the dot (same thing as levelUp(user2))
- user2.levelUp();
+ user2.levelUp()
}
```
@@ -104,7 +130,7 @@ struct User {
level: i32
}
-const createUser = (name: str, age: i32, level: i32 = 1) User {
+const createUser = fn(name: str, age: i32, level: i32 = 1) User {
return User {
name: name,
age: age,
@@ -112,45 +138,21 @@ const createUser = (name: str, age: i32, level: i32 = 1) User {
}
}
-const main = {
- createUser(name: "John", level: 30, age: 30);
- createUser(age: 40, name: "Titor");
-}
-```
-
-## Closure
-```
-const apply = (func: () bool) {
- const result = func();
-}
-
-const main = () {
- // Return type is automatically deduces. If function returns multiple different types at different points, then you get an error and are required to specify the return type
- apply((){
- return true;
- });
-
- apply(() bool {
- return true;
- });
-
- // Or store in a variable and use it
- const func = () {
- return true;
- }
- apply(func);
+const main = fn {
+ createUser(name: "John", level: 30, age: 30)
+ createUser(age: 40, name: "Titor")
}
```
## Generic programming
```
-const add = (comptime T: type, a: T, b: T) !T {
- return try a + b;
+const add = fn(comptime T: type, a: T, b: T) !T {
+ return try a + b
}
-const main = () {
- var numberValue = add(20, 40);
- var stringValue = add("hello", "world");
+const main = fn {
+ var numberValue = add(20, 40)
+ var stringValue = add("hello", "world")
}
```
@@ -161,59 +163,59 @@ Rust doesn't handle this but Amalgam does it using #reallocatable(instance).
Reallocatable should be ignored if the reference that taken from the reallocatable memory
doesn't change location after realloc, which would be the case for pointers.
```
-const ArrayList = @import("std.array.ArrayList");
+const ArrayList = @import("std.array.ArrayList")
struct User {
name: str,
level: i32
}
-const addUserToList = (list: &ArrayList(User), user: User) {
+const addUserToList = fn(list: &ArrayList(User), user: User) {
// this is not actually needed for ArrayList because ArrayList uses #reallocatable internally for list.add and list.remove
- @reallocatable list.add(move user);
+ @reallocatable list.add(move user)
}
-const main = () {
- var users = ArrayList(User);
+const main = fn {
+ var users = ArrayList(User)
users.add(User {
name: "John",
level: 34
- });
+ })
const user1 = User {
name: "David",
level: 55
}
// error, addUserToList expects user1 to be moved or copied
- // addUserToList(users, user1);
+ // addUserToList(users, user1)
- // addUserToList(users, clone user1); // ok, user1 has been copied to function scope
- addUserToList(users, move user1); // ok, user1 has been moved to function scope
+ // addUserToList(users, clone user1) // ok, user1 has been copied to function scope
+ addUserToList(users, move user1) // ok, user1 has been moved to function scope
- next(move users);
+ next(move users)
}
-const getUserAtIndex = (list: &ArrayList(User), index: usize) User {
- return list.get(index);
+const getUserAtIndex = fn(list: &ArrayList(User), index: usize) User {
+ return list.get(index)
}
-const next = (users: ArrayList(User)) {
- const user = getUserAtIndex(users, 0);
+const next = fn(users: ArrayList(User)) {
+ const user = getUserAtIndex(users, 0)
// Reallocatable example:
addUserToList(users, User {
name: "John",
level: 34
- });
+ })
// error, "user" can't be safely used because addUserToList on line XXX can reallocate "users" which "user" belongs to
- stdout.writeln("user name: {}", user.name);
+ stdout.writeln("user name: {}", user.name)
}
```
## Table (inspired by lua)
```
-const main = () {
+const main = fn {
const values = {
"name": "John",
"age": 42,
@@ -223,29 +225,29 @@ const main = () {
]
}
- printMap(values); // stdout.writeln("{}", values) can also be used directly as it supports tables
+ printMap(values) // stdout.writeln("{}", values) can also be used directly as it supports tables
}
-const printTable = (value: TableValue) {
+const printTable = fn(value: TableValue) {
switch @type(value) {
array => {
// value type is automatically cast to array here, same with other cases in the switch
for index, val in value {
- stdout.write("[{}] = ", index);
- printTable(val);
- stdout.writeln(",");
+ stdout.write("[{}] = ", index)
+ printTable(val)
+ stdout.writeln(",")
}
}
map => {
- stdout.writeln("{");
+ stdout.writeln("{")
for key, val in value {
- stdout.write("'{}': ", key);
- printTable(val);
- stdout.writeln(",");
+ stdout.write("'{}': ", key)
+ printTable(val)
+ stdout.writeln(",")
}
- stdout.writeln("}");
+ stdout.writeln("}")
}
- else => stdout.write(value);
+ else => stdout.write(value)
}
}
``` \ No newline at end of file