Skip to content

Features

Pannous edited this page Nov 19, 2022 · 22 revisions

Features of the Angle programming language

Adaptations / Adoptions

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.

Speakablity

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:

Opinionated Standards

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

Extended unicode

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:

operators

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 ...

code, data and objects

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

Automatic imports

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.

Automatic number type

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.

As keyword for 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

of keyword

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.

it keyword in closures

This little invention makes closures so much more beautiful:
map contracts to name of it

its/their keyword

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

multiple dispatch

optional typing

copy on write

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

self hosting

(secondary goal to not bias language design)

online packages

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.

Home

Philosophy

data & code blocks

features

inventions

evaluation

keywords

iteration

tasks

examples

todo : bad ideas and open questions

⚠️ specification and progress are out of sync

Clone this wiki locally