Function

Function Instance

A function inputs parameter(s) and outputs (i.e. nothing) or a return value. A caller applies (a/k/a invokes or calls) a function by supplying a list of argument(s), which correspond to the expected parameters. The argument(s) list is comma delimited distributed inside one, or more, juxtaposed matched pair of grouping parenthesis.

Unlike JavaScript, a function in Copute is not a class, and thus does not have class members and can not be in the inheritance tree of another class. A literal function expression is simultaneously the declaration of a function type, the construction of an instance of that type, and is a reference to the constructed instance. A non-anonymous version of a function expression, includes an identifier, which stores the reference to the constructed instance.

The identifier, of a non-anonymous function expression, is a constant reference (i.e. implicitly and because function type is pass-by-value) that can not be pointed to (i.e. assigned) a different function instance. Ditto a reference that has the type of a function, because otherwise the reference identifier would not be pure if calling could return different results for same arguments.

Instance Reference

Each constructed instance of a , , , or function (but never a because it is not a type) is stored some where in computer memory and is accessed via a pointer known as a reference. There is no way to read the memory address of a reference. A reference is created by a reference declaration, which must normally be preceded by , except must not precede the function parameters nor class members. Two or more juxtaposed declarations may optionally be separated by a comma, e.g. ref decl1, decl2 instead of ref decl1; ref decl2.

Each reference (creation) declaration begins with an identifier, followed by an optional colon : and type declaration, followed by an optional assignment operator = and initialization expression. A reference, and thus any initialization expression, must have the type of a , , , or function; but never a because it is not a type.

The type of the initialization expression must be invariant or covariant with the optional declared type, if the declared type is not without a <...> suffix. If there is no initialization expression (and there is not a declared type ), the instance reference must be initialized by assignment before it is accessed in the right-hand side of an assignment expression, a closure, or assigned as an argument to a function.

Mutability

The pointer of a reference is always passed-by-value, but this is irrelevant since it is not accessible. There is no pointer type, such as in C, in order to access the value of the reference address that is being passed-by-value. The reference can only access the instance it points to. When the reference is passed, the instance may pass-by-reference or by-value. The following built-in types pass-by-value, e.g. Int, Bool, Float, String, and function type.

Compiler optimization: if the destination reference has a pass-by-value type that is or has no mutable members, then pass-by-reference may be more efficient (e.g. large data structures), is equivalent to, and can be utilized by the compiler instead of pass-by-value.

Compiler or class library optimization: if the destination reference has pass-by-value type that is not , the compiler might choose lazy evaluation and only copy the value when the instance is referenced for write access, or the class implementation might implement the equivalent the laziness (e.g. as most language string type do).

Passed-by-value Passed-by-reference
reference =, i.e. Assign copy of instance pointer to instance

Assignment Access

After initialization, in some cases a reference can not be assigned to, i.e. the pointer to the instance can not be changed, if for any of the following the reference:

Instance Access

The is a modifier that, when prepended to a type declaration, declares that only access to pure methods, and read-only members, of the instance is allowed. Thus for a pass-by-value type, creates a constant instance, because there is no way to pass the instance without making a copy. Whereas, for a pass-by-reference type which is returned from a function, or otherwise passed (e.g. assignment), only if the destination reference resides in a function, is it allowed to also be .

Thus for a pass-by-reference type, the modifier declares a pointer to an instance with immutable access, that is only transitive in a chain of pure functions. An improvement over the historical 'const' blunder, does not propagate (i.e. require destination reference to be ) for pass-by-value, and not to every impure function for pass-by-reference. A pass-by-reference type shall not be declared within a function that is not .

Pass-By-Expression

As explained the previous section, the instance result (a/k/a value) of an expression is passed (a/k/a assigned) to another reference, either by-value or by-reference.

To delay the evaluation of the passed expression to the first and every time that the destination reference is read (i.e. called), prepend Void -> before the explicitly declared type of the destination reference. This is also allowed for function parameters. This is also known as call-by-name and lazy evaluation. This declares the destination reference to be a function that takes no parameters and returns the expected type, thus the destination reference becomes read-only.

