a little friday-norming syntax bikeshedding

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

a little friday-norming syntax bikeshedding

Bob Nystrom
I noticed this didn't seem to get any responses, so I figured I'd throw some
ideas in:

> The concept of a statement is a little bit arbitrary. There are
> languages that do not have such a concept, but they're few; most
> languages have *some* concept of a form shorter-than-a-program that
> represents a single "chunk" of declaration-or-computation.

I thought Ruby, Smalltalk, Scheme, Lisp, and Io all lacked statements, so it
doesn't seem that uncommon to me. I'm not sure but maybe even the ML
languages fall into this?

Personally, I dislike statements so I'd be in favor of Rust or any other
language ditching them entirely. I'm working on a little language, magpie,
that's imperative but doesn't have statements. So far, it's been working
fine.

>  In a language with a first class unit-value like rust, the concept
becomes blurrier:
> any "just for side-effects" execution can also be considered as a
unit-value expression.

Yup. Isn't that what most ML languages do? The only tricky part is handling
expressions that do non-local flow control, like "return", "break", and
"throw". For magpie, I just defined another special type "Never" that is the
return type for those expressions. The type-checker then validates that that
type is never used in a place where it doesn't make sense. That lets you
prevent things like:

    foo(return 123); // meaningless since "foo" can never be called
    // (hence the name "Never" for the return expression's type)

That lets me do basic reachability analysis without having to add statements
to the language.

> - Blocks

Those can just become a "sequence" expression that evaluates a series of
expressions, discards intermediate results, and returns the last. Pretty
much "begin" in Scheme, if I remember right.

> - All 'movement' operations (assignment, send, receive) that have a
guaranteed side-effect.

It can be nice to be able to compose assignment expressions: if (foo =
bar()) { /* use foo */ }, but it's also a source of errors. <shrug>

Send can be a unit-valued expression, and receive seems like you'd want it
to be an expression anyway, unless its semantics are fixed to always receive
into some variable?

> - All branches (if, alt, alt type, etc.)

I like being able to do:

    let foo = if bar then 123 else 345

It reduces the number of times I need to declare a variable without an
initializer, which is always a good thing in my book. I prefer the former
over:

    let int foo
    if bar then foo = 123 else foo = 345

If you do go down this path, ad-hoc union types can be useful to allow the
branches to return different types:

    let foo = if bar then 123 else "string"

Normally, that would be a compile error. If you have unions, it could just
be that foo's type is "int | string". This is what Typed Scheme does as well
as other languages that try to do static analyis of dynamic languages.

> - All loops (while, do-while, for, for-each)

You could always just make them unit-valued expressions. If you add 'else'
clauses to loops (which could be neat!) then they could actually have a real
return type.

> - All non-local jumps (ret, put, break, cont).

I'm handling that with a "Never" type (essentially a bottom type). Seems to
work OK and lets you do some interesting things like:

    fn alwaysThrows(-> Never) // throw an exception...

    fn foo() {
        alwaysThrows();
        bar(); error: unreachable
   }

In other words, users can define their own abstractions that may also do
non-local flow and still be able to do reachability analysis on it for free.

> part of it was my own bias against
> programs that nest too deeply rather than just using more lines of text.

I agree completely. From my experience, ditching statements hasn't led to
painfully nested Lisp-style mega-expressions in my code. For the most part,
I still treat it as if many things are statement-like. I just have more
flexibility if I want it, and it makes the interpreter simpler to implement.

It also plays nicely with metaprogramming: it's easier to have things like
macros that expand to a chunk of code inserted in the middle of an AST if
you don't have to worry about distinguishing between whether a statement or
an expression is expected there.

> auto x = break + type t = int;

Reachability analysis and a bottom or never type would address that for you.
That would be a compile error since you can statically tell that the "+"
will never be evaluated so it's unreachable.

> I think it *might* be painting us into some corners syntactically; but it
is plausible.

One concern would be C-style declarations where a type name is the only
indicator that you're declaring a variable. Since Rust is using 'let', I
think it's OK there. I've found in magpie that making most expressions start
with a keyword ('let', 'if', etc.) keeps the grammar fairly comprehensible.

Anyway, hopefully that's helpful for you. Keep up the good work, I'm really
excited about the language.

Cheers,
- bob
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/rust-dev/attachments/20101201/1bd20088/attachment.html>

Reply | Threaded
Open this post in threaded view
|

a little friday-norming syntax bikeshedding

Graydon Hoare
On 10-12-01 12:04 PM, Bob Nystrom wrote:

> Anyway, hopefully that's helpful for you. Keep up the good work, I'm really
> excited about the language.

Thanks. It is helpful, yes. The approach we settled on trying looks
largely like what you've outlined here; we're mostly rearranging it into
an expression language with syntax carefully ordered to behave exactly
like statements, if you choose to write that way. Assuming most people will.

Not sure if we'll try to merge reachability and typechecking a la your
proposed Never type, but it's an interesting suggestion. Might go there.
We have a little reworking of the typestate algorithm ahead to
compensate for the change to deeper exprs.

We *are* retaining a 'stmt' AST node but it'll wind up just as the
tagged union of 'decl' and 'expr'. We're not even going to parse

    "a + type b = int;"

for example; decls only live at block-top level, not nested in exprs.
This is mostly just to make the scope rules maximally obvious. A decl is
in scope for the block it's declared in. I don't think it's much of a
limitation since block is an expr; you could do "a + { type b = int; };"
and mean the same thing; really it's just a rule about being unambiguous
about decl scopes.

(Dherman suggested that if you look carefully, Scheme makes this
distinction too; I haven't looked closely enough to be sure so I will
not claim it is so but I recall a distinction at compilation-unit or
module top-level in various lisps. I know Ocaml has it: note the
'definition' production in their AST :)

-Graydon

Reply | Threaded
Open this post in threaded view
|

a little friday-norming syntax bikeshedding

Bob Nystrom-2
On Wed, Dec 1, 2010 at 3:05 PM, Graydon Hoare <graydon at mozilla.com> wrote:

>   "a + type b = int;"
>
> for example; decls only live at block-top level, not nested in exprs. This
> is mostly just to make the scope rules maximally obvious. A decl is in scope
> for the block it's declared in. I don't think it's much of a limitation
> since block is an expr; you could do "a + { type b = int; };" and mean the
> same thing; really it's just a rule about being unambiguous about decl
> scopes.
>

Ah, yes. My mind has been mostly in a dynamic mindset lately (where
definitions are just another thing you can do imperatively) so I didn't
think about that. Type definitions are definitely special, as are a couple
of other things I can think of like package management stuff ("import",
"#include", or whatever is appropriate) where it doesn't make sense to allow
them anywhere.


> I know Ocaml has it: note the 'definition' production in their AST :)
>

Thanks, that's a good reference. I was thinking about "let" being an
expression, but didn't think about "type" or other definitions.

- bob
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/rust-dev/attachments/20101201/52c448fb/attachment.html>