Sign in

    marianoguerra/pure-py/value does not have a README file

    Env

    An environment: ρ. Immutable, because a closure captures one by value.

    DefClosure

    LamClosure

    MatchResult

    pub(all) enum MatchResult {
    Match(
    HashMap
    [String, Value])
    NoMatch
    MatchStuck(String)
    } derive(
    Debug
    )

    Whether a pattern matched, and what it bound.

    MatchResult::then

    fn MatchResult::then(self : MatchResult, other : MatchResult) -> MatchResult

    m ⊗ m': a match result composes by union of bindings, and no-match is absorbing.

    Outcome

    pub(all) enum Outcome {
    Val(Value)
    Aborts(Termination)
    Stuck(String)
    } derive(
    Debug
    )

    The outcome of evaluating an expression: a value, an abort, or an operation the semantics leaves undefined.

    Primitive

    pub(all) enum Primitive {
    Print
    Len
    Range
    Exit
    Sqrt
    Exp
    Log
    Sin
    Cos
    Tan
    MathFloor
    MathCeil
    Opaque(String)
    Foreign(String)
    } derive(Eq,
    Debug
    )

    The predefined functions of Figure 2.7.

    Opaque is for the three members that are values only so that importing them binds something: typing.Any, typing.Callable and dataclasses.dataclass are never called.

    StmtResult

    pub(all) enum StmtResult {
    Assigns(
    HashMap
    [String, Value])
    Returns(Value)
    ResultAborts(Termination)
    ResultStuck(String)
    } derive(
    Debug
    )

    The result of evaluating a statement.

    StmtResult::outcome

    fn StmtResult::outcome(self : StmtResult) -> Outcome

    outcome(r): a statement's result read as the outcome of calling it.

    StmtResult::then

    fn StmtResult::then(self : StmtResult, other : StmtResult) -> StmtResult

    Sequential composition of results: assigns ρ ⊗ r.

    Termination

    pub(all) enum Termination {
    TypeError
    IndexError
    KeyError
    ZeroDivisionError
    AttributeError
    AssertionError(String?)
    SystemExit(
    BigInt
    )
    } derive(Eq,
    Debug
    )

    How a run ends. Every kind but SystemExit is named after the exception the same program raises under Python.

    Termination::name

    fn Termination::name(self : Termination) -> String

    The name Python prints for a termination, and the message after it.

    Termination::to_text

    fn Termination::to_text(self : Termination) -> String

    AssertionError: message when there is one, the bare name otherwise -- the last line of the traceback Python prints.

    Value

    Runtime values, environments and evaluation results: Figure 4.1.

    Two things about this type are the whole shape of the language:

    • Nothing is mutable. A list, a tuple, a dictionary and an object are built once and never changed, so an environment can be shared by every closure that captures it and nothing has to be copied defensively.
    • Stuck is a result, not a crash. The semantics leaves some operations undefined, and Chapter 1 allows an implementation either to abort or to produce Python's answer. This one aborts, with the operation named, so that the conformance suite's dynamically excluded bucket is CHECKED rather than merely tolerated.

    Value::kind_name

    fn Value::kind_name(self : Value) -> String

    What kind of value this is, for a message naming an undefined operation.

    Value::repr

    fn Value::repr(self : Value) -> String?

    Value::str

    fn Value::str(self : Value) -> String?

    Python's str and repr of a value.

    The two differ in exactly one place -- a string is itself under str and quoted under repr -- and print uses str while a container prints its elements with repr. Everything else is the same function.

    A closure, a module, a class or a primitive has no printable form here. Python prints one with an address in it, which no implementation can reproduce, so printing one is an undefined operation and is reported as such rather than invented.

    big_to_double

    fn big_to_double(n :
    BigInt
    ) -> Double

    A BigInt as a double, correctly rounded -- which is what Python's float(n) gives. MoonBit's BigInt has no conversion, so it goes through the decimal text, which parse_double rounds correctly.

    binop

    The operators: ⊙̂(v, v') and ⊖̂(v).

    Arithmetic is Python's, with three things this port has to supply that MoonBit does not:

    • // and % floor. MoonBit's / and % on BigInt and Int64 truncate toward zero; Python's floor toward negative infinity, so the remainder takes the sign of the DIVISOR: -7 // 2 is -4 and 7 % -2 is -1.
    • bool is not a number. True + 1 and True < 2 are undefined, even though Python accepts both. That is the same rule that makes True == 1 undefined, and the conformance suite has a test for it.
    • An integer and a float compare exactly, not by converting the integer, so a large integer is not made equal to the double nearest it.

    The spec leaves arithmetic as "as in Python" with one sentence added: TypeError where an operand is not a number. So "a" + "b" aborts here rather than concatenating. Nothing in the conformance suite reaches it, and the decision is written down rather than guessed at each call site.

    compare

    ==, !=, in, not in and the four orderings.

    contains

    fn contains(haystack : Value, needle : Value) -> Bool?

    Whether needle is in haystack: an element of a list or tuple, a key of a dictionary, a substring of a string.

    contains_elems

    fn contains_elems(xs : Array[Value], needle : Value) -> Bool?

    elems

    fn elems(v : Value) -> Array[Value]?

    The elements of a list, a tuple or a string. A string's elements are its CODE POINTS, which is what Python iterates.

    empty_env

    entries

    fn entries(base : Array[(String, Value)], more : Array[(String, Value)]) -> Array[(String, Value)]

    entries(δ, δ'): the left entries extended by the right ones in order.

    env_of

    fn env_of(entries : Array[(String, Value)]) ->
    HashMap
    [String, Value]

    fn eq(a : Value, b : Value) -> Bool?

    The functions on values of Annex A.3: equality, membership, elements, iteration, subscripting and the dictionary builders.

    Equality is PARTIAL, and the order in which elements are compared is therefore observable: eq short-circuits at the first pair that decides the answer, so an undefined comparison later in a sequence does not make the whole comparison undefined. [1, "a"] == [1, 3] is stuck and [1, "a"] == [2, 3] is False, and the difference is deliberate.

    Every function that can be undefined returns a Bool? or an Outcome; None and Stuck mean "no rule", not "false".

    eq_elems

    fn eq_elems(xs : Array[Value], ys : Array[Value]) -> Bool?

    Elementwise equality, stopping at the first pair that decides it.

    exact_integer

    fn exact_integer(d : Double) ->
    BigInt
    ?

    The integer a double exactly equals, or None when it is not one.

    Read off the bit pattern rather than through decimal text, so it is exact at every magnitude: a double is mantissa * 2^exponent, and when it is integral the shift below loses nothing.

    extend_env

    ρ ⊲ ρ': extension, which differs from override only on modules -- a loaded module is preferred to a stub of the same module, and two loadings of one module merge member by member.

    extend_value

    fn extend_value(a : Value, b : Value) -> Value

    float_mod

    fn float_mod(x : Double, y : Double) -> Double

    Python's % on floats: the result takes the sign of the divisor.

    floor_div

    Python's // on integers: floor, not truncation.

    floor_mod

    Python's % on integers: the remainder takes the sign of the divisor.

    getitem

    fn getitem(v : Value, key : Value) -> Outcome

    Subscripting: a dictionary by a string key, a sequence by an integer index counting from the end when negative.

    host_fn

    fn host_fn(name : String) -> Value

    A function the host supplies, as a value the guest can call.

    The host puts one of these in a module (@eval.HostModule) and answers calls to it by name.

    iter

    fn iter(v : Value) -> Array[Value]?

    What a generator draws from a value: the KEYS of a dictionary, and otherwise its elements.

    order

    fn order(a : Value, b : Value) -> Int?

    Three-way comparison, None where the two values have no order.

    Numbers with numbers, strings by code point, lists with lists and tuples with tuples lexicographically. A nan anywhere makes the pair unordered, which is how Python's comparisons all come out False.

    override_env

    ρ ⊗ ρ': the right-hand side wins outright.

    unop

    not, unary + and unary -.

    update

    fn update(entries : Array[(String, Value)], key : String, v : Value) -> Array[(String, Value)]

    update(δ, w, v): the entries with w bound to v, in place if it was already there and appended otherwise.