<commit_before>extends ../../tmpl/base.jade

block body
    .container
        | Guides &bull; Learn Standard ML &bull; Chapter 3
        h1 Expressions and Variables
        .subheader
            a(href="https://github.com/eatonphil/ponyo/blob/master/site/static/app/templates/guides/learn-standardml/expressions-and-variables.jade") Source
    .container
        .sidebar-container
            .row
                .col-md-6
                    h3 Expressions
                    p.
                        An expression is a chunk of code that returns a value. An expression
                        can always be assigned to a variable. On the other hand, a statement
                        is a chunk of code that does not return a value. A statement cannot be
                        assigned to a variable. Most parts of Standard ML are expressions,
                        not statements. Expressions consist of one of <the following:
                    ul
                        li Literals (e.g. <code>1</code>, <code>"foo"</code>)
                        li Variable references (e.g. <code>a</code>, <code>theVariable</code>)
                        li Expressions combined by an infix operator (e.g. <code>a + b</code>, <code>1 + theVariable</code>)
                        li If blocks (e.g. <code>if a then b else c</code>)
                        li Case blocks (e.g. <code>case a of 2 => true | 3 => false</code>)
                        li Lambda functions (e.g. <code>fn a => a</code>)
                        li Function calls (e.g. <code>split "foo" "o"</code>)
                        li Let blocks (e.g. <code>let val i = 1 in i end</code>)
                        li Local blocks (e.g. <code>local val i = 1 in i end</code>)
                        li Compound expressions (e.g. <code>1; a + b</code>)
                        li Any expression in parenthesis (e.g. <code>(1)</code>, <code>(theVariable)</code>)
                    p The following are <strong>not</strong> expressions:
                        li Val declarations (e.g. the <code>val i = 1</code> part of <code>let val i = 1 in i end</code>)
                        li Named function declarations (e.g. <code>fun a b => b</code>)
                        li Type declarations (e.g. <code>type a = int</code>, <code>datatype b = Int of int | String of string</code>)
                        li Structures (declarations and references)
                        li Signatures (declarations and references)
                        li Functors (declarations and references)
                    h4 Literals
                    p.
                        The following types can be expressed as literal values:
                    ul
                        li Unit <code>()</code>
                        li Nil <code>nil</code>
                        li Integers (e.g. <code>1</code>, <code>~1</code>)
                        li Reals (e.g. <code>1.0</code>, <code>~1.0</code>)
                        li Strings (e.g. <code>"my string"</code>)
                        li Characters (e.g. <code>'f'</code>, <code>'\n'</code>)
                        li Booleans <code>true</code> and <code>false</code>
                        li Tuples (e.g. <code>(1, 2)</code>, <code>(x, 4.0, (12, 1))</code>)
                        li Lists (e.g. <code>[1, 2]</code>, <code>[(1, 2), (3, 4)]</code>)
                        li Records (e.g. <code>{ foo=1, bar=2.0 }</code>)
                    p.note.
                        Some Standard ML implementations (including MLton, but not Poly/ML)
                        additionally have support for vector literals (e.g. <code>\#[1, 2, 3]</code>)
                        as part of Successor ML, informally agreed upon extensions to the standard.
                    h4 Parenthesis, semi-colons and compound expressions
                    p.
                        Semi-colons are mostly optional, except for in compound expressions.
                        But semi-colons are often necessary to disambiguate the end of a function
                        call. Here is an example:
                    pre
                        code.
                            let
                                val foo = fn a => a
                                val goo = fn b => b
                            in
                                foo "bar"
                                goo "baz"
                            end
                    p.
                        In this example, even though Standard ML knows that <code>foo</code> and <code>goo</code>
                        only take one argument, the compiler will still complain as it tries
                        to apply three arguments to <code>foo</code>: <code>foo "bar" goo "baz"</code>. You will need
                        a semi-colon to disambiguate this:
                    pre
                        code.
                            let
                                val foo = fn a => a
                                val goo = fn b => b
                            in
                                foo "bar";
                                goo "baz"
                            end
                    p.note.
                        Standard ML officially requires there be no trailing semi-colon in
                        a compound expression. Simarily, a comma must not follow the last
                        element in a tuple, list, or record. Some Standard ML implementations
                        (including MLton, but not Poly/ML) relax this requirement as part of
                        Successor ML.
                    p.
                        The <code>in ... end</code> block actually takes a single expression. But compound
                        expressions are expressions too and provide a way to evaluate multiple
                        expressions for side-effects while only returning the value of the last
                        expression.
                    p.
                        Parenthesis are typically allowed anywhere, surrounding an expression.
                        But outside of the <code>in ... end</code> block of a let block, you will need to
                        surround a compound expression in parenthesis to disambiguate:
                    pre
                        code.
                            > val i = (1 + 2; 8);
                            val i = 8: int
                    h4 Conditional logic
                    p.
                        There are two primary ways to conditionally evaluate expressions:
                        <code>if ... then ... else</code> and <code>case ... of ...</code> blocks.
                        Both return a value. An if block operates only on booleans. There is
                        no coercion of types like in C so you cannot test an int directly. You
                        must first compare the int to something to produce a boolean value.
                        Here is a simple example of an if block that returns <code>3</code>:
                    pre
                        code.
                            > if true then 3 else 12;
                            val it = 3: int
                    p.
                        The two simplest builtin operators for boolean logic are <code>andalso</code>
                        and <code>orelse</code>. Here is an example of their use.
                    pre
                        code.
                            > val twelve = if true andalso false then 3 else 12;
                            val twelve = 12: int
                            > val eleven = if false orelse true then 11 else 13;
                            val eleven = 11: int
                            > twelve + eleven;
                            val it = 23: int
                    p.
                        If you need an if block inside the then block, you will need to wrap it
                        in parenthesis to disambiguate the ending else. But if an if block follows
                        another if block in the <code>else</code> section, you don't need
                        parenthesis:
                    pre
                        code.
                            > val i = if true then 1 else if false then 11 else 14;
                            val i = 1: int
                            > val j = if true then (if false then 12 else 14) else 15;
                            val j = 14: int
                    p.
                        Case blocks provide generic pattern matching. You can match
                        on any values and can destructure here like anwhere else. Here
                        are a few examples:
                    pre
                        code.
                            > val j = 12;
                            val j = 12: int
                            > val i = case j of
                            #     11 => true
                            #   | 12 => false
                            val i = false: bool
                            > val s = case "foo" of
                            #     "bar" => 1 + 2
                            #   | "foo" => 45
                            val s = 45: int
                    p.
                        Case blocks have the same ambiguities as if blocks when it comes to
                        nested case blocks. If a case block follows another case block as
                        the ending case, it doesn't need to be wrapped in parenthesis. Otherwise,
                        it does.
                    h3 Variables
                    p.
                        Variables are declared in the top-level with the <code>val</code> keyword. The
                        type of the variable will (typically) be inferred without explicit
                        annotation.
                    pre
                        code.
                            > val i = 1;
                            val i = 1: int
                    p.
                        Outside of the top-level, variables must be declared inside a
                        <code>let ... in ... end</code> block. Only variable declarations can occur inside
                        the first sub-block. And only expressions can occur inside the second
                        sub-block. Semi-colons between variable declarations are optional, but
                        semi-colons between expressions are not.
                    p.
                        The simplest let block contains no variable declarations and returns a
                        single value.
                    pre
                        code let in 1 end
                    p.
                        The value of this let expression is <code>1</code>. A more complex let block will
                        declare variables. These variables are in scope immediately after
                        declaration and within the second sub-block. These variables are not
                        valid outside the let block.
                    pre
                        code.
                            let
                                val i = 1
                                val g = i + i
                            in
                                i;
                                g
                            end
                    p.
                        The result of evaluating this expression would be 2. While we reference
                        <code>i</code> in the second sub-block, the resulting value is not used and so is
                        discarded. Only the value of <code>g</code> is returned by the entire
                        <code>let ... in ... end</code> block.
                    h4 Discarding variables
                    p.
                        The underscore is a special variable marker that can be used to discard
                        a value or values. It can be used multiple times even within the same
                        variable declaration:
                    pre
                        code.
                            > val _ = 12;
                            > val [_, _, a] = [1, 2, 3];
                            val a = 3: int
                    p.
                        If you try to re-use any other variable name like that, the compiler will
                        error out:
                    pre
                        code.
                            > val [a, a, b] = [1, 2, 3];
                            poly: : error: a has already been bound in this declaration
                            Found near val [a, a, b] = [1, 2, 3]
                            Static Errors
                    h4 Shadowing variables
                    p.
                        Variables in Standard ML can be shadowed. In most languages, this happens
                        within a nested block scope. This is the case in Standard ML as well.
                        For instance, the following example will produce the value <code>5</code>:
                    pre
                        code.
                            let
                                val i = 1
                            in
                                let
                                    val i = 2
                                in
                                    i + i
                                end
                                + i
                            end
                    p.
                        Variables can also be shadowed within the same scope.
                        On the one hand, this can be a useful way to approximate procedural
                        algorithms. On the other hand, you can get subtle bugs because you
                        can erase a variable's previous value (as long as the type remains
                        the same) without the compiler complaining. The following example
                        will compile with no warnings and produce <code>2</code>:
                    pre
                        code.
                            let
                                val i = 1
                                val i = i + i
                                val i = 2
                            in
                                i
                            end
                    p I would avoid doing this in general.
                    h4 Mutable values
                    p.
                        Variables in Standard ML are immutable. However not all values in
                        are immutable. <code>ref</code> cells are the simplest example of mutable
                        values. Here are is a full example using <code>ref</code> cells:
                    pre
                        code.
                            let
                                val a = ref 1
                                val b = a
                                val c = (!b) + 3
                            in
                                a := 3;
                                c * (!a) + (!b)
                            end
                    p.
                        The result of this expression is (1 + 3) * (3) + (1) = 15.
                        The <code>!</code> operator dereferences the ref cell to get its value.
                        The type of <code>a</code> and <code>b</code> is <code>ref int</code>.
                    p.
                        Arrays are also mutable values (as opposed to vectors), but we'll get into
                        those in a later chapter.
                    p
                        a(href="/guides/learn-standardml/functions") Chapter 4. Functions