This is merely syntactical sugar that for any destination reference which is a function that takes no parameters, the passed expression (if not the expected function type) is implicitly wrapped by the compiler in a function closure that returns the result of the expression. A compiler error is generated when the implicit wrapping can not occur if the destination reference expects a pure function, and the closure would not be pure in that context.

Note unlike for Scala, every time the destination reference is read (i.e. called), the function will be called and if the function was implicitly created by a passed expression, that expression will be evaluated again. For applicable use cases, see how lazy evaluation is used in Scala, see "Lazy vals" subsection in section 20.5 "Initializing abstract vals", section 9.5 "By-name parameters", and "by-name parameter" in Glossary, of in Programming in Scala (Odersky, Spoon, Venners). See also how this is employed in Scala to create Domain Specific Language. Note Copute does not have a Unit type, but a brace enclosed scope automatically creates an anonmyous function Void -> Void, thus can be passed as an expression.

Purity

Functions declared with the keyword instead of , implicitly declare their pass-by-reference type parameter(s) to be , and may not do any of the following.

Functions can be overloaded on the impure, pure, and mutable attributes-- a pure can be assigned to a mutable and a mutable to an impure. For functions declared with the keyword (lambda) instead of , the compiler will infer whether the function is pure or mutable, however (unlike for example Haskell which unperspicuously infers impurity from type[1] that can be deceptive[2] even when the inferred type is explicit) it is usually desirable that the purity of functions is explicit in Copute, although it requires explicitly declaring functions overloaded on purity. A function containing the keyword creates two overloads where it is and (impure), which can be useful for writing a single function that can compile for both via type inference. There are some additional purity requirements for class methods.

Closure

A closure is formed when a function declaration accesses an instance reference, and the reference (creation) declaration is outside the function declaration (thus a static member is also implicated), except in the case where the reference has a type that is passed-by-value and is immutable in the scope of reference (creation) declaration. Thus a function call (not the declaration), with a passed-by-reference argument, is a closure on that argument.

A function declaration which is otherwise , will remain under closure, where for the life of every possible call of the function:

Multi-threadable

Thus a containing function is not multi-threadable (although potentially still compositionally , see below) if it contains a re-pointable and/or non- reference (creation) declaration subjected to a closure of an inner function declaration or call. A function is declared to be both and multi-threadable. A non-multi-threadable containing function could potentially contain multi-threadable function declaration(s).

A function declaration which is otherwise (except for optionally returning ) and which is declared and called from within a containing function, will not violate the purity of its containing function if:

Currying

