masak / alma

ALgoloid with MAcros -- a language with Algol-family syntax where macros take center stage
Artistic License 2.0
137 stars 15 forks source link

Implement class declarations #32

Open masak opened 9 years ago

masak commented 9 years ago

Proposed syntax:

class Q::Statement::My {
    my ident;
    my assignment = None;

    sub Str {
        if assignment == None {
            return "My" ~ children(ident);
        }
        return "My" ~ children(ident, assignment);
    }

    sub run(runtime) {
        if assignment == None {
            return;
        }
        assignment.eval(runtime);
    }
}

(Here's the Perl 6 class for comparison:)

role Q::Statement::My does Q::Statement {
    has $.ident;
    has $.assignment;
    method new($ident, $assignment = Empty) { self.bless(:$ident, :$assignment) }
    method Str { "My" ~ children($.ident, |$.assignment) }

    method run($runtime) {
        return
            unless $.assignment;
        $.assignment.eval($runtime);
    }
}

A couple of things of note:

The syntax for creating an object mirrors object literals quite a lot.

my declaration = Q::Statement::My {
    ident: Q::Identifier {
        name: "foo"
    },
    assignment: None
};

(In this case, we could've skipped passing assignment, as it's already optional.

We could have used the new keyword in the construction syntax, but I don't feel that it adds anything.

As a nice retrofit, writing Object { ... } also works, and means the same as an ordinary object literal. Unlike user-defined classes, the Object type doesn't check for required or superfluous properties.

Tuple classes

After we get types, I'd very much like for us to get a "tuple class" syntax, where you don't name your attributes, but only specify their types as a tuple:

class Q::Statement::Sub (Q::Identifier, Q::Parameters, Q::Block) { ... }

And the corresponding constructor syntax would look like a function call:

my function = Q::Statement::Sub(
    Q::Identifier("fib"),
    Q::Parameters(Q::Identifier("n")),
    Q::Block(Q::Statements(...))
); 

Internally, the attributes of a tuple class could have 0-based indices instead of names, and so you could still index them with getProp et al. This type of class would really shine if we had ADTs and case matching, the issue which see.

Meta-object protocol

If you ask type(Q::Statement::My), you get back the answer Type. (Much like Python.) Here's a rough attempt to get Type to "close the loop", meta-object-wise. Using the conjectural/future type syntax.

class Type {
    my name: Str;
    my tuplish: Int;    # since we don't have Bool
    my properties: Array<PropSpec>;
}

class PropSpec {
    my name: Str | Int;
    my type: Type;
}
masak commented 5 years ago

And C#'s upcoming records don't appear to be the same, syntactically, as Simula and what I'd like for 007's "exotic" syntax — the former is syntactic sugar for a very predictable value class (well, a "record"), while the latter contains the constructor arguments up in the class declaration header while still having an explicit body (where the constructor parameters are in scope).