darcsden :: alex -> the -> blob

the programming language

root / README.md

This document is a work-in-progress, outlining what I'd like this language
to be. It'll be updated and expanded over time.

Parts of it may be out of date. There will probably be inconsistencies.


Key Words
=========
Multiple dispatch, dynamic, strongly typed, functional, immutable,
message-passing concurrency, prototyping


Influences
==========
Scheme, Slate, Self, Io, Haskell, Ruby, Erlang


Syntax
======

Literals
--------

---------- -----------------
`Integer`                `1`
`List`              `[1, 2]`
`Char`                 `'a'`
`String`             `"foo"`
`Double`               `1.0`
`Particle`   `@foo` (+ more)
`Block`    `{ a b | a + b }`
---------- -----------------

Messages
--------

    1 negate
        => -1

    1 negate negate
        => 1

    1 + 1
        => 2

    'a' as: Integer
        => 97 (ASCII code)

Note that operators *must* be surrounded by whitespace. This lets us use more
symbols in identifiers, e.g. `up-to`.


Multiple keywords:

    0 up-to: 10 do: { n | print n }

This is dispatched as one message, `up-to:do:`, with the values `0`, `10`,
and `{ n | print n }`

Chained keywords:

    "foo/bar" (split-on: '/') (each: print)
        => foo
           bar

Currying:

    [1, 2, 3] map: (+ 2)
    ['a', 'b', 'c'] map: (as: Integer)

Definitions
-----------
Simple assignment:

    a = 1

This actually defines a method on the current scope which responds to the
symbol `a` being sent by returning `1`.

Pattern matching:

    0 fact = 1
    (n: Integer) fact = n * (n - 1) fact

These are two distinct methods that match on different messages being sent.
While the first definiton matches only on the integer `0`, the second matches
on any `Integer`. The most precise match gets chosen for the dispatch.

Note that the second definition could also have been simply `n fact = n * (n -
1) fact`, but explicitly targeting `Integer` is better form.

Also note that, were `Integer`s and `Int`s to have the same syntax, the
first definiton would be ambiguous: is it on an `Integer` or an `Int`? Thus,
the literal for `Int`s was born: `1i`, mirroring the distinction between
`Float` and `Double` literals (`2.0f` vs. `2.0`).

Something more fancy:

    (n: Integer) up-to: (end: Integer) do: action =
        if: (n == end)
            then: []
            else: (action n . ((n + 1) up-to: end do: action))

Data
----
New primitive forms of data can be defined with `data`.

For example:

    data: Bool delegates: { True; False }

This defines the following constructor hierarchy:

        Bool
       /    \
    True    False

Note that Bool is a valid constructor for itself.

The following is legal:

    0 as: Bool = False
    1 as: Bool = True

Constructors can have additional slots:

    data: Result delegates: { Ok value; Error }

    foo = Ok 1
    foo value
        => 1

These can also be pattern matched:

    (Ok (value = Int)) bar = "this OK is an int! " .. v as: String
    (Ok (value = String)) bar = "this OK is a string! " .. v

Note that in pattern matches, the order and existence of the slots being
matched on doesn't matter.


Messages
========
The language uses message-sending to objects, using multiple dispatch.

Defining "methods" on objects is really just defining patterns for objects to
respond to.

For example, defining this nonsensical method which takes mutiple keywords:

    (n: Integer) add: (d: Double) mul: (f: Float) =
        (n + d) * f

Really defines a method that responds to an `add:mul:` message sent along with
3 values (any `Integer`, any `Double`, and any `Float`).

Internally, the "matching" is done simply using pattern matching - the same
kind of pattern matching nested inside of the message to match on `n`, `d`, and
`f`.

This means that you can match on actual values being sent as messages to the
object, as well:

    Integer "foo" = "Integer responding to foo!"

    1 "foo"
        => "Integer responding to foo!"

Combined with pattern matching, this can yield something akin to defining
methods on individual integers:

    1 foo = "fooing on one"
    2 foo = "fooing on two"
    (n: Integer) foo = "fooing on any old number"

Or, for that matter, any old message:

    Integer _ = "unknown message" print

This will respond to any value being sent to any `Integer` by printing
`"unknown message"`. It will not override other messages as matches are sorted
by precision.

Scoping =======
The current scope is just a prototype, delegating to one higher up on the
chain, i.e. lexical scoping.

The toplevel target can be replaced. Defining variables is really just adding
methods that the current scope responds to to yield the value:

    foo = 1
    foo
        => 1

    bar: n = n + 2
    bar: 3
        => 5

As blocks are introduced, usual lexical scoping rules apply; inner blocks can
access outer values that were defined before the block's execution, but outer
blocks cannot see values defined in the inner blocks.


Concurrency
===========
The provides message-passing concurrency very similar to Erlang's model. The
goal is to see what kind of overlap there can be with messages sent to/from
processes and messages sent to/from objects (as is already inherent in the
language).

Brainstorming:

    pid = {
        {
            "waiting for message..." print;
            receive print;
            "got message!" print
        } repeat
    } spawn
        => waiting for message...

    pid do-something
        => :do-something
           got message!
           waiting for message...

    pid do-something-with: 0
        => do-something-with: 0
           got message!
           waiting for message...

The value of using the native message-passing system will shine more with an
Actor model; with that you'll get full multiple-dispatch and pattern-matching,
just like normal message-passing.