================================================================ | KLONG ** A SIMPLE ARRAY LANGUAGE | ================================================================ Version 2017-07-01 By Nils M Holm n m h @ t 3 x . o r g ---------------------------------------------------------------- CONTENTS ---------------------------------------------------------------- SYNOPSIS LEXEMES SYNTAX SEMANTICS : VALUES SEMANTICS : VERBS SEMANTICS : ARGUMENTS SEMANTICS : CONDITIONALS SEMANTICS : ADVERBS SEMANTICS : ARRAYS SEMANTICS : I/O CHANNELS SEMANTICS : OPERATORS SEMANTICS : FUNCTIONS SEMANTICS : VARIABLES SEMANTICS : MODULES INTERACTION ---------------------------------------------------------------- SYNOPSIS ---------------------------------------------------------------- Klong is an expression-based language that is inspired by K, which is in turn inspired by APL. A Klong program is basically a set of functions that manipulate list or array structures. You might also think of Klong as a glorified calculator with a massive set of functions for transforming arrays in various ways. One major difference from K is that Klong syntax is unambiguous. In K, semantic information is sometimes required to understand the syntax of a program, while in Klong, the syntax alone is enough to understand the meaning of a program. Of course, the ambiguity is by intention in K, because it allows to write very short expressions. For instance, the K expression f/x (f over x) may fold "f" over "x" or it may find the fixpoint of f(x), depending on the type of "f". In its dyadic form, x f/y, "/" may have three different meanings: fold, while, and iterate, depending on the type of "x". Klong uses different operators in this case: f/x (Over), f:~x (Converge), x f:~y (While), and x f:*y (Iterate). Converge and While are distinguished by their context (arity). Also, Klong does not overload operators to the same degree as K does. For instance, in K x_y can denote "drop" or "cut", depending on the type of the operand "x". Klong uses :_ for cut (and allows integers in the place of "x", e.g.: 3:_y). Klong's syntax is similar to K's, so some very simple K programs will run in Klong, but the similarity is superficial. Klong is also a work in progress. The version described in this document is noted at the top of the text. Programs written in this version will probably still work in future versions, but better means to the same ends may appear in later releases. This is the Klong language reference. If you are not familiar with K, J, APL, or a similar language, you might want to read the introduction instead. ---------------------------------------------------------------- LEXEMES ---------------------------------------------------------------- Every program is eventually composed of small lexical entities (lexemes, tokens) consisting of one or multiple ASCII characters. This is an overview of the lexemes that exist in Klong. Basic regular expression (regex) syntax (using just the . * + [] \ ? operators) is used to describe lexemes. All other characters represent themselves. . denotes any character * denotes zero or more appearances of the previous component + denotes one or more appearances of the previous component ? indicates that the previous component is optional [...] denotes any character contained between the brackets; ranges may be abbreviated, e.g. 0-9 for 0123456789 \ indicates that the following character is a literal character and not a regex operator :".*" [Comment] A comment. The entire lexeme will be ignored. The regex is in fact a lie. See [String], below, for an explanation. Examples: :"this is a lie" :"say ""hi""" [a-zA-Z\.][a-zA-Z0-9\.]* [Symbol] A symbolic name, may be used to name variables. Upper and lower case is distinguished. Examples: foo F00F .d X. Note that names starting with "." are reserved for system use. [!$%&*+,-\./;<=>?@\\^_|~] [Operator] :[!$%&*+,-\./;<=>?@\\^_|~] [Operator] A symbol naming an operator. All operator names are one or two characters long. When an operator is two characters long, the first characters is a colon (:). See also [Shift], below. Examples: + @ :^ Note: there are two non-shifted two-character operators named \~ (Scan-Converging) and \* (Scan-Iterating). -?[0-9]+ [Integer] Integer number with optional negative sign. There is no limit on the values of integers. Examples: 0 1 -1 1267650600228229401496703205376 -?[0-9]+\.[0-9]+ [Real Number] -?[0-9]+e[+-]?[0-9]+ [Real Number] -?[0-9]+\.[0-9]+e[+-]?[0-9]+ [Real Number] Real numbers consist of an integer part plus an optional fractional part (after a "." character) and an optional exponent part (after an "e" character). Examples: 0.0 -1.0 3.1415927 1e6 6.62607004e-34 -?0b[01]+ [Binary Integer] -?0o[0-7]+ [Octal Integer] -?0x[0-9a-f] [Hexa-Decimal Integer] Positive integers may be specified using an alternative base by prepending a 0b (binary), 0o (octal), or 0x (hex) prefix to the digits. Upper or lower case letters may be used in hex numbers. Examples: 0b101010 0o777 0xcafe 0xDEAD 0c. [Character] The 0c prefix is used to specify character literals. Unprintable characters cannot be specified using 0c. "0c " denotes a blank. Examples: 0c0 0ca 0cA 0c* 0c~ ".*" [String] A string is (almost) any sequence of characters enclosed by double quote characters. To include a double quote character in a string, it has to be duplicated, so the above regex is not entirely correct. A comment is a shifted string (see below). Examples: "" "hello, world" "say ""hello""!" :lexeme [Shift] The : character is used as a "shift" operator that changes the meaning of the following lexeme. :operator When applied to an operator symbol, it forms a two-character operator symbol. E.g.: :^ :$ := :string A shifted string is a comment. :symbol A shifted symbol is being "quoted", i.e. the symbol no longer denotes a variable, but a literal symbol name, e.g. :foo, :.d. :number, :character Shifting other lexemes does not have any effect. The shift sign will be ignored in these cases. ---------------------------------------------------------------- SYNTAX ---------------------------------------------------------------- The complete Klong syntax will be given here in EBNF notation. An EBNF grammar consists of rules of the form a := b c meaning "a" can be written as "b c". Rules can be recursive and | means "or", so x := y | y x would mean "'x' can be a 'y' or a 'y' followed by another 'x'", which basically means "'x' can be any (positive) number of 'y's". y+ is short for the above "x" (at least one "y"). y* is short for (y|"") where "" means "nothing". Parentheses are used for grouping. Literal lexemes appear in quotes, e.g.: f := '{' p '}' meaning "an 'f' is formed by a left brace, followed by a 'p' and a right brace". NOTE: a newline character translates to a semicolon in Klong, except in functions, dictionaries, conditional expressions, and lists. So a() b() is equal to a();b(), but [1 2 3] is equal to [1 2 3] and :[x; y; z] is equal to :[x;y;z] and f::{.d("hello "); .p("world!"); []} is a valid function definition. Here is the full Klong grammar (an informal description can be found at the end of this section): ................................................................ # A program is a ';'-separated sequence of expressions. p := e | e ';' p # An expression is a factor or a dyadic operation applied to # a factor and an expression. I.e. dyads associate to the right. e := x | x d e # A factor is a lexeme class (C) or a variable (V) applied to # arguments (a) or a function (f) or a function applied to # arguments or a monadic operator (m) applied to an expression # or a parenthesized expression or a conditional expression (c) # or a list (L) or a dictionary (D). x := C | V a | V :a # using :() instead of () | f | f :a # using :() instead of () | m e | '(' e ')' | c | L | D # Lexeme classes, the set of the lexemes specified in the # previous section, except for operators. C := I # integer | H # character | R # real number | S # string | V # variable (symbol) | Y # (quoted) symbol # A conditional expression has two forms: :[e1;e2;e3] means "if # e1 is true, evaluate to e2, else evaluate to e3". # :[e1;e2:|e3;e4;e5] is short for :[e1;e2:[e3;e4;e5]], i.e. the # ":|" acts as an "else-if" operator. There may be any number of # ":|" operators in a conditional. c := ':[' (e ';' e ':|')* e ';' e ';' e ']' # A monadic operator is an operator followed by some optional # adverbs or a function or a variable, each followed by at least # one adverb. m := O A* | f A+ | V A+ # A dyadic operator is almost the same as a monadic operator, # but a variable name is not allowed here. d := O A* | f A+ # A function is a program delimited by braces. Deja vu? f := '{' p '}' # Arguments are delimited by parentheses and separated by # semicolons. There are up to three arguments. a := '(' ')' | '(' e ')' | '(' e ';' e ')' | '(' e ':' e ';' e ')' # A list is any number of class lexemes (or lists) delimited by # square brackets. L := '[' (C|L)* ']' # A dictionary is a sequence of tuples delimited bt ':{' and '}'. D := ':{' t* '}' # A tuple is a list of two elements. t := '[' (C|L) (C|L) ']' ................................................................ Informally speaking, a Klong program is an expression or a sequence of expressions, separated by semicolons. Each expression is one out of these: - a factor (like a string, a function, a number, etc) - a prefix monadic operator applied to an expression - an infix dyadic operator applied to two expressions, associating to the right, so 1-2-3 means 1-(2-3) - a function application - a conditional expression ---------------------------------------------------------------- SEMANTICS : VALUES ---------------------------------------------------------------- Numbers (integer, real), characters, strings, lists, and dictionaries all evaluate to their respective values. The notation X --> V is used to denote that an expression X evaluates to a value V. Integer --> Integer Real Number --> Real Number Character --> Character String --> String List --> List Dictionary --> Dictionary All of the above expressions represent constant values, i.e. no computation is involved in their evaluation. In particular, all members of lists and dictionaries are already in their evaluated forms. Lists (and therefore also the tuples in dictionaries) auto-quote variables, so [Symbol] --> [Symbol] Quoted symbols are values, so they also evaluate to themselves. :Symbol --> :Symbol Variables evaluate to the values that are currently bound to them. The :: (define) operator is used to change the value of a variable (except for "x", "y", and "z"). See [Define]. Variable --> value Functions are first-class values. They also evaluate to themselves: Function --> Function ---------------------------------------------------------------- SEMANTICS : VERBS ---------------------------------------------------------------- Operators and functions act as "verbs" in expressions. Both map values to values. There are three kinds of operators: Monadic operators (or monads) take a single argument and appear in prefix positions, like +x, @x, or #x. Dyadic operators (or dyads) take two arguments and appear in infix positions, like x-y, x*y, x:^y. Functions have four different flavors called nilad, monad, dyad, and triad, taking 0, 1, 2, and 3 arguments respectively. All types of functions expect their arguments in argument lists. When a function is bound to a name, the argument list can be appended directly to the name: f() f(1) f(1;2) f(1;2;3) Between an anonymous function and its argument list, a colon has to be inserted: {f(x)}:(1) The colon can also be used in applications of named function, but is not necessary. All verbs return a value that depends on the specific function or operator. All operators are built into the Klong language, and no new operators can be defined by Klong programs. Functions are mostly defined by programs, but there are also some pre-defined Klong functions. The semantics of operators and pre-defined functions will be explained in detail in the sections about OPERATORS and FUNCTIONS. Anonymous functions return the value of the last expression in their program, e.g. the function {f();g();h()} would return the value of h(). A named function is defined by assigning an anonymous function to a variable, e.g.: square::{x*x} The type of a function (nilad, monad, dyad, triad) is determined by the variables that appear in the function. When a function contains the symbol "x", it is a monad, when it contains the symbol "y", it is a dyad, and when it contains the symbol "z", it is a triad. A function not containing any of these symbols is a nilad. E.g.: {1} :"nilad" {x} :"monad" {x+y} :"dyad" {x+y*z} :"triad" In function calls, the first argument is assigned to "x", the second one to "y", and the third one to "z". It is an error to supply more or fewer arguments than a function expects. A function containing only the symbol "y" is still a dyad (ignoring its first argument), and a function containing only "z" is still a triad (ignoring its first two arguments): {y} :"dyad" {z} :"triad" A function contained in a function does not influence the type of the outer function. For example, {f({x+y};[1 2 3])} is a nilad that passes the dyad {x+y} (and a list) to "f". Functions may have local variables that are specified in a list at the beginning of the function: {[variables];program} For instance, the function {[f];f::x;y@(f'y)?1} defines the local variable "f", assigns it the value of the argument "x", and then uses it in some computation. See the section on "Adverbs and Function Arguments" for an explanation. Local variables are bound dynamically: they have an undefined value initially and when the function defining them returns, they are re-assigned the value they had before the function was entered. So a::1;{[a];a::2};a will evaluate to 1. Using local variables in escaping functions, i.e. in functions returned by functions, is discouraged, because their values may change after returning the function. ---------------------------------------------------------------- SEMANTICS : ARGUMENTS ---------------------------------------------------------------- There are basically two types of operators in Klong: ones that accept compound data types as arguments and those that expect atoms exclusively. For instance, the | (reverse) operator expects a list, returning a list with the original elements in reverse order: |[1 2 3] --> [3 2 1] It is not a type error to pass a non-list to "|" -- in this case it will just return its argument (identity). The operation is just designed to work on lists. Then there are operations that work on atoms exclusively. An "atom" is a data object that cannot be decomposed into smaller units. Basically all non-list objects are atoms. Lists can be decomposed into list elements. A string can be viewed as a list of characters, so it is also non-atomic. The empty string "" and the empty list [] (nil) are atoms. For instance, the + (plus) operator is an "atomic operator", i.e. it expects atoms as its arguments: 5+7 --> 12 However, you can also pass lists to +. There are three cases to distinguish: the first element can be a list, the second one, or both: [1 2 3]+4 --> [5 6 7] 1+[2 3 4] --> [3 4 5] [1 2 3]+[4 5 6] --> [5 7 9] When one operand is a list, the other operand it combined with each member of that list. When both operands are lists, their elements are combined pairwise. Lists can also be written in the form a,b,c instead of [a b c], where "," is the "join" operator (see [Join]). The difference to bracket notation is that the elements can be dynamic, i.e. you can write a+1,a+2,a+3 and the values of the list elements will be computed at run time. Given the Join operator, the above reductions can be written like this: [a b c]+d --> (a+d),(b+d),c+d a+[b c d] --> (a+b),(a+c),a+d [a b c]+[d e f] --> (a+d),(b+e),c+f This notation is useful, because atomic operators can do even more than the above. They can recurse into nested lists. For instance, [a [b c] d]+e --> ((a+e),,((b+e),c+e)),d+e :"[a+e] [b+e] [c+e]" (The monadic "," (List) packages an object in a list, e.g. ,1 --> [1], and ,[1] --> [[1]]). Even lists of different sizes can be combined, as long as they adhere to certain rules: [[a b] [c d] [e f]]+[1 2] --> (,((a+1),b+2)),(,((c+1),d+2)),,(e+1),f+2 :"[[a+1 b+2] [c+1 d+2] [e+1 f+2]]" As long as atoms match atoms, atoms match lists, or lists match lists of equal length while descending into the operands, list operands of different shapes (see [Shape]) can be combined. For example: [a b c d]+1 [a b c d] 1 1 1 1 [[a b] [c d] [e f]]+[1 2] [[a b] [c d] [e f]] [1 2] [1 2] [1 2] [[[a b] [c d]] [e f]]+[[1 2] 3] [[[a b] [c d]] [e f]] [1 2] [1 2] 3 3 However: [a b]+[1 2 3] Does not match Monadic operators can also be atomic. When an atomic monad is applied to a list, the operator is applied to each element of the list, recursively, no matter what shape it has: -[[1 2 3] [4 5 6]] --> [[-1 -2 -3] [-4 -5 -6]] -[1 [2 [3] 4] 5] --> [-1 [-2 [-3] -4] -5] ---------------------------------------------------------------- SEMANTICS : CONDITIONALS ---------------------------------------------------------------- A conditional expression has the general form :[p;c;a], where "p" is the predicate, "c" the consequent, and "a" the alternative of the expression. The predicate is being evaluated first. When it yields a "true" value, the consequent is evaluated and otherwise the alternative is evaluated, so the value of the expression depends on the predicate. A predicate value is considered to be "false", if it is zero (0), nil ([]), or the empty string (""). All other values are treated as "true" values. Examples: :[1;:yes;:no] --> :yes :[0;:yes;:no] --> :no Multiple predicate-consequent pairs can be contained in a conditional expression by using the :| (Else-If) operator: :[p1;c1:|p2;c2:|...:|pN;cN;a] In this case, the first consequent (c1) will evaluate only if the first predicate (p1) is true, the second consequent (c2) will evaluate when then p1 is false but p2 is true, etc. The alternative (a) will only evaluate if all predicates are false. Formally, :[p1;c1:|p2;c2:|...:|pN;cN;a] is equal to :[p1;c1;:[p2;c2;:[...:[pN;cN;a]...]]] Example: ack::{:[0=x;y+1 :|0=y;ack(x-1;1) ;ack(x-1;ack(x;y-1))]} ---------------------------------------------------------------- SEMANTICS : ADVERBS ---------------------------------------------------------------- An adverb is an operator that is being attached to a verb, thereby modifying the behavior of the verb. In this section, "f" denotes a verb (function or operator) and "a" and "b" denotes operands. "aI" denotes the i'th member of "a", and "aN" denotes its last member. f'a [Each] If "a" is a list, apply "f" to each member of "a": f'a --> f(a1),...,f(aN) If "a" is an atom, return f(a). If "a" is [], ignore "f" and return []. If "a" is a dictionary, apply "f" to each tuple stored in the dictionary. The resulting list will be in some random order. Applying {x} (the identity function) to a dictionary turns it into a list of tuples. Dictionary updates are in situ, i.e. the existing dictionary is modified. Example: -'[1 2 3] --> [-1 -2 -3] ................................................................ a f'b [Each-2] Each-2 is like each, but applies "f" pairwise to elements of "a" and "b": a f'b --> f(a1;b2),...,f(aN;bN) If both "a" and "b" are atoms, return f(a;b). If either "a" or "b" is [], ignore "f" and return []. When the lengths of "a" and "b" differ, ignore any excess elements of the longer list. Example: [1 2 3],'[4 5 6] --> [[1 4] [2 5] [3 6]] ................................................................ a f:\b [Each-Left] a f:/b [Each-Right] If "b" is a list, both of these adverbs combine "a" with each element of "b", where :\ uses "a" as the left operand of "f", and :/ uses it as its right operand: a f:\b --> f(a;b1),...,f(a;bN) a f:/b --> f(b1;a),...,f(bN;a) If "b" is an atom, then a f:\b --> f(a;b) a f:/b --> f(b;a) When "b" is [], ignore "f" and return []. Examples: 1,:\[2 3 4] --> [[1 2] [1 3] [1 4]] 1,:/[2 3 4] --> [[2 1] [3 1] [4 1]] ................................................................ f:'a [Each-Pair] If "a" is a list of more than one element, apply "f" to each consecutive pair of "a": f:'a --> f(a1;a2),f(a2;a3),...,f(aN-1;aN) If "a" is an atom or a single-element list, return "a". If "a" is [], ignore "f" and return []. Example: ,:'[1 2 3 4] --> [[1 2] [2 3] [3 4]] ................................................................ f/a [Over] If "a" is a list, fold "f" over "a": f/a --> f(...f(f(a1;a2);a3)...;aN)) +/a --> ((...(a1+a2)+...)+aN) If "a" is an atom, ignore "f" and return "a". Example: +/[1 2 3 4] --> 10 ................................................................ a f/b [Over/Neutral] This is like "/", but with a neutral element "a" that will be returned when "b" is [] or combined with the first element of "b" otherwise: a f/[] --> a a f/b --> f(...f(f(a;b1);b2)...;bN) For example, +/[] will give [], but 0+/[] will give 0. Of course, dyadic "/" can also be used to abbreviate an expression by supplying a not so neutral "neutral element". For instance, a++/b can be abbreviated to a+/b. Formally, a f/b is equal to f/a,b Example: 0,/[1 2 3] --> [0 1 2 3] ................................................................ f:~a [Converge] The converge adverb finds the fixpoint of f(a), if any., i.e. the value for which f(a) = f(f(a)). For example, {(x+2%x)%2}:~2 converges toward the square root of two using Newton's method. Starting with x=2: (2+2%2)%2 --> 1.5 (1.5+2%1.5)%2 --> 1.41666 (1.41666+2%1.41666)%2 --> 1.41421 :"next value is the same" (1.41421+2%1.41421)%2 --> 1.41421 (Of course, the precision of the actual implementation will probably be higher.) Example: ,/:~["f" ["l" "at"] "ten"] --> "flatten" ................................................................ a f:~b [While] This adverb computes b::f(b) while a(b) is true. Formally: - if a(b) is false, return b - assign b::f(b) and start over Example: {x<1000}{x*2}:~1 --> 1024 ................................................................ a f:*b [Iterate] This adverb computes f^a(b), i.e. "f" applied recursively to "b" "a" times. More formally: - if "a" is zero, return b - assign b::f(b) and a::a-1 and start over Example: 3{1,x}:*[] --> [1 1 1] ................................................................ f\a [Scan-Over] a f\b [Scan-Over/Neutral] "\" is like "/", but collects intermediate results in a list and returns that list. In the resulting list, - the first slot will contain a1 - the second slot will contain f(a1;a2) - the third slot will contain f(f(a1;a2);a3) - the last slot will contain f(...f(a1;a2)...;aN) (which is the result of f/a) For atomic arguments and single-element lists, the return value is the same as the value of f/a. "a f\b" is equal to f\a,b. Examples: ,\[1 2 3] --> [3 [2 3] [1 2 3]] 4,\[1 2 3] --> [4 [3 4] [2 3 4] [1 2 3 4]] ................................................................ f\~a [Scan-Converging] Monadic \~ is like monadic :~, but returns a list of all intermediate results instead of the end result. The last element of the list will be same as the result of a corresponding :~ application. For instance: {(x+2%x)%2}\~2 will produce a list containing a series that converges toward the square root of 2. Example: ,/\~["a" ["b"] "c"] --> [["a" ["b"] "c"] ["a" "b" "c"] "abc"] ................................................................ a f\~b [Scan-While] This adverb is (almost) like is non-scanning counterpart, :~, but it collects intermediate results in a list and returns that list. However, \~ will only collect values X that satisfy a(X), while :~ will return the first value that does *not* satisfy a(X). E.g.: {x<10}{x+1}:~1 --> 10 {x<10}{x+1}:\1 --> [1 2 3 4 5 6 7 8 9] Example: {x<100}{x*2}\~1 --> [1 2 4 8 16 32 64] ................................................................ a f\*b [Scan-Iterate] This adverbs is like its non-scanning counterpart, but collects intermediate results in a list and return that list. Example: 3{1,x}\*[] --> [[] [1] [1 1] [1 1 1]] ................................................................ Multiple Adverbs Multiple adverbs can be attached to a verb. In this case, the first adverb modifies the verb, giving a new verb, and the next adverb modifies the new verb. For instance: +/' (Plus-Over-Each) would apply Plus-Over to each member of a list argument: +/'[[1 2 3] [4 5 6] [7 8 9]] --> [6 15 24] ,/:~ (Flatten-Over-Converging) would apply Flatten-Over until a fixpoint is reached: ,/:~[1 [2 [3 [4] 5] 6] 7] --> [1 2 3 4 5 6 7] ./\~ (Flatten-Over-Scan-Converging) explains why ,/:~ flattens any object: ,/\~[1 [2 [3 [4] 5] 6] 7] --> [[1 [2 [3 [4] 5] 6] 7] [1 2 [3 [4] 5] 6 7] [1 2 3 [4] 5 6 7] [1 2 3 4 5 6 7]] ................................................................ Adverbs and Function Arguments Note that function arguments ("x","y","z") cannot be verbs in verb-adverb combinations, because they will be eta-expanded! E.g.: {x'y}:({-x};[1 2 3]) :"BZZZT, WRONG!" will not work, because it eta-expands "x": {{x(x)}'y}:({-x};[1 2 3]) and because the inner "x" gets values from "y", it will try to apply "1" to itself, resulting in an error. In this case, you have to rename "x" first: {[f];f::x;f'y}:({-x};[1 2 3]) ---------------------------------------------------------------- SEMANTICS : ARRAYS ---------------------------------------------------------------- An array is a list with a symmetric shape, as determined by the "^" (Shape) operator. All arrays are lists, but not all lists are arrays. For example: ^[[1 2] [3 4] [5 6]] --> [3 2] i.e. an array of the shape 3,2, or a list of three elements, each having a size of two elements. All shapes are given in row-major order. This is a vector, but not a matrix: ^[[1 2] [3] [4 5]] --> [3] This is list/array of three elements, because the shape is not symmetric at column level ([3] has only one element). Of course, arrays may have more than two dimensions: ^[[[1 2 3 4] [5 6 7 8]] [[9 0 1 2] [3 4 5 6]] [[7 8 9 0] [1 2 3 4]]] --> [3 2 4] ^[5 5 5 5 5]:^0 --> [5 5 5 5 5] (The :^ (Reshape) operator reshapes an object; see [Reshape].) ---------------------------------------------------------------- SEMANTICS : I/O CHANNELS ---------------------------------------------------------------- Input and output in Klong is based on channels. Functions like .rl (Read-line) and .p (Print) read and write the current input and output channels. The current input channel is called the From Channel, and the current output is called the To Channel. Here is a simple-minded Unix cat(1) utility that just echoes its input: cat::{.mi{.p(x);.rl()}:~.rl()} The .mi function returns truth as long as there is "more input" available from the current From Channel. So what this function does is "While More-Input, (Print x, Read-Line), starting with Read-line". Additional input channels can be opened with the Input-Channel (.ic) function and selected with the From-Channel (.fc) function. Here is a function that types the content of a file: type::{.fc(.ic(x));cat()} The function opens the input channel "x" and selects it as the current From Channel. With the new From Channel established, it calls "cat", which now reads from that channel. The channel is never closed here. Klong will automatically close it at some time. Similarly, a new output channel can be established using the Output-Channel (.oc) and To-Channel (.tc) functions. The following function copies file "x" to "y": copy::{[of];.tc(of::.oc(y));type(x);.cc(of)} Note that "copy" does close the output channel. This is not strictly necessary, but makes sure that all output has been written to "y" when "copy" returns. NOTE: in interactive mode, the From and To Channels are reset to stdin and stdout after program run time. So expressions like .tc(.oc("foo"));.p("hello!") are not guaranteed to finish their output at any specific time. To make sure that output is written, use: .tc(F::.oc("foo"));.p("hello!");.cc(F) ---------------------------------------------------------------- SEMANTICS : OPERATORS ---------------------------------------------------------------- a:=b [Amend] "a" must be a list or string and "b" must be a list where the first element can have any type and the remaining elements must be integers. It returns a new object of a's type where a@b1 through a@bN are replaced by b0. When "a" is a string, b1...bN must be characters. The first element of "a" has an index of 0. Examples: "-----":=[0cx 1 3] --> "-x-x-" [1 2 3]:=[0 1] --> [1 0 3] ................................................................ a:-b [Amend-in-Depth] :- is like :=, but "a" may be a multi-dimensional array. The :- operator replaces one single element in that array. The sequence of indices b1..bN is used to locate the target element in an N-dimensional array. The number of indices must match the rank of the array or a shape error will be signalled. Example: [[1 2] [3 4]]:-[42 0 1] --> [[1 42] [3 4]] [[[0]]]:-[1 0 0 0] --> [[[1]]] ................................................................ @a [Atom] @ returns 1, if "a" is an atom and otherwise 0. All objects except for non-empty lists and non-empty strings are atoms. Examples: @"" --> 1 @[] --> 1 @123 --> 1 @[1 2 3] --> 0 ................................................................ :#a [Char] Return the character at the code point "a". Examples: :#64 --> 0cA :#10 --> :"newline character" ................................................................ a:_b [Cut] Cut the list "b" before the elements at positions given in "a". "a" must be an integer or a list of integers. When it is a list of integers, its elements must be in monotonically increasing order. :_ returns a new list containing consecutive segments of "b". When "a" is zero or #b or contains two subsequent equal indices, nil (or an empty string if "b" is a string) will be inserted. Examples: 2:_[1 2 3 4] --> [[1 2] [3 4]] [2 3 5]:_"abcdef" --> ["ab" "c" "de" "f"] 0:_[1] --> [[] [1]] 3:_"abc" --> ["abc" ""] [1 1]:_[1 2] --> [[1] [] [2]] ................................................................ a::b [Define] Assign "b" to the variable "a" and return "b". When a local variable named "a" exists, the value will be assigned to it, otherwise the global variable "a" will be assigned the value. Note that :: cannot be used to assign values to the function variables "x", "y", and "z" (they are read-only). Examples: a::[1 2 3];a --> [1 2 3] a::1;{[a];a::2};a --> 1 ................................................................ a%b [Divide] Return the quotient of "a" and "b". The result is always a real number, even if the result has a fractional part of 0. "%" is an atomic operator. Examples: 10%2 --> 5.0 10%8 --> 1.25 ................................................................ a_b [Drop] When "b" is a list or string, drop "a" elements or characters from it, returning the remaining list. Dropping more elements than contained in "b" will leave the empty list/string. A negative value for "a" will drop elements from the end of "b". When "b" is a dictionary, remove the entry with the key "a" from it. Dictionary removal is in situ, i.e. the dictionary will be modified. Other objects will be copied. Examples: 3_[1 2 3 4 5] --> [4 5] (-3)_"abcdef" --> "abc" 17_[1 2 3] --> [] (-5)_"x" --> "" 0_[1] --> [1] ................................................................ !a [Enumerate] Create a list of integers from 0 to a-1. !0 gives []. Examples: !0 --> [] !1 --> [1] !10 --> [0 1 2 3 4 5 6 7 8 9] ................................................................ a=b [Equal] Return 1, if "a" and "b" are equal, otherwise return 0. Numbers are equal, if they have the same value. Characters are equal, if (#a)=#b. Strings and symbols are equal, if they contain the same characters in the same positions. "=" is an atomic operator. In particular it means that it cannot compare lists, but only elements of lists. Use "~" (Match) to compare lists. Real numbers should not be compared with "=". Use "~" instead. Examples: 1=1 --> 1 "foo"="foo" --> 1 :foo=:foo --> 1 0cx=0cx --> 1 [1 2 3]=[1 4 3] --> [1 0 1] ................................................................ &a [Expand/Where] Expand "a" to a list of subsequent integers X, starting at 0, where each XI is included aI times. When "a" is zero or an empty list, return nil. When "a" is a positive integer, return a list of that many zeros. In combination with predicates this function is also called Where, since it shortens a list of boolean values to indices, e.g.: [1 2 3 4 5]=[0 2 0 4 5] --> [0 1 0 1 1] &[1 2 3 4 5]=[0 2 0 4 5] --> [1 3 4] Examples: &0 --> [] &5 --> [0 0 0 0 0] &[1 2 3] --> [0 1 1 2 2 2] &[0 1 0 1 0] --> [1 3] ................................................................ a?b [Find] Find each occurrence of "b" in "a". "a" must be a list, string, or dictionary. When "a" is a dictionary, return the value associated with the given key. When "a" is a list or string, return a list containing the position of each match. In either case nil indicates that "b" is not contained in "a". When a key cannot be found in a dictionary, Find will return :undefined. (See [Undefined].) Examples: [1 2 3 1 2 1]?1 --> [0 3 5] [1 2 3]?4 --> [] "hello"?0cl --> [2 3] :{[1 2]}?1 --> 1 :{[1 2]}?3 --> :undefined ................................................................ *a [First] Return the first element of "a", i.e. the first element of a list or the first character of a string. When "a" is an atom, return that atom. Examples: *[1 2 3] --> 1 *"abc" --> 0ca *"" --> "" *[] --> [] *1 --> 1 ................................................................ _a [Floor] Return "a" rounded toward negative infinity. When "a" is an integer, this is an identity operation. If "a" can be converted to integer without loss of precision after rounding, it will be converted. Otherwise, a floored real number will be returned. Note: loss of precision is predicted by comparing real number precision to the exponent, which is a conservative guess. Examples: _123 --> 123 _123.9 --> 123 _1e100 --> 1.0e+100 :"if precision < 100 digits" ................................................................ a:$b [Form] Convert string "b" to the type of the object of "a". When "b" can be converted to the desired type, an object of that type will be returned. When such a conversion is not possible, :$ will return :undefined. When "a" is an integer, "b" may not represent a real number. When "a" is a real number, a real number will be returned, even if "b" represents an integer. When "a" is a character, "b" may must contain exactly one character. When "a" is a symbol, "b" must contain the name of a valid symbol. :$ is an atomic operator. Examples: 1:$"-123" --> -123 1.0:$"1.23" --> 1.23 0c0:$"x" --> 0cx "":$"string" --> "string" :x:$:symbol --> :symbol ................................................................ $a [Format] Write the external representation of "a" to a string and return it. The "external representation" of an object is the form in which Klong would display it. "$" is an atomic operator. Examples: $123 --> "123" $123.45 --> "123.45" $"test" --> "test" $0cx --> "x" $:foo --> ":foo" ................................................................ a$b [Format2] Dyadic "$" is like its monadic cousin, but also pads its result with blanks. The minimal size of the output string is specified in "a", which must be an integer. "b" is the object to format. When the value of "a" is negative, the result string is padded to the right, else it is padded to the left. When "a" is real number of the form n.m and "b" is also a real number, the representation of "b" will have "n" integer digits and "m" fractional digits. The integer part will be padded with blanks and the fractional part will be padded with zeros. "$" is an atomic operator. Examples: 0$123 --> "123" (-5)$-123 --> " -123" 5$"xyz" --> "xyz " (-5)$:foo --> ":ffoo" 5.3$123.45 --> " 123.450" ................................................................ >a [Grade-Down] <a [Grade-Up] Impose the given order ("<" = ascending, ">" = descending") onto the elements of "a", which must be a list or string. Return a list of indices reflecting the desired order. Elements of "a" must be comparable by dyadic "<" (Less). In addition, "<" and ">" will compare lists by comparing their elements pairwise and recursively. E.g. [1 [2] 3] is considered to be "less" than [1 [4] 0], because 1=1 and 2<4 (3>0 does not matter, because 2<4 already decides the comparison). When "a" is a string, these operators will grade its characters. To sort a list "a", use a@<a ("a" At Grade-Up "a") or a@>a. Examples: <[1 2 3 4 5] --> [0 1 2 3 4] >[1 2 3 4 5] --> [4 3 2 1 0] <"hello, world" --> [6 5 11 1 0 2 3 10 8 4 9 7] >[[1] [2] [3]] --> [2 1 0] ................................................................ =a [Group] Return a list of lists ("groups") where each group contains the indices of one element within "a". "a" must be a list or string. The indices of all elements of "a" that are equal according to "~" (Match) will appear in the same group in ascending order. All elements of "a" will be covered by "=". ="" and =[] will yield []. Examples: =[1 2 3 4] --> [[0] [1] [2] [3]] ="hello foo" --> [[0] [1] [2 3] [4 7 8] [5] [6]] ................................................................ a@b [At/Index] a@b [At/Apply] Extract one or multiple elements from "a" at (zero-based) positions given in "b". In this case "a" may be a list or a string. When "b" is an integer, extract a single element at the given position and return it. When "b" is a list, return a list containing the extracted elements. All members of "b" must be integers in this case. The order of indices in "b" does not matter. The same index may occur multiple times. When "a" is a function, "b" (if it is an atom) or the members of "b" (if it is a list) will be passed as arguments to the function and the result will be returned. Examples: [1 2 3 4 5]@2 --> 3 [1 2 3 4 5]@[1 2 3] --> [2 3 4] [1 2 3 4 5]@[0 0 0] --> [1 1 1] "hello world"@[3 7 2] --> "lol" {x}@:foo --> :foo {y+x*x}@[2 3] --> 7 ................................................................ a:@b [Index-in-Depth] :@ is like "@" but, when applied to an array, extracts a single element from a multi-dimensional array. The indices in "b" are used to locate the element. The number of indices must match the rank of the array. If "a" is a function, :@ is equal to "@". Examples: [[1 2] [3 4]]:@[0 1] --> 2 [[[1]]]:@[0 0 0] --> 1 {y+x*x}:@[2 3] --> 7 ................................................................ a:%b [Integer-Divide] Return the integer part of the quotient of "a" and "b". Both "a" and "b" must be integers. The result is always an integer. Formally, a = (b*a:%b) + a!b . ":%" is an atomic operator. Examples: 10:%2 --> 5 10:%8 --> 1 ................................................................ a,b [Join] The "," operator joins objects of any type, forming lists or strings. If "a" and "b" are lists, append them. If "a" is a list and "b" is not, attach "b" at the end of "a". If "a" is a not list and "b" is one, attach "a" to the front of "b". If "a" and "b" are strings, append them. If "a" is a string and "b" is a char, attach "b" to the end of "a". If "a" is a char and "b" is a string, attach "a" to the front of "b". If "a" is a dictionary and "b" is a tuple (a list of two members) or vice versa, add the tuple to the dictionary. Any entry with the same key will be replaced by the new entry. The head of the tuple is the key and the second element is the payload. Otherwise, create a tuple containing "a" and "b" in that order. Join always returns a fresh list, but dictionaries will be updated by replacing old entries in situ. Examples: [1 2 3],[4 5 6] --> [1 2 3 4 5 6] 1,[2 3] --> [1 2 3] [1 2],3 --> [1 2 3] "abc","def" --> "abcdef" "ab",0cc --> "abc" 0ca,"bc" --> "abc" 1,2 --> [1 2] "a",1 --> ["a" 1] [[1 2 3]],4 --> [[1 2 3] 4] 1,2,3,4 --> [1 2 3 4] [1 2],:{[1 0]} --> :{[1 2]} :{[1 0]},[1 2] --> :{[1 2]} ................................................................ a<b [Less] Return 1, if "a" is less than "b", otherwise return 0. Numbers are compared by value. Characters are compared by ASCII code. Strings and symbols are compared lexicographically. "<" is an atomic operator; it cannot compare lists, but only elements of lists. Examples: 1<2 --> 1 "bar"<"foo" --> 1 :abc<:xyz --> 1 0c0<0c9 --> 1 [1 2 3]<[1 4 3] --> [0 1 0] ................................................................ ,a [List] "," packages any object in a single-element list. Examples: ,1 --> [1] ,:foo --> [:foo] ,"xyz" --> ["xyz"] ,[1] --> [[1]] ................................................................ a~b [Match] "~" is like "=", but can also compare lists and real numbers. It uses "=" (Equal) to compare integers, characters, symbols and strings. Real numbers are compared by subtracting them and comparing the result to .e (Epsilon). When the magnitude of a-b is less than .e, "a" and "b" match. Two lists match if all of their elements match pairwise. "~" descends into sublists. Examples: 1~1 --> 1 "foo"~"foo" --> 1 :foo~:foo --> 1 0cx~0cx --> 1 [1 2 3]~[1 2 3] --> 1 [1 [2] 3]~[1 [4] 3] --> 0 ................................................................ a|b [Max/Or] Return the larger one of two numbers. When both "a" and "b" are in the set {0,1} (booleans), then "|" acts as an "or" operator, as you can easily prove using a truth table: a b max/or 0 0 0 0 1 1 1 0 1 1 1 1 Dyadic "|" is an atomic operator. Examples: 0|1 --> 1 123|-123 --> 123 1.0|1.1 --> 1.1 ................................................................ a&b [Min/And] Return the smaller one of two numbers. When both "a" and "b" are in the set {0,1} (booleans), then "&" acts as an "and" operator, as you can easily prove using a truth table: a b min/and 0 0 0 0 1 0 1 0 0 1 1 1 Dyadic "&" is an atomic operator. Examples: 0&1 --> 0 123&-123 --> -123 1.0&1.1 --> 1.0 ................................................................ a-b [Minus] Subtract "b" from "a" and return the result. "a" and "b" must be numbers. "-" is an atomic operator. Examples: 12-3 --> 9 12--3 --> 15 1-0.3 --> 0.7 ................................................................ a>b [More] Return 1, if "a" is greater than "b", otherwise return 0. See "<" (Less) for details on comparing objects. ">" is an atomic operator; it cannot compare lists, but only elements of lists. Examples: 2>1 --> 1 "foo"<"bar" --> 1 :xyz<:abc --> 1 0c9<0c0 --> 1 [1 4 3]<[1 2 3] --> [0 1 0] ................................................................ -a [Negate] Return 0-a. A must be a number. "-" is an atomic operator. Examples: -1 --> -1 -1.23 --> -1.23 ................................................................ ~a [Not] Return the negative truth value of "a", as explained in the section on CONDITIONALS. It will return 1 for 0, [], and "", and 0 for all other values. Examples: ~0 --> 1 ~1 --> 0 ~[] --> 1 ~:foo --> 0 ................................................................ a+b [Plus] Add "b" to "a" and return the result. "a" and "b" must both be numbers. Dyadic "+" is an atomic operator. Examples: 12+3 --> 15 12+-3 --> 9 1+0.3 --> 1.3 ................................................................ a^b [Power] Compute "a" to the power of "b" and return the result. Both "a" and "b" must be numbers. When ~b=_b ("b" has a fractional part), then "a" must not be negative (i.e. the result of a^b cannot be a complex number). Dyadic "^" is an atomic operator. Examples: 2^0 --> 1 2^1 --> 2 2^8 --> 256 2^-5 --> 0.03125 0.3^3 --> 0.027 2^0.5 --> 1.41421356237309504 ................................................................ ?a [Range] Return a list containing unique elements from "a" in order of appearance. "a" may be a list or string. Examples: ?[1 2 3 4] --> [1 2 3 4] ?[1 1 1 2 2] --> [1 2] ?"aaabbcccd" --> "abcd" ................................................................ %a [Reciprocal] Return 1%a. "a" must be a number. "%" is an atomic operator. Examples: %1 --> 1.0 %2 --> 0.5 %0.1 --> 10.0 ................................................................ a:^b [Reshape] :^ reshapes "b" to the shape specified in "a". The shape is specified in the form returned by the "^" (Shape) operator: a list of dimensions in row-major order. The operand "b" may be in any shape. The elements of the new array will be taken from "b" in sequential order: [3 3]:^[1 2 3 4 5 6 7 8 9] --> [[1 2 3] [4 5 6] [7 8 9]] When the source array contains more elements that can be stored in an array of the shape "a", excess elements in "b" will be ignored. When the source array contains too few elements, :^ will cycle through the source object, repeating the elements found there: [3 3]:^[0 1] --> [[0 1 0] [1 0 1] [0 1 0] Both "a" and "b" may be atoms: 5:^1 --> [1 1 1 1 1] Examples: 5:^:x --> [:x :x :x :x :x] [3]:^[1] --> [1 1 1] [2 2 2]:^[1 2 3] --> [[[1 2] [3 1]] [[2 3] [1 2]]] ................................................................ a!b [Remainder] Return the truncated division remainder of "a" and "b". Both "a" and "b" must be integers. Formally, a = (b*a:%b) + a!b . Dyadic "!" is an atomic operator. Examples: 7!5 --> 2 7!-5 --> 2 (-7)!5 --> -2 -7!-5 --> -2 ................................................................ |a [Reverse] Return a new list/string that contains the elements of "a" in reverse order. When "a" is neither a list nor a string, return it unchanged. Examples: |[1 2 3] --> [3 2 1] |"hello world" --> "dlrow olleh" |1 --> 1 ................................................................ a:+b [Rotate] Rotate the list or string "b" by "a" elements. "a" must be an integer. When "a" is positive, rotate elements to the "right", i.e. drop elements from the end of "b" and append them to the front. When "a" is negative, rotate "b" to the left, i.e. drop from the beginning, append to the end. "a" may be greater than #b. In this case, the number of elements rotated will be a!#b. Note that n:+M rotates the rows of a matrix M (i.e. it rotates it vertically); to rotate its columns (horizontally), use n:+:\M (Rotate-Each-Left). Examples: 1:+[1 2 3 4 5] --> [5 1 2 3 4] (-1):+[1 2 3 4 5] --> [2 3 4 5 1] 1:+[[1 2] [4 5] [5 6]] --> [[1 2] [4 5] [5 6]] {1:+x}'[[1 2] [4 5] [5 6]] --> [[2 1] [5 4] [6 5]] ................................................................ ^a [Shape] Return the shape of "a". The shape of an atom is 0. The shape of a list L of atoms is ,#L. Such a list is also called a 1-array or a vector. The shape of a list of lists of equal length (M) is (#M),#*M. Such a list is called a 2-array or a matrix. A list of lists of unequal length is a vector. This principle is extended to higher dimensions. An N-array A is is an array with equal-sized sub-arrays in each dimension. Its shape is (#A),(#*A),...,(#*...*A), where there are N-1 "*" operators in the last group of that expression. All shapes are written in row-major notation. For example: [1 2 3 4 5] is a vector (shape [5]) [[1 2] [2 4] [5 6]] is a matrix (shape [3 2]) [[[1 2 3 4] [5 6 7 8]] [[9 0 1 2] [3 4 5 6]] [[7 8 9 0] [1 2 3 4]]] is a 3-array (shape [3 2 4]) [[1] [2 3]] is a vector (shape [2]) The shape of a string S is #S. A list of equally-sized strings is a matrix of characters. Strings may form the innermost level of higher-dimensional arrays. Examples: ^1 --> 0 ^:xyz --> 0 ^[0] --> [1] ^[1 2 3] --> [3] ^"hello" --> [5] ^[[1 2] [3 4] [5 6]] --> [3 2] ^[1 [2]] --> [2] ^["abcd" "efgh"] --> [2 4] ................................................................ #a [Size] Return the size/magnitude of "a". For lists, the size of "a" is the number of its elements. For strings, the size is the number of characters. For numbers, the size is the magnitude (absolute value). For characters, the size is the ASCII code. Examples: #[1 2 3] --> 3 #[1 [2 3] 4] --> 3 #"123456789" --> 9 #-123 --> 123 #0cA --> 65 ................................................................ a:#b [Split] Split a list or string "b" into segments of the sizes given in "a". If "a" is an integer, all segments will be of the same size. If "a" is a list of more than one element, sizes will be taken from that list. When there are more segments than sizes, :# will cycle through "a". The last segment may be shorter than specified. Examples: 2:#[1 2 3 4] --> [[1 2] [3 4]] 3:#[1 2 3 4] --> [[1 2 3] [4]] 3:#"abcdefg" --> ["abc" "def" "g"] [1 2]:#[1 2 3 4 5 6] --> [[1] [2 3] [4] [5 6]] ................................................................ a#b [Take] Extract "a" elements from the front of "b". "a" must be an integer and "b" must be a list or string. If "a" is negative, extract elements from the end of "b". Extracting more elements than contained in "b" will fill the extra slots by cycling through "b". Taking 0 elements will result in an empty list or string. Examples: 1#[1 2 3] --> [1] 2#[1 2 3] --> [1 2] 5#[1 2 3] --> [1 2 3 1 2] (-2)#[1 2 3] --> [2 3] (-5)#[1 2 3] --> [2 3 1 2 3] 3#"abcdef" --> "abc" (-3)#"abcdef" --> "def" 0#[] --> [] 0#"" --> "" ................................................................ a*b [Times] Return "a" multiplied by "b". "a" and "b" must both be numbers. Dyadic "*" is an atomic operator. Examples: 3*4 --> 12 3*-4 --> -12 0.3*7 --> 2.1 ................................................................ +a [Transpose] Return the transposed matrix (2-array) "a". Examples: +[[1] [2] [3]] --> [[1 2 3]] +[[1 2 3] [4 5 6]] --> [[1 4] [2 5] [3 6]] +[] --> [] ................................................................ :_a [Undefined] Return truth, if "a" is undefined, i.e. the result of an operation that cannot yield any meaningful result, like division by zero or trying to find a non-existent key in a dictionary. Else return 0. Examples: :_1%0 --> 1 :_:{[1 2]}?3 --> 1 :_:valid --> 0 ---------------------------------------------------------------- SEMANTICS : FUNCTIONS ---------------------------------------------------------------- .ac(a) [Append-Channel] See [Output-Channel]. ................................................................ .cc(a) [Close-Channel] Close the input or output channel "a", returning []. Closing an already closed channel has no effect. A channel will be closed automatically when no variable refers to it and it is not the current From or To Channel. ................................................................ .comment(a) [Comment] Read and discard lines until the current line starts with the string specified in "a". Also discard the line containing the end-of-comment marker and return "a". Example: .comment("end-of-comment") this will be ignored this, too: *%(*^#)&(# end-of-comment ................................................................ .d(a) [Display] See [Write]. ................................................................ .df(a) [Delete-File] Delete the file specified in the string "a". When the file cannot be deleted (non-existent, no permission, etc), signal an error. ................................................................ .E(a) [Evaluate] Evaluate the Klong program contained in the string "a" and return its result. This is a direct interface to the Klong system, e.g. .E("a::123");a will yield 123. ................................................................ .fc(a) [From-Channel] .tc(a) [To-Channel] .fc selects input channel "a" for reading and .tc selects output channel "a" for writing. All input/output will be redirected to the given channel. When a false values (0,[],"") is passed to these functions, they restore the default input or output channel (stdin, stdout), respectively. ................................................................ .ic(a) [Input-Channel] Open the file named in the string "a", link it to an input channel, and return that channel. Opening a non-existent file is an error. ................................................................ .l(a) [Load] Load the content of the file specified in the string "a" as if typed in at the interpreter prompt. Klong will try the names "a", a,".kg", and both combinations in all directories specified in the KLONGPATH environment variable. Directory names in KLONGPATH are separated by colons. When KLONGPATH is undefined, it defaults to ".:lib". A program can be loaded from an absolute or relative path (without a prefix from KLONGPATH) by starting "a" with a "/" or "." character. .l will return the last expression evaluated, i.e. it can be used to load the value of a single expression from a file. ................................................................ .mi(a) [More-Input] This function returns 1, if the From Channel is not exhausted (i.e. no reading beyond the EOF has been attempted on that channel). When no more input is available, it returns 0. This is a "negative EOF" function. ................................................................ .module(a) [Module] Delimit a module. See MODULES, below. ................................................................ .oc(a) [Output-Channel] .ac(a) [Append-Channel] Both of these functions open a file named in the string "a", link it to an output channel, and return that channel. The difference between them is that .oc truncates any existing file and .ac appends to it. ................................................................ .p(a) [Print] Pretty-print the object "a" (like Display) and then print a newline sequence. .p("") will just print a newline. ................................................................ .r() [Read] Read a single data object from the currently selected input port and return it. The object being read may be an atom or a list. When it is a dictionary or list, the input may span multiple lines. ................................................................ .rl() [Read-Line] Read a line from the From Channel and return it as a string. If there is a line separator at the end of the line, it will be stripped from the string. ................................................................ .rn() [Random-Number] Return a random number between 0 and 1. ................................................................ .rs(a) [Read-String] .rs is like .r, but reads its input from the string "a". It is intended for the converting sequentialized compound data objects, such as lists, arrays, and dictionaries, back to their internal forms. ................................................................ .sys(a) [System] Pass the command in the string "a" to the operating system for execution and return the exit code of the command. On a Unix system, the command would be executed as sh -c "command" and an exit code of zero would indicate success. ................................................................ .tc(a) [To-Channel] See [From-Channel]. ................................................................ .w(a) [Write] .d(a) [Display] .d and .w both write "a" to the currently selected output port. However, .w writes a "readable" representation of the given object and .d pretty-prints the object. The "readable" output is suitable for reading by .r. For most types of object there is no difference. Only strings and characters are printed in a different way: Object | .d(Object) | .w(Object) ---------------------------------------- 0cx | x | 0cx "test" | test | "test" "say ""hi""" | say "hi" | "say ""hi""" For some objects, there is no readable representation, including functions, operators, the undefined object, and the "end of file" object. A symbolic representation will be printed for those: :nilad, :monad, :dyad, :triad, :undefined, :eof. None of these functions terminates its output with a newline sequence. Use .p (Print) to do so. ................................................................ .x(a) [Exit] Terminate the Klong interpreter, returning exit code "a" to the operating system. "a" must be an integer, but the range of "a" may be further limited by the system. ---------------------------------------------------------------- SEMANTICS : VARIABLES ---------------------------------------------------------------- .a [Arguments] This variable holds a list of strings containing the command line arguments passed to a Klong program. ................................................................ it [It] In interactive mode, "it" always holds the value of the most recent successful computation. See also: INTERACTION, below. ................................................................ .f [Function] The variable .f is always bound to the function that is currently being computed, so it allows you to write anonymous recursive functions: {:[0=x;[];1,.f(x-1)]} Note that .f is lexically bound to the innermost function, so {:[@x;0;1+|/{.f(x)}'x]} ^^^^^ would diverge. (But this effect is achieved here by unnecessary eta expansion; {:[@x;0;1+|/.f'x]} would work fine.) ................................................................ .e [Epsilon] .e is the smallest value that can be represented by the mantissa of a real number. Its actual value is implementation-dependent. On a 9-digit implementation, it would be 0.000000001 (1e-9). The "~" (Match) operator considers two real numbers "a" and "b" to be equal, if .e>#a-b. Note that increasing .e will decrease the precision of the built-in math functions. This can be useful to speed up heavy calculations at the cost of reduced accuracy. ---------------------------------------------------------------- SEMANTICS : MODULES ---------------------------------------------------------------- Klong's module mechanism is extremely simple. Its only goal is to protect mutually dependent definitions inside of a module from redefinition. It also allows to create variables that are local to the module. A module begins with .module(:name) where :name names the module. A module ends with .module(0) Here is a sample module. It will be rewritten internally as follows: .module(:foo) a::1 a`foo::1 g::{a} g`foo::{a`foo} f::{g()} f`foo::{g`foo()} s::{a::x} s`foo::{a`foo::x} .module(0) Note that x`y is not a valid Klong symbol, it is just a notation used by the interpreter to indicate that "x" is a symbol of "y". Behavior is as follows: Redefining any of the variables "a", "g", "f", "s" after the end of the module will not change the values of those variables inside of the module. E.g.: a::2;f() --> 1 g::0;f() --> 1 The function "s" will only affect the "a" of "foo". If there is a variable named "a" outside of "foo", it will not be affected: a::0 s(2) a --> 0 f() --> 2 So a module just creates a closed namespace that allows you to refactor more complex programs without having to worry about later (accidental) redefinition of local and variables. Modules may not be nested. When loading external modules, they should be loaded *before* opening a new module, e.g.: .l("external") .module(:local) :"..." .module(0) ---------------------------------------------------------------- INTERACTION ---------------------------------------------------------------- ]! command [Shell] Pass the given command to the Unix shell. ................................................................ ]i dir [Inventory] ]i lists all *.kg files (Klong source programs) in the given directory. When no directory is given, it defaults to the first element of the KLONGPATH variable. The ]i command depends on a Unix shell and the "ls" utility (it does "cd dir; ls *.kg"). ................................................................ it [It] This variable holds the result of the most recent successful computation, so you do not have to re-type or copy-paste the previous result. E.g.: {(x+2%x)%2}:~2 1.41421356237309504 it^2 1.99999999999999997 ................................................................ ]l file [Load] ]lfile is short for .l("file"). ................................................................ ]q [Exit] ]q is short for .x(0). However, end-of-file (control-D on Unix) typically also works. ................................................................ ]t file [Transcript] Start appending user input and computed values to the given file. When no file is given, stop transcript. Input will be prefixed with a TAB (HT) character in the transcript file. ================================================================ | This is the end, my friend | ================================================================