138 lines
4.2 KiB
Markdown
138 lines
4.2 KiB
Markdown
# Design Notes for the Fine Language
|
|
|
|
This language is being designed as I go, because the main thing I'm
|
|
interested in is building something that's fun and productive for me
|
|
personally. That means, rather than being super careful, I'm just
|
|
building the thing that pleases me at any given moment.
|
|
|
|
Here are some notes. The notes are for me in the future, in case I'm
|
|
wondering why the language is one way instead of another way.
|
|
|
|
## The `new` keyword
|
|
|
|
I really like rust's "just use a type name with curly braces to
|
|
construct new values". It's really clean! Unfortunately it leads to an
|
|
ambiguity in the syntax that I don't like:
|
|
|
|
``` rust
|
|
if something { ...
|
|
```
|
|
|
|
In the code above, after I have parsed `something` and I see `{`, am I:
|
|
|
|
- Parsing an object construction expression for the type `something`?
|
|
- Parsing `something` as a boolean value reference and `{` as the
|
|
start of the block?
|
|
|
|
Naively you would expect the latter, but if I scan ahead a little more:
|
|
|
|
``` rust
|
|
if something { foo: true }.foo { }
|
|
```
|
|
|
|
Rust does not allow `struct` literals in the condition of the `if`,
|
|
which is correct, but that's more work than I want to do here. There's
|
|
just a lot of context flowing around about whether or not I can parse
|
|
a structure literal in any particular situation.
|
|
|
|
The `new` keyword is a compromise: we know that the context
|
|
immediately following the `new` keyword is always a type expression,
|
|
so we know that e.g. `<` or whatever means "generic type parameter"
|
|
and not "less than".
|
|
|
|
## Patterns and Alternate Types
|
|
|
|
Instead of `enums` or inheritance, we're using an alternate type like
|
|
in Typescript, with `or` between types. See the `alternates.fine` test
|
|
for a work-up of the types and syntax and whatnot. I think it works
|
|
pretty well.
|
|
|
|
Actually using alternate types involves pattern matching, either one
|
|
at a time, with the `is` operator, or in bulk, with the `match`
|
|
expression. `match` can check for completeness, but `if/is` cannot.
|
|
|
|
Patterns are VERY simple, and are explicitly *not* destructuring right
|
|
now. (Destructuring brings up a whole lot of complexity that I don't
|
|
want to deal with.)
|
|
|
|
Patterns are basically:
|
|
|
|
```
|
|
(identifier ":")? type_expression ("and" <expression>)?
|
|
```
|
|
|
|
The identifier at the front represents a binding of the value being
|
|
considered as if it were of the same type as the type expression; the
|
|
identifier is in scope for the optional predicate after the "and" and
|
|
so you can use it as if it were the type because, well, that part of
|
|
the pattern already matched.
|
|
|
|
As a special case, the identifier is *also* in scope for the body of
|
|
an `if` expression when an `is` expression is used as the condition.
|
|
|
|
```
|
|
if b is c:Foo {
|
|
result = result + c.a; // c should still be in scope!
|
|
}
|
|
```
|
|
|
|
`match` is the multi-value pattern matching expression, like this:
|
|
|
|
```
|
|
match b {
|
|
c:Foo -> c.a,
|
|
_ -> 0,
|
|
}
|
|
```
|
|
|
|
The special pattern `_` always evaluates to true.
|
|
|
|
Note that unlike rust we do not allow variable binding, e.g., in rust you can write:
|
|
|
|
```
|
|
match b {
|
|
d -> ...,
|
|
}
|
|
```
|
|
|
|
but in fine you need to write:
|
|
|
|
```
|
|
match b {
|
|
d:_ -> ...,
|
|
}
|
|
```
|
|
|
|
The reason is that the rust version is ambiguous: if `d` matches some
|
|
value already in scope (e.g., an `enum` arm) then the arm is matching
|
|
if b == d, but if `d` is unbound then `d` becomes a variable
|
|
declaration. This is a spooky action-at-a-distance and I don't approve
|
|
of it.
|
|
|
|
|
|
# Complete Garbage
|
|
|
|
## Lambdas/Closures/Anonymous Functions
|
|
|
|
Looking for a syntax here; I want to keep `fun` as a declaration like
|
|
`let` and not let it enter the expression space. I don't like
|
|
fat-arrow syntax because it makes expression parsing very ambiguous,
|
|
potentially requiring a lot of lookahead. (TODO: Is that true?)
|
|
|
|
Maybe a leading character like ` \x => x+1 ` or ` \(x,y) => x+y `?
|
|
|
|
## Interfaces/Traits/Whatever
|
|
|
|
These are incomplete structural types. Methods are easier to make
|
|
compatible than members, but members should also work so long as they
|
|
are strict prefixes of the thing.
|
|
|
|
What about sound casting with narrowing? That's union types baby, do
|
|
we really want those? It could be neat if we're doing otherwise
|
|
structural-compatibility.
|
|
|
|
## On Objects and Classes
|
|
|
|
Sometimes I think it should all be structural types.
|
|
|
|
Maybe later there can be anonymous types that match shapes.
|