-
-
Notifications
You must be signed in to change notification settings - Fork 4
Features
Adaptions and guiding principles are not new inventions, but cherry-picking the most beautiful, powerful and sane features of other modern programming languages, especially Julia, Swift, Crystal and Kotlin but definitely not Java.
Angle most important guiding principles are Speakablity and mathematical soundness. Kotlin and ruby are cute languages, but fail at basic mathematical principles such as logical operator precedence/associativity. While pronounciability of the code and mathematical brevity seem to be conflicting interests, beauty appears where both principles can be united:
Angle has an excessive alias mechanisms, allowing multiple variants to be normed into a standard form.
The compiler informs developers about the preferred way to do things, picking up newcomers who expect puts
to output a string even if the standard form is
called print
. A linter might automatically transform between the angle standard form and the user preferred form (before and after git interactions).
Exampes:
- tabs vs spaces
- snake_case, kebab_case-case, CamelCase, context case
- use, using, import, include, require
- print, puts, writeln, println, log
Julia made the beginning with allowing \alpha in the console (repl), now this approach shall become universally available in language identifiers and template strings:
\alpha = `4\beta+1`
α=="4β+1" #true
\beta = \{aleph}a
β = אa
The map of unicode names to code points is extensive (adding custom names available) but can be omitted in compiled backends: It is mostly sufficient to handle those in the fronend.
The pure ascii representations such as 4\{beta}+1
shall be called Unicode-Entity-Format UEF. Our strings contain a bit to denote this data type.
For (variable) identifiers and operators we even go one step further:
Special unicode/ascii signs are universally equivalent in almost every respect:
1 + 2 == 1 add 2
in the earliest layers of the compiler, and indistinguishable by default (unless one really needs to
know). Rendering these words as symbols or the other way around may even become a linter feature.
Equivalence such as 1 + 2 == 1 add 2
requires a sound operator precedence system:
Like in swift and julia, most unicode symbols can be used as operators or functions. Angle goes one step further and lets you define prefix, infix and suffix operators with precedence on the spot. Now we can define within the language, without bloating the parts o and compile:
prefix operator ++ of a number reference n := increase n by one
operator ++ has precedence above operator *
This is very important and central to the language as making operator precedence a first class citizen makes much of the language seem less ad hoc, but instead like lisp complex syntax arises from very simple first principles.
One experimental feature (which may rose eyebrows and needs further investigation) is the automatic alignment of arguments:
add 1 2
1 add 2
1 2 add
Can all be resolved to add(1,2)? The big question is: under which circumstances and with which rules can we allow such
an agnostic application of operators without creating a mess? One simple case is If there is only one operator involved.
Especially the last case 1 2 add
can cause a heavy burden on the reader, as can be experienced in WAT where stack
notation and functional notation can be freely mixed.
The operator
keyword takes any symbol and treats it as quoted, similar to the function
and import
keyword. Should
we turn this into a general language feature?
function blah of symbol =... ?
macro name literal ...
These three are almost the same: person ={ name: James, born: now } Can be treated as code and evaluated to person={ name: James, born: date:2020-10-22 }
when does code get executed or data get evaluated to final objects?
This warrants a chapter of its own: see evaluation
Like Swift all code in files in the same folder is accessible without specific require
include
import
… keywords.
To include high level bundles like Graphics, or to add external / online dependencies see the use keyword.
The internal number types are mostly isolated from the user facing number type, which handles automatic casting, up and downgrading between all different
kinds of numbers: integers, reals, rational and complex numbers. Developers can unknowingly accept an int8 and return a bignum pointer if their calculation
grows out of hand. Unless they really want all they need is number
as parameter or variable type. Or no type at all if they rely on automatic typing.
Following the above equivalence of certain symbols with keywords, 'as' is the equivalent of '::' in julia.
x as number = 9
number x = 9
x is number 9
x is number
x is a number
Because of optional typing and type inference this is mostly superfluous, but can speed up compile time and stabilize APIs.
Similar to how the '+' operator is just a synonym for the overloadable add function, if one says 9 as string
it calls a convert
function if available. In this case convert number to string: return atoi(number)
.
As is a bit of a special case because it also denotes return types and arguments:
to render text as pdf:
return createPdf(text)
Again using types as parameter names
Thanks to return polymorphism, the concepts of arguments, functions and their return type become unified in a general matching framework:
to render text as docx:
return createDocx(text)
render "hello" as pdf
type of result == pdf
docx example = render "hello MS"
render "now what" # error or interactive compiler question:
# 1. render text as docx or
# 2. render text as pdf
name of person = person.name
person.name == name of person
What else needs to be said?
By the way this is a beautiful example where readability, pronounceability and brevity can all be achieved at once.
This little invention makes closures so much more beautiful:
map contracts to name of it
The following are equivalent:
map(contacts, {c in c.name})
# old swift style
map contacts {.name}
map contacts to name of it
map contacts to its name
for large structures ideally "diff modification" This Megabyte object a' is the same as a except for property x which is y instead of z. Difficult to implement and mention, but could bring performance boost in version 3.0
(secondary goal to not bias language design)
use units
looks up package units in default packages, local packages and online resources like
http://github.com/angle/units and other sources. Downloading, compilation, installation and caching will happen in the background and usually should not need user intervention.
The mapping of package names to versions and sources can of course be configured in package files.