No Semicolons Needed | Terts Diepraam
Terts Diepraam
BLOG
RESUME
PROJECTS
No Semicolons Needed Posted on 18 March 2026
I'm making a scripting language called Roto. Like so many programming languages before it, it has the goal of being easy to use and read. Many languages end up making semicolons to delimit or terminate statements optional to that end. I want that too! This sounds simple, but how do they implement that? How do they decide where a statement ends without an explicit terminator? To illustrate the problem, we can take an expression and format it a bit weirdly. We can start with an example in Rust: fn foo(x: u32) -> u32 { let y = 2 * x - 3; y } In Rust, that is perfectly unambiguous. Now let's do the same in Python: def foo(x): y = 2 * x - 3 return y We get an "unexpected indent" error! Since Python doesn't require semicolons, it gets confused. As it turns out, many languages have different solutions to this problem. Here's Gleam, for instance: fn foo(x) { let y = 2 * x - 3 y } That's allowed! And if we echo foo(4) we get 5 just like in Rust. So, how does Gleam determine that the expression continues on the second line? I think these differences are important, especially when you're interested in programming language design. The syntax of the language need to be intuitive and clear for the programmer, so that they can confidently explain how an expression gets parsed. Usually, those syntactic rules are obvious; in many languages, function arguments are delimited by (). The rules for newline-separated statements are often vaguer and differ from language to language. Users are often told either to defensively put semicolons in their code or not to worry about it. Both seem like (minor) failures of language design to me. How then do I find an approach for Roto that doesn't have these problems? I decided that my best course of action was to look at what 11 (!) languages are doing and how their approaches stack up. This post is that exploration. I don't really have an answer on what's best, but I hope this is still be an informative overview.
NOTE: I'm not fluent in all the languages below, in fact, some of them I've barely used. I've tried to cite sources where possible but I might still have gotten some details wrong. Let me know if you find any mistakes!
Contents This is a long post, so I'd understand if you just want to jump to your favorite language, so here are links to all the sections:
Python Go Kotlin Swift JavaScript Gleam Lua Ruby R Julia Odin
Languages Python Let's start with the language most famous for whitespace sensitivity ([citation needed]). Python assumes that one line is one statement. In its grammar, it describes what it calls logical lines. These are constructed from one or more physical lines, i.e. the lines you see in your editor. There are 2 ways that physical lines can be joined:
either explicitly with the \ token, or implicitly while the end of the line is enclosed between delimiters such as (), [], {} or triple quotes.
The reference gives these examples: # Explicit joining if 1900 < year < 2100 and 1 <= month <= 12 \ and 1 <= day <= 31 and 0 <= hour < 24 \ and 0 <= minute < 60 and 0 <= second < 60: # Looks like a valid date return 1
# Implicit joining month_names = ['Januari', 'Februari', 'Maart', # These are the 'April', 'Mei', 'Juni', # Dutch names 'Juli', 'Augustus', 'September', # for the months 'Oktober', 'November', 'December'] # of the year Having only these rules would be fairly error prone. You can see this by considering the example from the introduction again: y = 2 * x - 3 If you forget to put a backslash at the end of the first line, Python would simply treat that as two statements. Luckily, Python has a solution: it strictly enforces correct indentation. Since the - 3 is on a new line, it must have the same indentation as the line before. Now let's consider the consequences of Python's approach. It is quite principled and strict about its statement separation. It is also very unambiguous. It's easy to keep the rule of "one line, one statement" in your head while programming with the two exceptions being quite explicit. A somewhat ironic consequence for an indentation-based language, however, is that Python's rules have encouraged the community to embrace explicit delimiters. For example, the ubiquitous code formatters black and ruff both prefer parentheses over backslashes. # "Unidiomatic" y = long_function_name(1, x) \ + long_function_name(2, x) \ + long_function_name(3, x) \ + long_function_name(4, x)
# "Idiomatic" y = ( long_function_name(1, x) + long_function_name(2, x) + long_function_name(3, x) + long_function_name(4, x) ) I think Python's system is pretty good! It's simple, it's clear and the indentation rules are likely to catch any mistakes. From my time writing Python, I don't remember this getting in my way much, except for sometimes having to wrap expressions in (). I was never really surprised by this behavior. Sources:
Python Reference Black documentation
Go Go's approach is very different from Python's. Go's official book states:
Like C, Go's formal grammar uses semicolons to terminate statements, but unlike in C, those semicolons do not appear in the source. Instead, the lexer uses a simple rule to insert semicolons automatically as it scans, so the input text is mostly free of them.
The first thing that I dislike about this is that it encourages thinking of semicolons being inserted instead of statements being terminated. I find that to be a roundabout way of thinking about the problem. But alas, this is what we're dealing with. I want to highlight something in that text: the semicolons are inserted by the lexer. The reasoning behind this is this that it keeps the rule for automatic semicolon insertion are very simple. Go's lexer inserts a semicolon after the following tokens if they appear just before a newline or a }:
an identifier, a basic literal, or one of break, continue, fallthrough, return, ++, --, ) or }.
Simple enough! Let's go to our introductory example: x := 4 y := 2 * x - 3 Those lines end with numbers so the lexer inserts semicolons: x := 4; y := 2 * x; - 3; Just like Python, that seems error prone! But as we run this, Go has a nice surprise in the form of an error (not just a warning!): -3 (untyped int constant) is not used So, it has some guardrails in place to prevent mistakes. Even when I replace -3 with more complex expressions it usually errors on unused values that might occur by accident. That's good! This post gives us an example that doesn't error and where the newline changes behavior. It first requires a bit of setup: func g() int { return 1 }
func f() func(int) { return func(n int) { fmt.Println("Inner func called") } } And then these snippets have a different meaning: f() (g())f()(g()) I'm not too worried about this to be honest; it looks like this requires pretty convoluted code to be considered ambiguous. Now remember that this semicolon insertion is done entirely by the lexer. That means that semicolons sometimes get inserted at unexpected places: if x // <- semicolon inserted here { ... }
foo( x // <- semicolon inserted here ) Both of these result in parse errors. The fix is to adhere to Go's mandatory formatting style: if x { ... }
foo(x) // or foo( x, ) That's fair, even if it seems a little pedantic. I like these formatting choices, but I'd prefer if the "wrong style" was still syntactically valid and a formatter would be able to fix it. As it stands with Go, its formatter also errors on these invalid snippets. This strictness also seems to lead to confusion for newcomers every once in a while, particularly if they come from languages like Java, where braces are often put on a separate line. So, Go's approach is simple, but in my opinion not very friendly. It is saved by disallowing some unused values, but I'm not competent enough with writing Go to evaluate whether that covers all ambiguous cases. Sources:
Effective Go Blog post on codegenes.net Automatic semicolon insertion in Go Stack Overflow: Why do I get an error when putting opening braces on the next line in Go?
Kotlin As far as I can tell, Kotlin does not have simple "rules" for when a newline separates two statements, like Python and Go have. Instead, it makes newlines an explicit part of the grammar. So, for each construct where a newline is allowed, it opts into it explicitly. I'll spare you the BNF-like notation, but it seems to boil down to this:
Statements are separated by one or more newlines or ;. If a construct is unambiguously incomplete at the end of a line, it is allowed to continue on the next line. Delimited constructs (like function calls) allow newlines within them. Newlines are not allowed before (, [ or {. Binary operators seem to fall into two camps:
&&, ||, ?:, as, as?, . and .? allow newlines on both side of the operator, the rest of the operators only allow a newline after the operator.
Prefix unary operators allow a newline after themselves.
This approach of baking newline handling into the grammar gives the language designers a lot of control, but this comes at the cost of simplicity and transparency. This approach is like the opposite of Go's. It can get pretty nuanced and I can't find a clear explanation of it. My best attempt at summarizing this approach: an expression is allowed to continue on the next line if that is unambiguous in the grammar. After that theory, we try our example: val x = 4 val y = 2 * x - 3 print(y) This gives us 8 with an unused value warning. That makes sense because -, + and many other infix operators only allow newlines after the operator. However, the logical operators && and || allow newlines on both sides. // This is one expression: val y = false || true
// This is two expressions: val y = 1 + 2 Another case where the "continue if unambiguous" approach gets into trouble is when very similar operators have different rules. Kotlin has the :: and . operators to respectively access a method and a field of a class. Of these two, . allows newlines on both sides, but :: doesn't. That is because :: is also a valid start of a callable reference expression. val x = foo .bar // one expression!
val y = baz ::quux // two expressions! Since newlines are part of the grammar explicitly and therefore plainly disallowed in some places, I expected that this would give me an error because + only allows newlines after the operator in the grammar: val y = ( 1 + 2 ) But it works! I think it makes sense that they added this behavior, but I cannot find traces of this behavior in the specification. If somebody could show me where this is documented, I'd love to see it! The vibe that I get from this implementation is that Kotlin's designers try really hard to make the behavior intuitive, regardless of how many rules and exceptions they need. I guess that if people never run into problems with it, then they don't need to understand it fully either. I'm not sure I agree with this fully, but it's a somewhat reasonable position. This Stack Overflow answer echoes that sentiment:
The rule is: Don't worry about this and don't use semicolons at all [...]. The compiler will tell you when you get it wrong, guaranteed. Even if you accidentally add an extra semicolon the syntax highlighting will show you it is unnecessary with a warning of "redundant semicolon".
We could characterize this approach "don't worry, your IDE will fix it" and I guess that's fair when the company behind the language creates IDEs. Although if that is truly the consensus in the community, they've done a pretty good job! Another potential problem might be that all these complex rules might make it difficult to write custom parsers for Kotlin. I wouldn't want to be the person responsible for maintaining its tree-sitter grammar for instance. Sources:
Kotlin language specification: Syntax and Grammar Stack Overflow: What are the rules of semicolon inference?
Swift There is a somewhat obvious approach that hasn't come up yet: just parse as far as you can ignoring newlines. Swift takes that approach and it's not hard to see why: let x = 4 let y = 2 * x - 3 print(y) That prints 5 as we would expect. The downside is that this prints 5 too: let x = 4 let y = 2 * x - 3 print(y) But that's not too bad if you just have the rule that the language does not have significant whitespace. That's a rule people should be able to remember. Interestingly, Swift does have some significant whitespace to prevent mistakes. For example, it is not allowed to put multiple statements on a single line: var y = 0 let x = 4 y = 4 // error! They seem to have decided to ignore that in their grammar specification, but it is part of the compiler. With this approach, the most confusing examples I can find are around symbols that can be both unary and binary operators (our eternal nemesis). This snippet prints 8: let x = 4 let y = 2 * x -3 print(y) Why? Because Swift has some special rules for parsing operators. If an operator has whitespace on both or neither side, it's parsed as an infix operator. If it has only whitespace on the left, it's a prefix operator. And finally, if only has whitespace on the right, it's a postfix operator. This means that this also parses as two statements: let y = 2 -foo() Swift's designers seem to be aware of this problem (obviously) and therefore emit a warning on unused values, which would trigger on the example above. That should catch most erroneous cases. Another tweak they made is that the parentheses of a function call must be on the same line as the name of the function. If it isn't, then expression will end after the first line. For example, The snippet below is parsed as two lines. They check whether the ( is at the start of the line and do not continue parsing if that's the case. The same is also done for [. This is a pretty good rule! You can check the JavaScript section to see how a language can get this wrong. let y = x (1) It's also worth discussing error reporting for syntax errors. Swift cannot easily guess where a statement is supposed to end if the syntax isn't correct. let x = 4 let y = ( 2 * x print(y) This snippet is obviously wrong, because there's a missing ), but Swift instead complains about a missing , and a circular reference because we're using y before we declare it. It does that because it doesn't know that the statement was supposed to end. Now, to be fair, I found only 1 comment complaining about that, so it might not be a big deal. I haven't written enough Swift code to judge. I like this approach a lot. It seems intuitive yet simple to understand and debug. The error messages might take a bit of a hit compared to languages with explicit semicolons, but you also get no "missing semicolon" errors so that's a bit of a trade-off. Sources:
Swift Language Reference: Statements Swift Language Reference: Lexical Structure: Operators Swift Forum: What are the rules of semicolon insertion in Swift? Swift's parser
JavaScript JavaScript seems to be the language that has given Automatic Semicolon Insertion a bad reputation. Its rules are pretty complex, but luckily there's an excellent MDN article about it. There are three important cases where a semicolon is inserted:
If a token is encountered that is not allowed by the grammar that either a. is separated by at least one newline with the previous token, or b. if the token is }.
If the end of the input is reached and that is not allowed by the grammar.
If a newline is encountered in certain expressions such as after return, break or continue.
Note that this is not everything! There are many exceptions to these rules, such as that no semicolons are inserted in the for statement's head and that no semicolon is inserted in places where it creates an empty expression. All in all, this means that our example is parsed as one line: const y = 2 * x - 3 // is parsed as const y = 2 * x - 3; The complexity of these rules is kind of a problem in itself as these rules are hard to remember. The worst part of this feature that the first rule only triggers on invalid syntax. The MDN article is full of examples where this goes wrong, such as these snippets, which are both parsed as a single line: const a = 1 // <- no semicolon inserted! (1).toString()
const b = 1 // <- and also not here [1, 2, 3].forEach(console.log) If you want to code without semicolons in JS, you have to think about whether consecutive lines would be valid syntax if they were joined. Or you have to learn a whole lot of rules such as:
Never put the operand of return, break, etc. on a separate line. If a line starts with one of (, [, `, +, -, /, prefix it with a semicolon, or end the previous line with a semicolon. And more!
No wonder that many people just opt to write the semicolons in JS. Take for instance this quote from JavaScript: The Good Parts:
JavaScript has a mechanism that tries to correct faulty programs by automatically inserting semicolons. Do not depend on this. It can mask more serious errors.
In conclusion, you could write JS without semicolons, but the fact that many people recommend you always add semicolons is quite damning. I haven't seen that sentiment with the other languages in this post and it means that the feature does more harm than good. This feature is too complex doesn't even manage to be robust. Quite honestly, this feature is a disaster. Sources:
MDN: Lexical Grammar JavaScript: The Good Parts
Gleam Gleam's approach is very similar to Swift's: it also just parses the expressions until they naturally end. Swift had a few exceptions to this though, so let's investigate what Gleam does. First, we can look at our recurring example: let y = 2 * x - 3 As we might expect, that's parsed as one expression. However, we can remove one space to change that: let y = 2 * y -3 Kind of like Swift, Gleam seems to parse the -3 as a single token if it is preceded by whitespace and as a binary operator otherwise. I couldn't find a source for this so the details might be off here. Gleam's approach of parsing everything regardless of whitespace does have some strange consequences. For example, this is accepted and parses as 2 expressions: pub fn main() { 1 + 1 1 + 1 } I would personally require a newline there if I was designing Gleam, but this is technically unambiguous. Gleam's formatter will also put the expressions on separate lines and Gleam will warn you about an unused value, so you'll notice that something's off soon enough. This is parsed as one expression, i.e. a function call: pub fn main() { foo (1 + 1) } Now if you've written any Gleam, you might be yelling at your screen: "That isn't ambiguous!" And you'd be right; it can only be a function call, because Gleam uses {} for grouping expressions. So, if we use {} it's not a function call anymore: pub fn main() { foo { 1 + 1 } } In another stroke of genius ambiguity prevention, Gleam doesn't have list indexing with []. So this is also parsed as two expressions: pub fn main() { foo [ 1 + 1 ] } It's interesting that Gleam doesn't have the same guardrails that Swift has. It gets away with that by having a very unambiguous grammar. This is very impressive language design. Its rules are also pretty easy to grasp, so it looks like a pretty good implementation to me. Sources:
Gleam Language Tour
Lua Speaking of languages that just parse the thing as far as they can, Lua does that too! The book says:
A semicolon may optionally follow any statement. Usually, I use semicolons only to separate two or more statements written in the same line, but this is just a convention. Line breaks play no role in Lua's syntax[.]
This means that it basically works like Gleam! What sets it apart is that it does have indexing with [] and groups expressions with (). Here's an example that requires a semicolon to prevent it being parsed as a single statement: (function() end)(); -- semicolon is required here (function() end)() There might be even more problematic cases, but I'm not experienced enough with Lua to find them. Sources:
Programming in Lua
R We've seen before that some languages insert semicolons when reading further would be invalid. R sort of takes the opposite approach: it inserts a semicolon when the grammar allows it. Here's the official explanation from the R Language Definition:
Newlines have a function which is a combination of token separator and expression terminator. If an expression can terminate at the end of the line the parser will assume it does so, otherwise the newline is treated as whitespace.
There's one exception to this rule, which is that the else keyword can appear on a separate line. That approach is somewhat reminiscent of Python's. However, R allows expressions to continue to the next line if they are incomplete. Our recurring example would parse as two expressions because the grammar allows the expression to end after the x: y = 2 * x - 3 But with a slight modification it parses as one expression: y = 2 * x - 3 The result is that you'd almost never have to worry about the next expression being parsed as part of the former. They are only joined explicitly, for example with parentheses or trailing operators. On the downside, I would generally prefer to write the operator at the start of the next line, which we can only do if we wrap the expression in parentheses (just like with Python). It looks like a pretty good approach. I like that the newline has some semantic meaning and it doesn't feel confusing. Sources:
R Language Definition
Ruby Another famously semicolonless language is of course Ruby. It has a very similar approach to R, but — as is becoming a bit of a theme — not quite the same. Like R, it splits statements by lines, but allows the expression to continue if it's incomplete. So we can basically copy our examples for R verbatim: # 2 expressions y = 2 * x - 3
# 1 expression y = 2 * x - 3 But Ruby has a few more tricks up its sleeve. First, you can end a line with \ to explicitly continue the expression on the next line, kind of like Python. Second, it has a special rule that lines starting with a ., && or || are a continuation of the line before. It does that to allow method chaining and logical chains. File.read('test.txt') .strip("\n") .split("\t") .sort
File.empty?('test.txt') || File.size('test.txt') < 10 || File.read('test.txt').strip.empty? I find this slightly confusing, because it's strange that some operators can start the next statement but not all of them. I guess it's not too bad to remember 3 exceptions. So, it looks pretty good! Sources:
Ruby Documentation: Layout
Julia Documentation on how Julia's syntax works was a bit hard to find, so I looked at their parsing code. This means I have to guess a little bit at what the intention is. Here are some things I tried: b = 3 - 4 # -> 3
c = 3 - 4 # -> -1
d = ( 3 - 4) # -> -1 It seems to be dependent on the kind of expression whether a newline continues a statement. But in general, they seem to prefer splitting into multiple lines if that is legal. The newline is really treated as a separator in the parser. In that sense, it matches other languages with a lot of use in the scientific community, such as Python and R. If anybody knows where to find documentation on Julia's syntax, let me know! Sources:
JuliaSyntax.jl
Odin While I was working on this post, Odin's creator GingerBill released a blog post that contained an explanation of Odin's approach. What I found particularly interesting are the reasons he cites for making semicolons optional:
There were two reasons I made them optional:
To make the grammar consistent, coherent, and simpler To honestly shut up these kinds of bizarre people
It looks like he didn't care much for that feature himself. What's nice about this post is that he lays out some reasoning for Odin's approach. He describes it as a mix of Python and Go, where semicolon insertion is done by the lexer, but not within (), {} and []. Another exception he lays out is that Odin has a few exceptions to allow braces to start on the next line: a_type :: proc()
a_procedure_declaration :: proc() {
}
another_procedure_declaration :: proc() {
}
another_type :: proc() // note the extra newline separating the signature from a `{`
{ // this is just a block
} In a way, this looks like the opposite of Go, where instead of enforcing a certain coding style, they go out of their way to allow other coding styles than their own. This rule seems a sign that their grammar might be a bit too "overloaded", using very similar syntax for different concepts. But hey, they probably had good reasons to do so. Sources:
Choosing a Language Based on its Syntax
A Different Idea Here is an idea I haven't seen being used and I wonder whether it makes sense. The only language that seems to consider indentation at all is Python, but only to restrict mistakes. I would love to see a language try to implement a rule where only an indented line is considered part of the previous expression. x = 3 - 3 # two expressions!
x = 3 - 3 # one expression! This feels quite intuitive to me. I could see this being a replacement for Python's line joining with \. A problem, of course, is that now the indentation always needs to be correct and many developers (myself included) like to just have their formatter deal with the indentation. In any case, it might be an interesting lint to consider for a language with optional semicolons. Overview We made it to the end! I think the best way to summarize this document is by grouping the languages:
Split statements on newlines, with exceptions
Python Ruby R Julia Odin Kotlin
Continue statements on the next line, unless that's invalid
JavaScript Swift
Let the lexer insert semicolons
Go
Do not consider whitespace while parsing
Lua Gleam
Note: These categories are not perfect, for some languages, you could make the argument that they fit in multiple categories.
You could make some other categories as well. For example, you could call Python, Ruby, R, Julia, and Odin conservative in their parsing, they usually stop parsing at a newline. Lua, Gleam and Swift, on the other hand, are more greedy: they usually keep parsing across newlines as far as they can. Another distinction to make is how it is implemented. JavaScript, Go and Odin have at least some part of the semicolon insertion implemented in the lexer, while many other languages make it part of the parser. A final interesting category are the languages that are entirely insensitive to whitespace, such as Lua and Gleam. Even though Swift gets close to this category, it turned out to have some whitespace-sensitive rules. Conclusion This turned out to be a much more complicated topic than I expected! While there are approaches I like better than others, not all languages should use the same solution, because there might be other ways that the syntax differs that should be taken into account. Nevertheless, I guess this is the part where I have to give my opinion about all of this, so here are some guidelines I would use (which you may very well disagree with):
Prefer defining clear rules over baking it into your parser. Keep those rules as simple as possible (looking at you, JS). Use a parser that splits on newlines in most cases, instead of continuing expressions greedily onto the next line. Think about the rest of your language's syntax and what problems might arise with your chosen approach. Add tooling to help catch mistakes (such as warnings on unused values) to prevent the most ambiguous cases.
Do you agree? What do you think is best? Have I missed any important languages? Do you have cool ideas for better implementations? Let me know be responding to this post on Mastodon! Acknowledgments Thanks to Thijs Vromen, waffle and Anne Stijns for proofreading drafts of this post. Any mistakes are my own. You can send corrections to terts.diepraam@gmail.com or on Mastodon. No LLMs were used while writing this piece, neither for gathering information or for writing. |
This document, authored by Terts Diepraam, explores the diverse approaches languages take when handling whitespace and statement termination, specifically examining how languages decide where a statement ends without explicit delimiters. The core of the post centers around the challenges that arise from optional statement termination, highlighting the inconsistencies and complexities that emerge when languages differ in how they handle newlines, indentation, and semicolon insertion.
Diepraam begins by illustrating the problem with examples in Rust and Python, demonstrating how subtle differences in formatting – particularly the use or omission of backslashes in Python – can lead to parsing errors. He then moves on to examine several languages, including Gleam, Lua, Ruby, R, Julia, and Kotlin, detailing their specific rules for navigating whitespace and statement boundaries. Each section analyzes the language's approach, often focusing on the role of the lexer or parser in inserting or enforcing semicolons. For instance, Go's lexer automatically inserts semicolons based on specific rules, while Kotlin relies on a strict grammar that dictates where newlines can break statements, and Ruby and R allow for flexible continuation on subsequent lines. JavaScript is addressed with a considerable degree of critical appraisal, emphasizing the complexity and potential pitfalls of its automatic semicolon insertion mechanism. Gleam and Swift are scrutinized for their more similar approaches to parsing expressions across multiple lines. Odin's implementation receives particular attention for its deliberate departure from strict semicolon enforcement, reflecting a design philosophy focused on consistency and user choice rather than rigid adherence to a single convention.
Throughout the post, Diepraam emphasizes the importance of language design, arguing that syntax should be intuitive and clear for programmers. He points out the irony that Python's indentation-based approach has inadvertently led to a preference for explicit delimiters in other languages. He uses the examples to highlight key distinctions: the rigidness of Python’s one-statement-per-line rule versus Go’s looser, lexer-driven approach; Kotlin’s grammar-based rule, leaning heavily on the ambiguity of the parser; and Swift’s almost completely whitespace insensitive approach. The post consistently argues against languages that rely on complex, potentially obscure, rules for statement termination, advocating for simplicity and clarity. Diepraam also notes subtle variations in error reporting, highlighting how different languages handle parsing errors differently, and offering a comparative assessment of their usability and debuggability.
Ultimately, Diepraam doesn't offer definitive answers to what constitutes the "best" approach, but successfully provides a comprehensive overview and a thoughtful exploration of the trade-offs involved. The post is primarily a pedagogical exercise, aiming to illuminate the design choices behind different languages' syntax, particularly in the context of whitespace and statement termination. It encourages critical thinking about the impact of these choices on developer experience and promotes appreciation for the nuances of programming language design. The author’s focus on the complexities of automatic semicolon insertion and their impact on code readability and maintainability serve as a valuable lesson for any developer. Furthermore the document successfully emphasizes the varying design philosophies implemented throughout the languages showcased and allows the reader to recognize those approaches. |