Currying is the application of a function by supplying some, but not all, of a functions parameters, and returning (instead of the original function's return type) a function that has the original function's parameters, except not those corresponding to the supplied arguments. The returned function employs a closure on the supplied arguments. The returned function may or may not have already partially or completely evaluted the body of the original function. To skip a parameter in the supplied argument(s) and/or to force that parameter to exist in the returned function, use the double underscore wildcard __, which also skips an optional parameter. Whereas, the single underscore wildcard _ will skip an optional parameter, and apply its default value.

Each application of currying with the same instance references for the function and the argument(s), will return a new instance of a closure on the curried function. This may cause an error for example if the function is stored and later compared for some reason, e.g. a memory leak on failure to delete a callback function. This can be prevented by prefixing the curry expression with ; however, this should only be done when necessary as it will incur a hash key check on every use, hash table entry creation for each distinct instance, and the space versus time tradeoff is not unspecified.

Whether a curried function is , is determined by the closure on the supplied arguments.

Optional Parameter

In the function declaration, a formal parameter is optional if it has a default value. Note a formal parameter has a default value of . When called (a/k/a applied), each supplied argument is matched from left-to-right, thus in the function declaration, an optional may not precede a non-optional argument. An overloaded function can offer a version which skips an optional formal parameter followed by a non-optional formal parameter. Note: the special single underscore wildcard _ is reserved for "not used" placeholder, which on function call will skip over (and force) the corresponding default value, or on function declaration will throw away the corresponding formal parameter in the function body (will avoid the compiler error that the parameter is not used).

Operators

An operator name is composed of one or more adjacent visible characters other than the alphabet, digits 0 to 9, space, ( or ) parenthesis, nor as the first character . period, $ symbol, @ symbol, or _ underscore. An operator inputs a left operand and a right operand, except when the operator name is enclosed in parathesis, then it is a function which inputs its two operands with the left operand as the first argument. The operator functions are overloaded, and the standard operator functions may be redefined. Standard operators have the same precedence and associativity as JavaScript, except that the left-to-right associative concatenation operator # is added in between bit-shift and addition precedence, the left-to-right associative extrapolation operator .. is added in between comparison (relational) and bit-shift precedence. TODO: a syntax to declare the associativity and precedence of operator functions relative to the standard operators.

The = operator assigns the second argument's value or reference to the first argument, where the first argument is a built-in or class type instance respectively.

In addition to their standard semantics for integers, the left TL << TR and right TL >> TR bit-shift operators also apply to any type that has TR.(<<)( void ) : TL (aka unshift), TL.(<<)( TR ) : TL (aka push) and TR.(>>)( TL ) : TR (aka shift), TL.(>>)( void ) : TR (aka pop) methods respectively. The concatenation operator TL # TR applies to any type that has a TL.(#)( TR ) : TL (aka concat) method. The interpolation operator T .. T applies to any type that has an T.(..)( T ) : AT (aka interpolate) method (and typically it will return an array of values of that type).

TODO: move this to page for built-in data (string and container) types. The + operator is only for int and float. The # concatenation operator is used to combine string and container (e.g. array and list) types. This is to avoid a design error in languages that use + for both numbers and non-numbers, where auto-conversion of numbers to strings is supported. That creates an ambiguity as to whether string + number makes a number or a string. Thus in Copute, containers are not automatically converted to strings, call the toString method instead.

Parametrized Types

Each type parametrer of a function, may be used any where the (possibly bounded) type is expected in the function, as long as it does not demand a unique compilation for each concrete type the parameter could have. Where accessing a type parametrized function, and not providing a parameter list of concrete type(s), the type parameter(s) are inferred.

Type Parameter Constraint

Each type parameter may be optionally constrained by supertype T :> Super, subtype T <: Sub, or dual T :> Super <: Sub bound(s). There is also an abbreviation for the supertype bound T:Super or a verbose form T inherits Super. Scala documents an important use case, but note Scala has swaps the direction of the angled brackets T <: Super and T >: Sub, and at least at the time of writing this, has no analogous abbreviation. The choice of direction for angled brackets is arbitrary, and Scala adopted the academic standard, while Copute favors a beginner's understanding. Scala's syntax concurs with the notion of a supertype being "greater" than a subtype. By definition a supertype has a greater (i.e. more general, wider, less restrictive) set of possibilities (i.e. potential subtypes) than each of its subtypes. Copute's syntax concurs with the notion of a subtype "adding" (i.e. converting a potential subtype to) a concrete subtype of the supertype. And in Copute's notion, the angled bracket visually forks (analogous to a tree branch) in the direction of the subtype children.

Static Variables

To declare a static variable within a function scope, create a closure on a returned nested function.

var counter = function( d )
   {
      var i = d      // static variable
      return function()
         {
            return ++i
         }
   }( 0 )
counter()            // increment it

Overloading

Copute has the same overloading conventions as Java, C#, and Scala. Functions may be overloaded on arity and/or parameter type, but not on return type. The restriction on return type is due to inheritance substitutability, observing that any subtype can be substituted for (e.g. assigned to) its supertype, thus overloads whose return types have a common supertype can not be unambiguously assigned to that supertype, because they are all equally general on the cast to that supertype. And via mixin inheritance, an extension can change the set of possible supertype(s) of a subtype . Whereas, there is always an unambiguous assignment to overloaded parameter types that are supertypes of a specific subtype, because one of them will be the least general, i.e. a subtype of all the rest of them. Generality does not increase in the contravariant direction, but generality does decrease in the covariant direction, because Liskov Substitution Principle depends on that a supertype has a greater set of possible subtypes, than any of its potential subtypes.


[1]Hughes (2005). "Programming with Arrows", in 5th International Summer School on Advanced Functional Programming, LNCS 3622, pp 73-129, Springer, 2005 (§1.1, "Point-free programming")

[2]Hughes (2005). "Programming with Arrows", in 5th International Summer School on Advanced Functional Programming, LNCS 3622, pp 73-129, Springer, 2005 (§1.3, "Arrows as computations")