23
$\begingroup$

Many C-style languages represent code blocks using curly brackets:

if (condition) {
    ...;
    ...;
}

Python, however, uses significant whitespace:

if condition:
    ...
    ...

The first can have some problems though. For example, it can be ambiguous with object literals:

(args) -> { /* Is this an object literal or a code block? */ }

Are there any alternatives to C-style or Python-style code blocks which are present in mainstream languages?

$\endgroup$
2
  • 5
    $\begingroup$ Note that the "object literal" syntactic ambiguity only exists in some languages (such as JS/TS). Others choose to make the literal syntax unambiguous: Rust Thing { a: 1, b: 2 }, C (thing){1, 2} $\endgroup$
    – zdimension
    May 16, 2023 at 17:09
  • 1
    $\begingroup$ Wikipedia has it covered. $\endgroup$ May 17, 2023 at 11:56

7 Answers 7

17
$\begingroup$

Lisp-style

(if (test-clause) (action₁) (action₂))

Here, the "brace" includes the keyword.

$\endgroup$
1
  • $\begingroup$ Wow, first time seeing this style for some reason and I am really in love with it. $\endgroup$ Nov 29, 2023 at 18:39
13
$\begingroup$

"endif"

Somewhat similar to } but without requiring an opening brace, phrases like endif, endfor or sometimes fi (which is if written backwards) are common in many languages. This is especially true in scripting languages and templating languages.

Examples include:

Bash

if [ condition ]; then
  your code
fi

Jinja

{% if condition %}
    code
{% endif %}
$\endgroup$
4
  • 1
    $\begingroup$ Also pascal has similar syntax (begin ... end) $\endgroup$
    – TheMisir
    May 16, 2023 at 17:13
  • 1
    $\begingroup$ Languages in the BASIC family do this too. $\endgroup$
    – DLosc
    May 16, 2023 at 17:14
  • 1
    $\begingroup$ It should be noted that languages using fi generally will use reversed versions of other keywords (esac to end case, etc.) Just to clarify that this is the same basic idea as endif, and distinct from the end described in DLosc's answer. That is: with this technique, the closing keyword is expected to "match" the opening one in same way; the other option is to use the same end-like keyword to close every kind of block. $\endgroup$ May 17, 2023 at 0:16
  • $\begingroup$ CMake and Verilog also come to mind. $\endgroup$
    – starball
    May 19, 2023 at 1:19
10
$\begingroup$

end

Some languages, such as MATLAB and Julia, use the keyword end at the end of every block. Here is a MATLAB example from Wikipedia:

classdef Hello
    methods
        function greet(obj)
            disp('Hello!')
        end
    end
end

I suspect, but am not sure, that this syntax evolved from the begin ... end syntax introduced by ALGOL (essentially equivalent to open and close curly braces in C). If all blocks begin with a block-opening statement (function, if, etc.), a begin keyword is not needed.

(This is similar to endif from mousetail's answer, but the languages mentioned in that answer have different closing symbols for different types of blocks.)

$\endgroup$
2
  • $\begingroup$ Lua does this too. $\endgroup$
    – Seggan
    May 16, 2023 at 18:56
  • $\begingroup$ +1. ALGOL first appeared in 1958 and FORTRAN first appeared in 1957. FORTRAN has the ENDIF and ENDDO syntax, but functions/subroutines can end with END (I don't know what FORTRAN looked like in 1957, but certainly it was like this in FORTRAN 77!). $\endgroup$ May 16, 2023 at 23:09
8
$\begingroup$

Prolog-style

Prolog has a unique approach to syntax, based around commas and terminated by dots. Variables are capitalized and lowercase names are assumed to be atoms (Lispers would call those "symbols").

length([], 0).
length([_|Xs], N) :-
  M is length(Xs),
  succ(M, N).

While this was originally designed specifically for logic programming, it's seen adoption in Erlang and its derivatives for more typical imperative use. In Erlang, commas separate the statements of a multi-line function, while semicolons separate clauses of a case or if statement (if in Erlang behaves more like Lisp's cond than, say, a Python if). Functions are terminated by a period.

max(X, Y) ->
  if
    X > Y -> X;
    true -> Y
  end.
$\endgroup$
7
$\begingroup$

Haskell uses the so-called 'layout' syntax, where during lexical analysis, the lexer collects Python-like indentations and translate them into { (start of a block), ; (block separator), and } (end of a block). Then, the parser deals with C-style syntax.

This means you can use indentations and {, ;, } in the surface syntax at the same time.

$\endgroup$
5
$\begingroup$

This isn't a widespread alternative, but one of the first compilers, FLOW-MATIC, used semicolons to separate statements within an "operation", and a full stop to terminate the operation. This more or less corresponds to how the punctuation marks are used in English.

(1)  COMPARE PRODUCT-NO (A) WITH PRODUCT-NO (B) ; IF GREATER GO TO OPERATION 10 ;
     IF EQUAL GO TO OPERATION 5 ; OTHERWISE GO TO OPERATION 2 .

And now you know where the semicolon originally came from!

BASIC does something similar, with colons separating statements within a line.

FOR I = 1 to 10 : PRINT "Hello number ";I : NEXT I

The full stop terminator is still with us in COBOL declarations:

   ENVIRONMENT DIVISION.
   INPUT-OUTPUT SECTION.
   FILE-CONTROL.
       SELECT CUSTMAST-FILE  ASSIGN TO       CUSTMAST
              ORGANIZATION  IS INDEXED
              ACCESS MODE   IS SEQUENTIAL
              RECORD KEY    IS CUSTMAST-PKEY-00001-00012
              FILE STATUS   IS CUSTMAST-STATUS.
       SELECT CUSTLTXT-FILE  ASSIGN TO       CUSTLTXT
              ORGANIZATION  IS LINE SEQUENTIAL
              ACCESS MODE   IS SEQUENTIAL
              FILE STATUS   IS CUSTLTXT-STATUS.
$\endgroup$
5
$\begingroup$

Those "blocks" are compound statements, that is, sequences of statements which themselves are an imperative unit in imperative languages and which is most closely related to Assembly instructions.
Many languages just don't have any of those.

Functional languages, for instance, most of the time have an expression-oriented syntax where "sequence of things" may not exist.
They may look like this:

parseIntOr s x =
    match parseInt s with
        | Some res -> res
        | None -> x

Those same languages, which include ones like Haskell, Idris, OCaml, Scala or F#, also avoid the statement/declaration problem by having let-bindings as expressions which nest, instead of variable declarations.

main =
    getLine >>= \s ->
        let str = parseIntOr s 42 in
        putStrLn ("Read: " ++ str)

Note how getLine above is not a function.
In fact, such "stateful operations" as I/O, may be interpreted as values of their owns, which may be abstracted by Monads allowing you to chain them (above with the >>= operator function).
It is not uncommon for languages to then also have an optional syntax that makes Monadic uses into statements, as in

main = do
    s <- getLine
    putStrLn ("Read: " ++ (parseIntOr s 42))
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .