everything is an expression
semicolons act as low-precedence operators which evaluate and discard their left side, then evaluate the right side and return that, so something; something_else; another_thing is the same as (something; something_else); another_thing
function keyword: fn
usual operators for numbers
& and | are for booleans, while && and || are for bitwise operations, as opposed to practically every other language
number types: similar to Rust's
'nothing' type? for functions that don't return a value
never type for stuff that never returns; only useful if it's static typing though
lambdas: || something, |a, b| a + b or |a, b| { ... }
other form: _.some_method()/_ * 3/do_something_with(_) - readability might be affected in the latter case though
array.map(_ * 3) /* => */ array.map(|x| x * 3)
array.map(_.sqrt()) /* => */ array.map(|x| x.sqrt())(probably bad) idea : .{} blocks to add to the scope any properties or methods of the thing on which you use it
screen.{
width += 640;
height += 480;
width * height
}
// same as
{
screen.width += 640;
screen.width += 480;
screen.width * screen.height
}match, both postfix and not postfix
match value {
something => a,
something_else => b,
_ => c,
}
// or
value.match {
something => a,
something_else => b,
_ => c,
}with/if and the like : no parentheses around conditions
generics : allow code inside functions to treat T and friends as parameters, but that are types? e.g.
fn eee<T: Add>(arg: T) -> T {
match T {
u8 => arg + 2,
_ => arg + 1,
}
}possibly - duck keyword for duck typing
fn do_stuff(something: duck SomeTrait) {
// `something` is guaranteed to have all of SomeTrait's methods but not forcedly by implementing SomeTrait
}other possible way:
fn do_stuff<T: duck SomeTrait>(something: T) {
// more or less the same as above
}this could be an interesting feature, but it might result in code simply not using traits when they are available, instead relying on the presence of duck - and what should happen if T implements a method of SomeTrait twice (in different traits' implementations)?
modules - for example use std.fs.read_file; or, without useing it, .std.fs.read_file(...)
general ideas (what I want this to be)
- relatively readable
- nice to write
- neither too verbose nor minimalist - I think that removing everything not absolutely necessary to the syntax can make it less readable; compare
fn do_stuff(a: u8, b: i32, c: f64) { ... }andfn do_stuff a u8 b i32 c f64 { ... }(this one is a bit extreme of an example) - expressions expressions expressions EVERYTHING IS AN EXPRESSION
- not quite sure about going full Rust mode with a borrow checker, but if I do so, I want to have a more concise syntax than the Rc<RefCell<...>> stuff for multiple mutable references
- some way of knowing easily for the programmer if data is stored on the stack or the heap
- this might not be a good idea, but allowing generic arguments to be anything as long as it's known at compile-time
type syntax
type annotation format: : Type (e.g. let x: f64 = 0.0)
Preferred case (for types) : PascalCase, except for built-in types (for example u8)
Generics: SomeType<T1, T2> -- considering Rust-like turbofish syntax (SomeType::<T1, T2>) for use in expressions, to avoid confusion
Arrays: [Type] (; L for length?)
Tuples: (T1, T2,) (trailing commas are fine; namely for the singleton, where (T1), in an expression context, would evaluate to T1, while (T1,) is a 1-element tuple) -- tuple indexing via .n (as in Rust), would, as it is known at compile-time, yield a TN; however, dynamic indexing [n] would yield a T1 | T2 | T3 | ...
Anonymous enums : T1 | T2 | ... | TN