<commit_msg>Pluralize the word 'parenthesis' (#72)<commit_after>extends ../../tmpl/base.jade

block body
    .container
        | Guides &bull; Learn Standard ML &bull; Chapter 3
        h1 Expressions and Variables
        .subheader
            a(href="https://github.com/eatonphil/ponyo/blob/master/site/static/app/templates/guides/learn-standardml/expressions-and-variables.jade") Source
    .container
        .sidebar-container
            .row
                .col-md-6
                    h3 Expressions
                    p.
                        An expression is a chunk of code that returns a value. An expression
                        can always be assigned to a variable. On the other hand, a statement
                        is a chunk of code that does not return a value. A statement cannot be
                        assigned to a variable. Most parts of Standard ML are expressions,
                        not statements. Expressions consist of one of <the following:
                    ul
                        li Literals (e.g. <code>1</code>, <code>"foo"</code>)
                        li Variable references (e.g. <code>a</code>, <code>theVariable</code>)
                        li Expressions combined by an infix operator (e.g. <code>a + b</code>, <code>1 + theVariable</code>)
                        li If blocks (e.g. <code>if a then b else c</code>)
                        li Case blocks (e.g. <code>case a of 2 => true | 3 => false</code>)
                        li Lambda functions (e.g. <code>fn a => a</code>)
                        li Function calls (e.g. <code>split "foo" "o"</code>)
                        li Let blocks (e.g. <code>let val i = 1 in i end</code>)
                        li Local blocks (e.g. <code>local val i = 1 in i end</code>)
                        li Compound expressions (e.g. <code>1; a + b</code>)
                        li Any expression in parentheses (e.g. <code>(1)</code>, <code>(theVariable)</code>)
                    p The following are <strong>not</strong> expressions:
                        li Val declarations (e.g. the <code>val i = 1</code> part of <code>let val i = 1 in i end</code>)
                        li Named function declarations (e.g. <code>fun a b => b</code>)
                        li Type declarations (e.g. <code>type a = int</code>, <code>datatype b = Int of int | String of string</code>)
                        li Structures (declarations and references)
                        li Signatures (declarations and references)
                        li Functors (declarations and references)
                    h4 Literals
                    p.
                        The following types can be expressed as literal values:
                    ul
                        li Unit <code>()</code>
                        li Nil <code>nil</code>
                        li Integers (e.g. <code>1</code>, <code>~1</code>)
                        li Reals (e.g. <code>1.0</code>, <code>~1.0</code>)
                        li Strings (e.g. <code>"my string"</code>)
                        li Characters (e.g. <code>'f'</code>, <code>'\n'</code>)
                        li Booleans <code>true</code> and <code>false</code>
                        li Tuples (e.g. <code>(1, 2)</code>, <code>(x, 4.0, (12, 1))</code>)
                        li Lists (e.g. <code>[1, 2]</code>, <code>[(1, 2), (3, 4)]</code>)
                        li Records (e.g. <code>{ foo=1, bar=2.0 }</code>)
                    p.note.
                        Some Standard ML implementations (including MLton, but not Poly/ML)
                        additionally have support for vector literals (e.g. <code>\#[1, 2, 3]</code>)
                        as part of Successor ML, informally agreed upon extensions to the standard.
                    h4 Parentheses, semi-colons and compound expressions
                    p.
                        Semi-colons are mostly optional, except for in compound expressions.
                        But semi-colons are often necessary to disambiguate the end of a function
                        call. Here is an example:
                    pre
                        code.
                            let
                                val foo = fn a => a
                                val goo = fn b => b
                            in
                                foo "bar"
                                goo "baz"
                            end
                    p.
                        In this example, even though Standard ML knows that <code>foo</code> and <code>goo</code>
                        only take one argument, the compiler will still complain as it tries
                        to apply three arguments to <code>foo</code>: <code>foo "bar" goo "baz"</code>. You will need
                        a semi-colon to disambiguate this:
                    pre
                        code.
                            let
                                val foo = fn a => a
                                val goo = fn b => b
                            in
                                foo "bar";
                                goo "baz"
                            end
                    p.note.
                        Standard ML officially requires there be no trailing semi-colon in
                        a compound expression. Simarily, a comma must not follow the last
                        element in a tuple, list, or record. Some Standard ML implementations
                        (including MLton, but not Poly/ML) relax this requirement as part of
                        Successor ML.
                    p.
                        The <code>in ... end</code> block actually takes a single expression. But compound
                        expressions are expressions too and provide a way to evaluate multiple
                        expressions for side-effects while only returning the value of the last
                        expression.
                    p.
                        Parentheses are typically allowed anywhere, surrounding an expression.
                        But outside of the <code>in ... end</code> block of a let block, you will need to
                        surround a compound expression in parentheses to disambiguate:
                    pre
                        code.
                            > val i = (1 + 2; 8);
                            val i = 8: int
                    h4 Conditional logic
                    p.
                        There are two primary ways to conditionally evaluate expressions:
                        <code>if ... then ... else</code> and <code>case ... of ...</code> blocks.
                        Both return a value. An if block operates only on booleans. There is
                        no coercion of types like in C so you cannot test an int directly. You
                        must first compare the int to something to produce a boolean value.
                        Here is a simple example of an if block that returns <code>3</code>:
                    pre
                        code.
                            > if true then 3 else 12;
                            val it = 3: int
                    p.
                        The two simplest builtin operators for boolean logic are <code>andalso</code>
                        and <code>orelse</code>. Here is an example of their use.
                    pre
                        code.
                            > val twelve = if true andalso false then 3 else 12;
                            val twelve = 12: int
                            > val eleven = if false orelse true then 11 else 13;
                            val eleven = 11: int
                            > twelve + eleven;
                            val it = 23: int
                    p.
                        If you need an if block inside the then block, you will need to wrap it
                        in parentheses to disambiguate the ending else. But if an if block follows
                        another if block in the <code>else</code> section, you don't need
                        parentheses:
                    pre
                        code.
                            > val i = if true then 1 else if false then 11 else 14;
                            val i = 1: int
                            > val j = if true then (if false then 12 else 14) else 15;
                            val j = 14: int
                    p.
                        Case blocks provide generic pattern matching. You can match
                        on any values and can destructure here like anwhere else. Here
                        are a few examples:
                    pre
                        code.
                            > val j = 12;
                            val j = 12: int
                            > val i = case j of
                            #     11 => true
                            #   | 12 => false
                            val i = false: bool
                            > val s = case "foo" of
                            #     "bar" => 1 + 2
                            #   | "foo" => 45
                            val s = 45: int
                    p.
                        Case blocks have the same ambiguities as if blocks when it comes to
                        nested case blocks. If a case block follows another case block as
                        the ending case, it doesn't need to be wrapped in parentheses. Otherwise,
                        it does.
                    h3 Variables
                    p.
                        Variables are declared in the top-level with the <code>val</code> keyword. The
                        type of the variable will (typically) be inferred without explicit
                        annotation.
                    pre
                        code.
                            > val i = 1;
                            val i = 1: int
                    p.
                        Outside of the top-level, variables must be declared inside a
                        <code>let ... in ... end</code> block. Only variable declarations can occur inside
                        the first sub-block. And only expressions can occur inside the second
                        sub-block. Semi-colons between variable declarations are optional, but
                        semi-colons between expressions are not.
                    p.
                        The simplest let block contains no variable declarations and returns a
                        single value.
                    pre
                        code let in 1 end
                    p.
                        The value of this let expression is <code>1</code>. A more complex let block will
                        declare variables. These variables are in scope immediately after
                        declaration and within the second sub-block. These variables are not
                        valid outside the let block.
                    pre
                        code.
                            let
                                val i = 1
                                val g = i + i
                            in
                                i;
                                g
                            end
                    p.
                        The result of evaluating this expression would be 2. While we reference
                        <code>i</code> in the second sub-block, the resulting value is not used and so is
                        discarded. Only the value of <code>g</code> is returned by the entire
                        <code>let ... in ... end</code> block.
                    h4 Discarding variables
                    p.
                        The underscore is a special variable marker that can be used to discard
                        a value or values. It can be used multiple times even within the same
                        variable declaration:
                    pre
                        code.
                            > val _ = 12;
                            > val [_, _, a] = [1, 2, 3];
                            val a = 3: int
                    p.
                        If you try to re-use any other variable name like that, the compiler will
                        error out:
                    pre
                        code.
                            > val [a, a, b] = [1, 2, 3];
                            poly: : error: a has already been bound in this declaration
                            Found near val [a, a, b] = [1, 2, 3]
                            Static Errors
                    h4 Shadowing variables
                    p.
                        Variables in Standard ML can be shadowed. In most languages, this happens
                        within a nested block scope. This is the case in Standard ML as well.
                        For instance, the following example will produce the value <code>5</code>:
                    pre
                        code.
                            let
                                val i = 1
                            in
                                let
                                    val i = 2
                                in
                                    i + i
                                end
                                + i
                            end
                    p.
                        Variables can also be shadowed within the same scope.
                        On the one hand, this can be a useful way to approximate procedural
                        algorithms. On the other hand, you can get subtle bugs because you
                        can erase a variable's previous value (as long as the type remains
                        the same) without the compiler complaining. The following example
                        will compile with no warnings and produce <code>2</code>:
                    pre
                        code.
                            let
                                val i = 1
                                val i = i + i
                                val i = 2
                            in
                                i
                            end
                    p I would avoid doing this in general.
                    h4 Mutable values
                    p.
                        Variables in Standard ML are immutable. However not all values in
                        are immutable. <code>ref</code> cells are the simplest example of mutable
                        values. Here are is a full example using <code>ref</code> cells:
                    pre
                        code.
                            let
                                val a = ref 1
                                val b = a
                                val c = (!b) + 3
                            in
                                a := 3;
                                c * (!a) + (!b)
                            end
                    p.
                        The result of this expression is (1 + 3) * (3) + (1) = 15.
                        The <code>!</code> operator dereferences the ref cell to get its value.
                        The type of <code>a</code> and <code>b</code> is <code>ref int</code>.
                    p.
                        Arrays are also mutable values (as opposed to vectors), but we'll get into
                        those in a later chapter.
                    p
                        a(href="/guides/learn-standardml/functions") Chapter 4. Functions

{'fraction_non_alphanumeric': 0.060458607639220174, 'fraction_numerical': 0.008332538243806253, 'mean_word_length': 0.8571850451833914, 'pattern_counts': {'":': 0, '<': 261, '<?xml version=': 0, '>': 311, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 98, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3754314', 'n_tokens_mistral': 7051, 'n_tokens_neox': 6350, 'n_words': 2904}