/fu-lang

Primary LanguagePython

Fu

A Programming Language
Aware Alert Kind Simple Opaque

The Tao gave birth to machine language. Machine language gave birth to the assembler.
The assembler gave birth to the compiler. Now there are ten thousand languages.
Each language has its purpose, however humble. Each language expresses the Yin and Yang of software. Each language has its place within the Tao.

— The Master Programmer, The Tao of Programming by Geoffrey James (1987)

example: namespace = {
    hello: void() = {
        print("hello, word\n");
    };
};

[Entrypoint]
main: void() = {
    example.hello();
};

Tenets

Orthogonality

Patterns used in one place should be reused in others:

  • Declarations will always be of form name: type-sig, read as: "name is-a type-sig". 1
  • Types can be suffixed with obvious modifiers, like int[] (int array), void(int) (void when-called-with an int).
  • Assignment will always follow the pattern of name = value.

Intentionality

Code should always clearly convey intent:

  • Declarations and type signatures are always readable and unambiguous.
  • Statements should always be readable from left to right when transcribed to spoken words, so ordinals: str[](int[]) is read as "ordinals is-a string array when-called-with an int array".
  • Function types are return-type(param-name: param-type[, ...]), read as: "return-type, when-called-with: param-name is-a param-type". 2

Opt-Out Async3

Async/await patterned programming enables performant, block-free code:

  • The entire program will be run in an async executor, unless you opt-out.4
  • Async state is stored on the stack.

Concurrent Safety Aware3

Static analysis will be aware (to the extent it can be) of concurrent safety:

  • Primitive containers and other data types provided by the standard library will be annotated with static type metadata exposing whether they are safe to use in concurrent programming.

Copy-Free First

Data passing will prefer references with explicit mutability over unnecessary copying.

C-String Free

C-style strings (null-terminated strings) are not supported. Instead:

  • The language will have first-class support for views/ranges/slicing.
  • String types will be lightweight views over byte buffers.
    • Variable width Unicode support comes naturally with the use of performant iterators5.

Examples

foo: bar (foo is-a bar)

Variables
foo: str;
bar: int = 0;
Read as: foo is-a str.

Namespaces67

example.impl: namespace = {
};
Read as: example.impl is-a namespace.
Functions
main: void(args: str[]) = {
};
Read as: main is-a void when-called-with: args is-a str-array.
Types
foo: type;
bar: type = {
    this: foo;
};
Read as:
  • foo is-a type
  • bar is-a type, this is-a foo
    (i.e., bar is-a type that inherits foo)

Footnotes

  1. This pattern holds even when defining types, namespaces, and functions.

  2. Currently, all callable types' parameters must have names.

    For example, you can say foo: void(a: int), but not foo: void(int).

    In the future3, names will still be required when assigning a function, but not when declaring one without defining it, or when specifying an inner type:

    foo: void(logger: void(str)) = { logger("message"); };
    foo: void(void(str)) = {/* How do I know how to access the logger? */};
  3. This is a future feature that is not yet supported. 2 3 4

  4. In the future3 the [Entrypoint] metadata decorator will support something like [Entrypoint(async=false)]. However, all blocking standard library functions will be async, so there will be additional work required to have a synchronous entrypoint.

  5. A Branchless UTF-8 Decoder — Chris Wellons, null program (2017).

  6. Namespaces must be initialized with a body.

  7. Namespaces with dotted names is syntactically equivalent to defining them separately:

    example.impl: namespace = {};
    // Same as:
    example: namespace = {
    impl: namespace = {};
    };