diff options
author | dec05eba <dec05eba@protonmail.com> | 2019-03-02 21:48:30 +0100 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2020-07-25 14:36:46 +0200 |
commit | af74ddb119e3c5167a10bf5468af3960c8db42c0 (patch) | |
tree | 678034785d3127d2bdb41c5e27f8a3d58fa99340 /doc | |
parent | 5e240bdab90c45f935e7d2b33181de13295e7e6b (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.md | 192 |
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 |