for i in 1 to(10) do print(i)
for line in open("example/File.mag") do print(line)
for description, block in _specs do
...
end
So far I like it, but consider this an experiment (like everything
else in the language!). Maybe we'll go back to "=". Comments,
criticism?
- bob
Great question!
There are basically three block styles in languages today:
1. Braces (C, C++, JS, Java, Smalltalk):
if (foo) {
bar();
}
2. Keywords (BASIC, Ruby, Pascal):
if foo then
bar()
end
3. Indentation (Python, CoffeeScript):
if foo:
bar()
Braces are the most common. I shied away from them in Magpie because
they require a certain amount of extra text in all cases. A single
line block like:
if (foo) bar()
Has an unnecessary "(" after "if". It's there just to match the
closing ")" but it doesn't add any value. The only meaningful one is
the ")" because it separates the condition from the body. Go addresses
this by getting rid of the parentheses:
if foo { bar() }
But now you've got a useless "}" at the end. What control syntax
really needs is *separators* between the clauses, and not *grouping*
characters surrounding them. With "if", you need to separate the
condition, the then branch, and the else branch. You can do that with
just: if ... then ... else. To me, that's nicely minimal.
So now, if one of the branches is a block instead of a single
expression, do we use indentation or keywords to delimit it? We could
support either:
if foo then
a
end
or
if foo then
a
In the first case, a newline where the expression is expected (right
after "then") indicates the beginning of the block, and "end" (or
"else") indicates the end. In the second case, the newline and indent
does, and the "dedent" ends it.
Indentation is appealing because it's even more minimal. You should be
indenting for readability anyway, so why not make that thing that
actually defines the blocks?
I actually had Magpie working like that for a while. The problem is
that it doesn't play nice with expression-oriented languages. Python
is statement-oriented, so it works fine, but in Magpie, blocks are
just expressions, which means they can be nested. For example:
match 2
case 1 then "one"
case 2 then "two"
case 3 then "three"
end shouldEqual("two")
Without an "end" keyword, it's hard to tell where that "shouldEqual()"
should go. Would it be like this?
match 2
case 1 then "one"
case 2 then "two"
case 3 then "three"
shouldEqual("two")
That would mean shouldEqual() is just it's own expression. I think
you'd have to do something like:
(match 2
case 1 then "one"
case 2 then "two"
case 3 then "three"
) shouldEqual("two")
Which is... strange. I really like CoffeeScript, but I think they
struggle with this a bit.
After trying both, I found using ending keywords made it a good bit
easier to compose expressions. Maybe this is just me, but I also found
it easier to mentally parse the grammar. I made fewer mistakes and
spent less time thinking "how do I write this?" then I did with
significant indentation.
Aesthetically, I also like the way ending keywords look. I find it
gives code a nice vertical symmetry:
it should("unwind past uncaught error types") with
var caught = false
do
do
throw "unwind error"
catch is Int then
fail("Should not be caught here.")
end
catch is String then
caught = true
end
caught shouldEqual(true)
end
Here it's easy for me to tell that the block started on the first line
has ended because the "end" at the bottom lines up with it. This isn't
a huge deal, but I find it pleasant on the eyes, which matters to me.
But the main reasons are minimalism (which braces lack) and
composability (which indentation lacks).
- bob
Dave
The "end" may not be that bad even in that case. I could be wrong, but
I wouldn't be surprised if non-programmers found keywords friendlier
than punctuation like braces. It's worth noting that "end" comes from
BASIC, which was intentionally designed for non-experts to use.
> OTOH, I would tend to advise users to avoid composing expression statements
> (if/match/etc) too much, as I fear the ultimate algorithm may be hard to
> decypher.
Agreed. Just because you *can* nest them deeply, doesn't mean you
*should*. When learning a language, people tend to follow the style of
the standard library and the code that came before them, so it will be
up to us to set a good example.
- bob
The "end" may not be that bad even in that case. I could be wrong, but
I wouldn't be surprised if non-programmers found keywords friendlier
than punctuation like braces. It's worth noting that "end" comes from
BASIC, which was intentionally designed for non-experts to use.
> OTOH, I would tend to advise users to avoid composing expression statementsAgreed. Just because you *can* nest them deeply, doesn't mean you
> (if/match/etc) too much, as I fear the ultimate algorithm may be hard to
> decypher.
*should*. When learning a language, people tend to follow the style of
the standard library and the code that came before them, so it will be
up to us to set a good example.
Ah, good point! How Anglo-centric of me. :)
- bob
It isn't documented yet, but there's actually a lot more in Magpie for
that than it first seems. The entire expression grammar can be
extended and you can take over the parser completely. If you want to
see an example, take a look in lib/magpie/core.mag. You'll see that
all of these are defined in Magpie code:
- Arithmetic operators (including precedence)
- if/then/else
- The "and" and "or" operators, including short-circuiting
I'm still figuring out some details on how it works, which is part of
the reason it isn't documented yet, but Magpie should let you go
farther with your own DSLs than most languages let you. At some point,
I need to write down my thoughts on metamodules too. :)
> But even with "end", it ain't that bad at all for now, the language being
> already very readable in its current state.
Great! :)
- bob