Panda Reference Manual

Beta

Panda is a new experimental language, it's in 'beta', and showcase of what is possible, it's not optimized for performance of running programs but aim at optimize and make programming more fun. You've been arned: some functions have simplistic implementations.

This manual

This manual describes the syntax and semantics of the core Panda Language. I want to be terse, and give you this a single document to read on the "bus" possibly offline, without an Internet connection. It will not describe all the functions, types and libraries, as Panda is an extensible language and most of them can be seen as external extensions.

It's somewhat counter to the idea of the Panda language that aims to be interactive and explorative. But some humans who are programmers may prefer this way. Panda's runtime itself contains documentation that is readily available. For the non-human reader - use the source Luke!

Introduction

This specification is informal, I will not use semantic denotations, and in general I will avoid many BNF syntax diagrams. These are not needed for Panda as it tends to be rather small. It does have syntax extensions, most are not needed but are provided for the convenience of the programmer as well as to make it more easy readable. Ultimately, the syntax will go away as I don't necessarily believe in programming using linear text, see discussion on Quora and my Quora answer).

Philosophy

Why create a new programming language? There are so many of them already? Actually, there are, but they are mostly the same, and limited and not practical and "no longer fun". I believe that to do a giant leap forward we need to make programming simpler for simple things, and other things, well, possible. Ultimately, Panda will be programmed not by typing but by program-by-example, refined and aided in an online IDE-style environment.

We can note that many jobs are aided by the usage of a computer, however, while a programmer is programming the computer does nothing except to wait for keystrokes, possibly it does some syntax highlighting. But essentially, we're still programming using tools from the 70s and the computer is mere there to tell as what we did wrong by explicitly asking it (compiling) than to actually ease our programming. IDE:s have not substantially improved. They are bleak re-enactments of the smalltalk and lisp machine environments. What if you had the power of a super computer available to you while you were programming - What would you make it do? Find syntax errors and generate machine code?

Interactive programming is difficult mostly because writing code requires many lines bundled up in lots of syntax and functions, thus to edit we need to have a text editor.

Vision

Features

No-install

No need to install, the philosophy of Panda is that you can use it directly, it was designed to be easy to use, available anywhere. It's hosted on http://pandalang.org.

Functional

Panda is nothing else than an execution environment that calls functions on arguments and schedules calls to functions for the results. A function returns 0 or 1 or several values. This allows functions to act as filters or predicates return 0 or 1 value. Any function that would have used the returned value is not called (fails) if no value is produced. A normal function returns only a single value, but functions may return several values. If they do, they can be said to be generators/iterators. Functions 'behave' synchronous and will wait and only return when a value is available to be returned. These are called asynchronious (LOL) as they don't return immediately. There are various ways of interfacing with javascript functions. In most cases, they just need to be registered in Panda with their type signature.

Functional programming

Panda is not traditional functional programming, it has in principle only functions and all code you write becomes functions. You could think of this as functional programming. However, many functional programmers have deviated from that core and gone off in type-system-land. It's heaven if you want to think about hard problems using symbols, but not always conductive to getting things done.

No recursion

Recursion is when a function calls itself. Panda does not encourage recursion based solution, it also does not directly support it. Mainly because it unnecessarily serilizes of computation instead of expressing intent on higher level. There are other ways to express iterative processes.

Higher-order functions

Panda provides you with a variety of high level operators. Most notably is that map, filter and reduce(fold) is built into the language. Panda is a high-level language, to implement other such high-level operators, you'd use low-level javascript. You'd seldomly need this as most constructs are already available. It's still possible to pass functions around and invoke them.

From 'normal' modern function langauges perspective, one could say that the functions are auto-'lifted', to work on streams, only it's the opposite that's happeing, Panda breaks down the streams and applies the functions to them. Haha!

Monads

One could say that the Panda compiler is just a giant monad factory. The '.' (dot) in Panda can be seen as bind in the SimpleMonad, MaybeMonad, ListMonad etc. But, when writing code in Panda you need not be concerned with this complexity.

Strongly typed

Panda language is strongly typed. This is not to be obnoxious, but rather allow it to be interactive! Each function have a specific type signatures of what argument types are expected and the type of the returned value. Function names can be overloaded. At compile time, the type system is used to resolve which actual implementation of a function to call. Types can have multiple parent types, or none. Thus a sort of 'inheritance' of functionality can be achieved.

Object-oriented

Panda could be considered to be object oriented. All values are 'objects' with methods associate with the types. It has types, super-types, sub-types, uses the 'dot-notation' to call a function on the object. However, functions don't modify the object. The function to be called is choosen by observing the type of the arguments, including the 'dot-value'. In this aspect, Panda is mostly 'point-free'.

Panda does in general not do late binding of type/functions, thus which function implementation to be called is determined at compile time. Many object oriented languages provide 'virtual' methods which allows one type to be used to do the dispatch to actual function implementation at runtime. Currently, Panda does not have this readily available, but it's possible to mimic.

Generic functions

Function signatures can be somewhat generic, in that types of arguments can be constrained to 'the same' type as another argument, even if it's a subtype. This is useful for the return type. Consider a generic add function taking time in ms adding 5, you'd like the return type to still be of type ms.

State

Panda programs doesn't have state in the normal way. It doesn't have variables that can be updated or changed. State is preferably kept outside in a database like Panda Database, or in the browser.

Some state is temporary during a process of calculation, often this can be replaced by higher level operators like an aggregator operator (sum, product, min, max, fold) or by directly using an accumulator.

Interactive

Panda aims at making prototyping and programming fast by instant feedback. It's interactive so that when you type an expression and it's immediately parsed, compiled and then run so that you can see the results. The result is essentially instant, as most expressions aren't very large.

Documentation and help

In Panda functions have documentation strings, these are written at the time the function is defined, together with tests that also then serves as examples of usage of the function/API. Using powerful introspection and reflection this can be searched.

Program by example

Imagine that you want to process, calculate, or program a task, typically you have some data and you want to process it. In Panda this is easy, as everything is just a value that can be selected, inspected, extracted, transformed as well as used to generate other derived data. Just start with the value as an example and explore it, work with it till you get the result you want, then you can generalize it to a function, and store it in the Panda system.

Panda's environment can suggest the next function to invoke based on previous functions and the type of the values you're working with, in some times it can even suggest functions and their arguments by actually taking the actual values into account. For example, if retrieving an HTML page from an URL it can suggest to you that you that you want to extract links, headers, tables, but it won't suggest to extract images as maybe there are not images in the page you're looking at. By basing suggestions on actual data, Panda can do a more customized job of guiding the programmer, so that it rather becomes an exploration instead of abstract programming. At each stage, results are shown and new functions suggested, this effectively avoids bugs as you're testing as you go along.

Extensible

Panda allows extensions of the language and type system, dynamically. During the loading the core system is loaded. Any function can be replaced and overwritten later, either by loading or by just entering a new definition.

Multiple dispatch

Panda functions can be overloaded on their type signatures of the arguments, this frees the programmer from the burden of finding new names for functions that all do the same thing on different types.

Reactive - Data flow

Reactive simply means reacting to changes. Panda is reactive, by using the callback paradigm. In Panda external events are mapped to functions that seemingly wait till a value is received and then the function 'returns' that value. This may happen several times, at any time, each time only processing a single value. Essentially, the functions are composed into a graph where values are passed along branches as they are made available or generated. In database systems implementation this is called data flow. Panda is a dataflow based language.

No control structures

The language by itself has no explicit control structures like for/while-loops or if statements. Instead functions can act as functions/generators/filters/predicates and return a value, can do selection of some values, by either returning the value given, or no value at all. Functions can also return a several values in form of a stream. When a function returns a value it is processed by just passing it to the next function call, as we would normally expect. For example plus(plus(3 5) 2) which gives 10. However, if we use a function returning several values each value in turn is passed through the expression and yields another value.

This may seem like a major limitation, however, I hope to convince otherwise, most looping (map) mostly done over some collection selecting values according to a predicate (filter).

Concurrent

Erlang is another language that is getting lots of interest, in particularly, because it employs a reactive/actor approach to processing external/internal events in form of messages. Each actor there is a process and runs independently of others. Golang has goroutines which are explicitly created. Panda doesn't have processes per se, but each expression typed generates a processing task which will potentially run forever. These expressions in Panda run concurrently, each getting a time slice and performing some work if there is any work pending. This is data driven, and if no data is available, then there is nothing to do. They can sleep and be woken up when data is available. This is the job of the scheduler. In javascript, as in most browsers as well as in nodejs only one piece of the same program code runs at any point of time, so there is no race conditions or unexpected values created. However, this also means, javascript cannot be interrupted and timeshared using the interrupts. If there is a large for-loop being run, all browser/app UI will block and be unresponsive. Panda does not provide loops, instead it schedules closures to be called on these data items and each is executed in turn. Each expression will run for a while, if too many are run, and the wait time between each expression are high, the scheduler will automatically reduce the time slices for all processes. In effect making the execution highly concurrent and keeping the UI very responsive.

Communication

As with Erlang, Panda provides communication infrastructure. Communication takes place by sending messages on named ports. A named port can be local with a simple string name, or global with an URL-based name. Panda provides way to listen/wait for messages as well sending and shouting messages. It provides a simple round-robin allocation of data between several local listeners. This enables some parallel processing/scheduling. If messages are meant to be received by all listeners, they can be shouted. Shouted messages are broadcast, but aren't queued. Normal messages are guaranteed to be received by at least one, and hopefully by at most one listener.

Data storage

Panda provides a simplified persistent storage for data.

Computational Completeness

Panda is not built to be used to implement a C compiler. It could be done, but wouldn't be appropriate. Like SQL isn't computational (Turing) complete, it's still useful. Computational complete means ultimately that you can't determine if your program ever finishes, even for finite input. This is called the Halting Problem. Panda programs work on finite input with almost all operations completing. It generally does not allow for explicit looping, recursion, or "goto", also, there is no way to build explicit circular graphs of data. But it's easy to add primitives that will break this, and a few of them exists.

Browser based IDE

Panda has an interactive command-style IDE running in the chrome browser. It actually runs Panda in the browser, there is no server involved for compilation parsing or running panda, other than explicit communications, or loading remote source code. Data can be inspected and interacted with graphically. These methods are written in Panda and can easily be extended at runtime.

Notation

I'll probably end up using some kind of 'BNF' notation. Suck it up!

Lexical Analysis

Line structure

In Panda a line is a 'multi-expression'. 1 2 3 will return 1 2 3. 1 plus(1 1) 3 will do the same.

Logical lines

In multi-line mode input, such as in input from file or library/wiki/editor lines can be 'continued' by indenting them. I.e., a line following a unindented line is in principle physically concatenated to the previous line before being parsed. This is mainly used in function definitions.

Comments

Comments are lines starting with '#'.

# this is a comment

Blank lines

Blank lines are fine and have no semantic meaning. They don't terminate a logical line, see above.

Indentation

You may indent as you wish, only remember that a logical line must start at the first position on the line and that following indented lines belong to the previous logical line. Blank lines don't break this.

Whitespace

Whitespace is a delimiter. Thus arguments to functions doesn't need to be separated by commas. plus(3 4).

Other tokens

Panda has a kind of parser that does a pattern matching (due to change with new implementation).

Identifiers and keywords

Panda doesn't have keywords, no words are reserved, however, the parser is weak, thus avoiding the few special words may be good. The special form words are: fun, type, where.

Identifiers such as function names and named values/variables are formed by an ASCII letter optionally followed by more letters or numbers. Underscore '_' should not be used. Case matters.

There are some "special forms" in panda used for defining functions, docs, and tests. Ultimately these maps unto functions, possibly with some special quoted parts.

Literals

A literal is taken 'as it is', it stands for itself. It's constant unchangeable data. The main literal types are integers, numbers, strings.

Integers

An integer is a sequence of digits. 123 is decimal 123, negative numbers are written as -123. 0173 is octal for decimal 123, -077 is octal for -63. There is no explicit hex notation, octal or binary notation. Use the 'radix' function to convert between different bases. Numbers in 'other' bases are strings.


# get some help
Panda> /help radix
functions matching 'radix'
 integer.radix( integer radix ) => stringPrint integer using base RADIX (integer, radix, hint)
  string.radix( integer radix ) => integerInterpret it using base RADIX to give an integer (integer, radix, hint)

Panda> /doc radix
... run it yourself ...

# hex
Panda> 255.radix:16
ff
Panda> 'ff'.radix:16
255

# binary
Panda> 42.radix:2
101010
Panda> '101010'.radix:2

Numbers

Numbers are decimals floating point numbers, such numbers contain a '.'. Pi is 3.14.

Currently there is no scientific notation literal syntax. TODO!

Strings

Simple strings are written as 'Hello world!'. Or using double quotes "Hello World!". The other quote character can be used just like this "Hell's Angels" or 'Ho"lles Angelen'. It's also possible to 'quote a quote' using '\' thus 'Hacker\'s Delight' will work. '\n' isn't recognized.

In addition there is an experimental, multi-line interpolating quote sequence:

  ...
  name='bar'
  $"foobar
    fie fum
    barf' quote"
    my name is: $name
    "$
We will use that later. Note that inside these 'long strings' you don't need to quote the simple quotes.

Strings are Unicode (UTF-8).

String functions

This is only a small overview of all string functions. Depending on if you have a regular expression or string you may look at the following functions results to find relevant functions

This lists all functions taking a regular expression. /help regexp This lists all functions in the 'string' category. Category:string .func .doc nl TODO: make a user function to give this result. "functions in category", "describe" action for a category

# - simple extractors
'foobar'.after:o              -- 'bar'
'foobar'.before:b             -- 'foo'
'foobar'.between('fo' 'ar')   -- 'ob'

'foobar'.butfirst             -- 'oobar'
'foobar'.butlast              -- 'fooba'
'foo'.char                    ** 'f' 'o' 'o'

'foobar'.first                -- 'f'
'foobar'.last                 -- 't'

# - slice(from [to])
'foobar'.slice:0              -- 'foobar'
'foobar'.slice:1              -- 'oobar'
'foobar'.slice:5              -- 'oobar'
'foobar'.slice(-2)            -- 'ar'
'foobar'.slice(1,3)           -- 'oo'
'foobar'.slice(1,100)         -- 'oobar'
'foobar'.slice(-5, -2)        -- 'oob'

# - matchers
'foobar'.empty                --
''.empty                      -- ''
'foobar'.notEmpty             -- 'foobar'

# - regexp match
'foobar'.rlike('o')           -- 'foobar'
'foobar'.rlike(regepx('o*'))  -- 'foobar'
'FooBar'.rlike('foobar')      --
'FooBar'.rlike('foobar', 'i') -- 'FooBar'
# - shorthand
'FooBar'./o/                  -- 'FooBar'
'FooBar'./foo/i               -- 'FooBar'

'foobar'.index:bar            -- 3
'foobar'.match('o')           -- 'o' 'o'
'foobar'.noMatch('x')         -- 'foobar'

# - extract
'foobar'.extract(regexp('(o).*(a)')->2     -- 'a'
'foobar'.extract(regexp('(o).*(a)').from   -- 'ooba' 'o' 'a' 1 'foobar'

# - building strings
'a'.concat:b('c' 77 'd')      -- 'abc77d'

# - remove/change stuff
'foobar'.remove:o             -- 'fbar'
'foobar'.subst('o.*' 'x')     -- 'fxxbar'
'foObar'.subst('o.*' 'x', 'i') -- 'fxObar'
'foObar'.subst('o.*' 'x')     -- 'fxObar'
'foobar'.shuffle              -- 'ofarbo' ?

'foobar'.sort                 -- 'abfoor'
'foobar'.split:o              -- 'f' '' 'bar'
' fo   bar   '.trim           -- 'fo   bar'
'foo bar:fie, fum'.word       -- 'foo' 'bar' 'fie' 'fum'

'Hello this the Panda?'.scrambleWords -- 'Hlelo this the Pdana' ?

'ab'.repeat:3                 -- 'ababab'
'abba'.iota('citta')          -- 'a' 'b' 'c'
'abba'..'citta'               -- 'a' 'b' 'c'
'a'..'c'                      -- 'a' 'b' 'c'

string.bold(string)->html
string.bold(regexp)->html
string.nonl

string.lower
string.upper

html.bold(string)->html
html.hilite(string)->html
html.nohtml
string.quote(char)->string
html.unquote->string

text.html->html
text.line->line
html.text->text
string.text->text

'A'.code                      -- 65
'ABBA'.code                   -- 65
65.char                       -- 'A'

'a'.inc                       -- 'b'
'c'.inc                       -- 'b'
'a'.plus(5)                   -- 'f'
'abba'.plus(5)                -- 'abbf'

MAX{{'foo bar fie fum'.word}} -- 'fum'
MIN{{'foo bar fie fum'.word}} -- 'bar'

freq{{'foo bar fie fum'.char}}.from=>kc->count kc->key nl
-- 3 f 
-- 3 
-- 2 o 
-- 1 b 
-- 1 a 
-- 1 r 
-- 1 i 
-- 1 e 
-- 1 u 
-- 1 m

TODO: better interface to freq...

Operations

Panda has some things which have the seemingly syntax of an operator, but these are notation for either auto-quoting of arguments and/or translated function names.

Here is the short list of these pseudo-operators:

: / -> = => |

Delimiters

The following tokens are delimiters:

<spc> . , ( ) { } {{ }} < > | : ; @ # $" "$ = ==

Space are also limiters. Panda delimits function calls, essentially. Un-indented lines are 'expressions'. In interactive mode (single line input), the line is interpreted and 'all the values are printed'.

Objects, values and types

Data in Panda is in general immutable. You cannot modify an existing number, string, or Tuple. There are functions that return copies with values changed. But the existing value is not changed. Compare it to 3.plus(5), this does not modify 3 to become 8. It returns 8.

By convention, most data types, when defined, specify their parent types. These can be several. All types should reasonably specify some parent types to aid the understand of value of the type. Often types are just aliases for scalar types. Like the type 'weekday' is an scalar, integer 0..6 but has a special display function that can print the weekdays' names.

scalar

A scalar value is essentially a literal value. It can a have a specific type, with specialized functions defined on it.

Tuple

In Panda, functions that creates abstract datatypes typically return typed tuples that contain named elements.

Tuples in Panda, are special. They are an generalization of a list, array, vector, (associative) map/hash/dictionary, struct, and object.

The Tuple syntax is simply using the parenthesis; (1 2 3). It can contain strings as well (123 'foo' "bar" 75). A named elements' tuple is just written (a: 1 b: 2 c: 3). Indices may be numbers (3: 0 2: 1 1: 2 0: 3) which is the same as (3 2 1 0).

Tuples are used in place of struct/objects in other languages. Here for example we represent a players card as (value: 3 suite: 'hearts'). There is no typed constant constructor for specific user defined types. This is managed by just creating a function that returns a tuple of specific named type.

Single non-named values are positional, the notations can be mixed. The latter overrides the earlier. (0 1 2 3 0: 'zero' 2: 'two'), this is equivalent of ('zero' 1 'two' 3 ). Now, notice that each entry is interpreted linearly in-order as given, thus (0: 'zero' 2: 'two' 0 1 2 3) is same as (0 1 2 3).

Since tuples aren't nestable by positional values, they are considered 'flat'. At their construction time they are automatically 'flattened'. This can be used as an limited templating facility for tuples. (value: 3 suite: 'hearts')=>a (a value: 4), this gives us (value: 4 suite: 'hearts'). Named elements can be nested (1 2)=>a (a a) gives (1 2), however, a=(1 2) (car: a cdr: a) gives (car: (1 2) cdr: (1 2)). Tuples named elements have no specific types, types is an convention of functions. More about that later.

In general you don't need to create lists, arrays, vectors etc of single values, these are better enumerated or generated.

Tuples aren't nestable, they are flat, it's a not a dot-data type or pair. The semantic may surprise you!

Tuples aren't implicitly destroyed, they may be garbage collected by the implementation when no longer are reachable or used.

"Modifying" a tuple

Tuples are immutable. Their values do not change. There are functions that take one version and returns a new version with new values.


'a=' a nl 'b=' b 'c=' c nl 'd=' d nl where
  a=(name: 'Peter' age: 32)
  b=(a age: 33)
  c=(b name: 'Lars')
  d=(a name: 'Pjotr')
Gives this output:
a= ( name: 'Peter' age: 32 ) 
b= ( name: 'Peter' age: 33 ) 
c= ( name: 'Lars' age: 33 ) 
d= ( name: 'Pjotr' age: 32 ) 

Similarly, positional elements can be replaced using the index as name.

Iterating over all keys, or values

To get all names of all elements in a Tuple simply write

(10 20 30 name: 'Jonas').key

and to get the values

(10 20 30 name: 'Jonas').value

or just the integers

(10 20 30 name: 'Jonas').integer

One can also get both as a 'keyvalue's

(10 20 30 name: 'Jonas').keyvalue and to just get the keys, or values (10 20 30 name: 'Jonas').keyvalue.key (10 20 30 name: 'Jonas').keyvalue.value More elaborately: (10 20 30 name: 'Jonas').keyvalue=>kv.key.<b> '=' kv.value nl or t=(10 20 30 name: 'Jonas') kv=t.keyvalue kv.key=>k.<b> '=' k.t nl

Notice how we use the tuple as if it was a function. Mathematically it is. It's a mapping from one domain to another for each key giving it's corresponding value.

Object

An Object is a native (internal) implementation specific value. It's defined by what functions its specific type has defined upon it. This is an easy way to say that it's opaque. In reality it's typically an javascript object returned from a javascript function. It has no specific interpretation. It can be reflected upon and inspected and pretty-printed to some extent.

Objects aren't implicitly destroyed, they may be garbage collected by the implementation when no longer are reachable or used.

Any

This is by convention the 'supertype' of most types. It's not automatically the supertype of your new type. Types are their own 'island'. However, they can be connected to other types, at define type or at a later moment, by adding supertypes.

Functions, methods and other animals

Panda is a functional language. Thus functions play a very big role. Panda comes with a large number of functions. These compromise the language. In general a function may return 0, 1 or more values. By convention we will use the following terms: (simple) functions, generators, filters/predicates, constructors, and attribute.

Functions have a simple template for definition:

fun name(arg1[,...]) type type1[,...]->rettype
  retexpr [where
    expr
    ...]
  [ /is ( pure | property | hassideeffect | async | intermittent | needcleanup | tupleout | cached )
  [ /tag tagname
    ... ]
  [ /doc doc (category[, ...]) ]
    ... ]
  [ /test testid testexpr -> expectedresult
    ... ]

where,

fun
bold words/chacters are required
name
italic words are defined below
[ ]
things enclosed in '[' and ']' brackets are optional
( foo | bar )
things enclosed in '(' and ')' parenthesis indicate choice of one alternative
,...
stands for something followed by a comma ',' and then directly another item of same type that preceded the comma. Please observer, for now, no white spaces before or after the comma!
...
the previous item may be repated manny times.
name
is the function name
arg1
is the first argument to the function
type1
is the type of the first argument
rettype
is the return type name of the retexpr
retexpr
is a single expression calculating values
expr
a single expression
doc
a string of characters terminated by the '(' character, thus cannot contain '('
tagname
a tagname, characters with no whitespaces
testid
an id to identify this test when it fails, a sequence of characters with no whitespaces
testexpr
a string not containing ' -> ' which is an expression evaluated by Panda to give expectedresult
expectedresult
a string represenation to be matched to the evaluated testexpr

These terms are further defined informally later. For now see the examples.

Function names

Many functions are named after what they return, not as much as what they do. I.e., prefer singular nouns to name them instead of active verbs, unless they cause side-effects. 3.sqr returns the square of 3 which is 9. http://yesco.org .html returns the html of the webpage that the URL is pointing to. func('foobar').func returns the functions called by the 'foobar' function. Names are singular even if they return a stream of values, as the evaluation and usage context is singular.

Simple functions

Here I'm only going to initially present a few functions: plus(3 4) gives 7, minus(10 3) gives 7, times(10 3) gives 30. sqr(4) gives 16.

Generator (function)s

A generator is a function that returns 0, 1, or more elements. A very simple function that enumerators integers is called 'iota'. iota(1 3) will give the 'stream' of values 1 2 3.

This is Panda's 'loops', so to say. Enumerate what you want to loop over and then perform the action on each item.

Filter (function)s

A filter function generates 0 or 1 value. If the filter as a predicate is 'true' it'll return the given value. It may take 1 or more value as input.

Consider the filter function 'odd'. It will only return odd integers. So 3.odd gives 3, whereas 4.odd gives nothing! iota(1 5).odd gives the filtered stream 1 3 5, and iota(1 5).even gives 2 4.

iota(1 10).sqr.odd gives the filtered stream 1 3 9 25 49 81.

iota(1 10).equal(3) gives only 3. iota(1 10).gt(5) gives the values from 1 to 10 greater than 5, which are 6 7 8 9 10.

Sometimes we call a filter function a predicate, as it either selects a value or it fails.

This is Panda's 'ifs', so to say. It can be used as selection from a stream of data, doing short circuit evaluation for each predicate. Each term, if it fail to generate a value, will fail and it'll "go back" and try the next value.

Constructor function

A construction function is just a function that return a value of a certain type. Mostly they have the same name as the type of the value they return. For example we previously showed how to represent a Card by (value: 3 suite: 'hearts'). By making a function that returns a tuple and we say that the type it returns is a 'Card' we're essentially done!

Attribute function

An accessor or attribute function will return a value or attribute from an Object or Tuple. In the case of the Card we have two attributes, so it would be reasonble if 'c' was a card that we could get the value by just typing c.value 'and' c.suite to get both the value and the suite name. We just define two such functions ourselves.

Aggegation functions

These are defined to work on an iterative process by eating a stream of values and finally output the aggregated result or final value. To implement aggegation functions, you'd typically use Javascript. You need only provide 3 functions. An intializing function returning initial state; A function that takes old state, new value and returns a new state; Finally, a function that given the state returns the final value. sum{{1..6}} gives 21 whereas product{{1..6}} gives 720.

Note that a functions' implementation can have internal state as long as it's predictable what it will return it's still 'functional' and pure. For example take the transitive closure function; It will call another function on a given argument and for each argument it returns it will apply the same function again. When a value is produced it's outputted to the next function. However, it will continue applying the function on successive value as long as new values are produced. If a value has already been seen it will not be outputted and not used anymore.

An example: 'Peter'.butlast will return Pete, by applying the function butlast on successive values we will generate all prefixes. This is how to do that 'Peter'.trans(func:butlast) it generates Peter Pete Pet Pe P which are all the prefixes. Using 'func:butfirst' will generate all the suffixes.

Function /is

Functions are tagged according to their inate properties. Think of it as compiler directives. If the compiler knows more about a function then it can take better decisions when generating code. For example, if untrusted code is received and compiled then it can refuse to compile functions that will update local environment, or have sideeffects and do IO or update the UI. The current properties we tag functions with currently are:

pure
is functionally pure,no side effect, same input always give same output
property
mark function as a property/attribute accessor, this effects it's position in the doc
hassideeffect
the function has a sideeffect, as in sending message, update DOM, or local environment
async
the function will not directly return results, thus must use callbacks for code generation, usually it means it'll send a request and wait for a response
intermittent
the function is async as well as may return results once in a while, depending on external events, most likely it'll return many times
needcleanup
TODO: implement cleanup for functions that hassideeffects
tupleout
Experimential: a kind of multiple return/matching of arguments, works like prolog, not general, currently limited, see source code.
cached
Experimential: for inputs, the generated outputs can be cached locally in the browser, for this sessions. Useful for pure functions that are expensive to calculate. This is essentially memoization. Currently, limited. See source code.

Function /pure /intermittent /hassideeffect /needcleanup

This property can be set, when you're sure a function is pure. It may/should only be set if you're really, really sure! It's mostly set on javascript implementation functions where the compiler can't infer it's pureness. For Panda defined functions, this is inferred: Any function defined in Panda that only uses pure functions is pure itself. You can see a list of all pure functions by running func().pure=>f f.panda {{f.func.

  • }}.unsafeHtml nl

    Functions that call other intermittent functions are intermittent. Same for any that hassideeffect or needcleanup.

    .

    Function /tag

    This is a generalization of the '/is' and '/doc ... (category, ...)' construct. Allows the user to tag functions arbitrary with names, and later search/find functions with this tag. However, tags have no semantic effect on compilation.

    Variables, named values

    Panda doesn't have variables, they don't vary, you can't change them. It does, however, have labeles or named values. a=3 plus(a a) gives 6. Panda has two ways of naming values a=3 4=>b, the difference being that a=3 evalues to no value at all and 4=>b gives the value 4. Thus you can write 3=>a.plus(4)=>b. The reason to have a 'forward-assignment' operator is to ease reuse of value. In most cases value don't need to be named as they are only used once.

    Functions properties

    Execution model

    We already touched on some simple values, lines. Now we will give a bit more clear definition of what an expression is as well as what we call 'multi-expression', including named values. We explain how evaluation works by examples.

    Function calls, object orientation

    A simple expression is just 42. We have function calls as in plus(3 4). Panda tries to simplify as much as possible and avoid the 'parenthesis from hell'-problem of lisp. Take the nested function call example times(plus(3 4) 2), it's perfectly legal, but somewhat inconvenient, as it's more difficult to type and if we started with 'plus(3 4)' we need to go back to the beginning of the line to add 'times(' before all and then go to the end and add ' 2)'. If we do this with many nested functions it's rather confusing. Instead, in Panda we introduce the point-free notation, which incidentially use the '.' (dot/point) operator. Haha! This is simply a convention, albeit the same as used in object oriented programming. 3.plus(4) evalutes the value 3, then 4, then adds them up. It's equivalent and rewritten as plus(3 4). Now, this applied on the whole expression shows its utility: 3.plus(4).times(2). Now, agreeably, for math this may not be the most efficient notation, but it simplifies the issue of operator preceedence rules, as we don't have any. it's purely 'left-to-right' and easy to build on.

    A line / multi-expression

    "A line" in Panda speak is what we input interactively. We've already seen an example like 1 2 3. It will just print the values 1 2 3. 1.plus(2) 3.plus(4) gives 3 7. Technically the expression outputs all the values.

    1 2 3 nl 1 2 nl 1 nl will return something with 'newlines' in it. 'nl' is just a label of a string containing '\n'. Thus this will display:

    1 2 3
    1 2
    1
    

    The non-existing loop construct

    Panda doesn't really have any control syntax constructs. It doesn't even have explicit loops or if:s!

    Wait a minute!... How can I use this piece of...?

    Well, let me show you. We'll use the generators!, remember that iota(1 3) gives you the stream of values 1 2 3.

    Using some sugare '..' is just convenient syntax for 1..3, it's easy to read it as '1 to 3'.

    1..3.plus(4) gives you the stream of values 5 6 7.

    Ok...? What? 1..3 gives an array? No!

    1..3 nl will give three lines with a number on each line. Try it out!

    Now what happens if we combine two of these 'loops'?

    1..3.time(1..3) will give you values of a limited multiplication table!

    Now, you have enough information to actually create a multiplication table on your own!

    1..3=>a '*' 1..3=>b '=' times(a b) nl will pretty-print this table for you.

    1 * 1 = 1 
    1 * 2 = 2 
    1 * 3 = 3 
    2 * 1 = 2 
    2 * 2 = 4 
    2 * 3 = 6 
    3 * 1 = 3 
    3 * 2 = 6 
    3 * 3 = 9
    

    How to understand the loops

    1..3.plus(100) nl can be seen as simple generating the following programs
    
    1.plus(100)
    2.plus(100)
    3.plus(100)
    

    Each being run in the order generated.

    Taking the more complex 1..3=>a '*' 1..3=>b '=' times(a b) nl this will 'generate' the following programs

    1=>a '*' 1..3=>b '=' times(a b) nl
    2=>a '*' 1..3=>b '=' times(a b) nl
    3=>a '*' 1..3=>b '=' times(a b) nl
    
    which becomes
    1 '*' 1..3=>b '=' times(1 b) nl
    2=>a '*' 1..3=>b '=' times(a b) nl
    3=>a '*' 1..3=>b '=' times(a b) nl
    
    and, then...
    1 '*' 1=>b '=' times(1 b) nl
    1 '*' 2=>b '=' times(1 b) nl
    1 '*' 3=>b '=' times(1 b) nl
    2=>a '*' 1..3=>b '=' times(a b) nl
    3=>a '*' 1..3=>b '=' times(a b) nl
    
    and
    1 '*' 1 '=' times(1 1) nl
    1 '*' 2 '=' times(1 2) nl
    1 '*' 3 '=' times(1 3) nl
    2=>a '*' 1..3=>b '=' times(a b) nl
    3=>a '*' 1..3=>b '=' times(a b) nl
    
    1 '*' 1 '=' 1 nl
    1 '*' 2 '=' 2 nl
    1 '*' 3 '=' 3 nl
    2=>a '*' 1..3=>b '=' times(a b) nl
    3=>a '*' 1..3=>b '=' times(a b) nl
    
    # 1 * 1 = 1
    # 1 * 2 = 2
    # 1 * 3 = 3
    2 '*' 1=>b '=' times(2 1) nl
    2 '*' 2=>b '=' times(2 2) nl
    2 '*' 3=>b '=' times(2 3) nl
    3 '*' 1..3=>b '=' times(3 b) nl
    3 '*' 1..3=>b '=' times(3 b) nl
    3 '*' 1..3=>b '=' times(3 b) nl
    
    # 1 * 1 = 1
    # 1 * 2 = 2
    # 1 * 3 = 3
    # 2 * 1 = 2
    # 2 * 2 = 4
    # 2 * 3 = 6
    # 3 * 1 = 3
    3 '*' 1..3=>b '=' times(3 b) nl
    3 '*' 1..3=>b '=' times(3 b) nl
    
    etc...

    The non-existing if syntax

    We've already presented filter functions. These work as well as a simple if with no branching, we can use them to select values. To only print squares of odd numbers 1 to 10, we can first just write 1..10 to verify what we start with:

    1 2 3 4 5 6 7 8 9 10
    Then we select only odd numbers 1..10.odd
    1 3 5 7 9
    and then square them 1..10.odd.sqr
    1 9 25 49 81

    Let's say we only want to find the divisors of 42. 42=>p gives 42. 42=>p 1..p=>a nl gives a long list:

    42 1
    42 2
    42 3
    42 4
    ...
    42 42
    

    We know that 6 is a multipland so let's try it out... 42=>p 1..p=>a a.times(6) nl

    42 1 6 
    42 2 12 
    42 3 18 
    42 4 24 
    42 5 30 
    42 6 36 
    42 7 42
    42 8 48 
    42 9 54 
    42 10 60 
    42 11 66 
    ...
    

    To only see that line we can test if the multiplication is equal to p 42=>p 1..p=>a a.times(6).equal(p) nl. This selects only one line.

    42 7 42
    

    Now let's replace 6 with 1..p so we have 42=>p 1..p=>a a.times(1..p).equal(p) nl

    42 1 42 
    42 2 42 
    42 3 42 
    42 6 42 
    42 7 42 
    42 14 42 
    42 21 42 
    42 42 42 
    
    Let's print out the second value/multiplicand too: 42=>p 1..p=>a a.times(1..p=>b).equal(p) b nl
    42 1 42 42 
    42 2 42 21 
    42 3 42 14 
    42 6 42 7 
    42 7 42 6 
    42 14 42 3 
    42 21 42 2 
    42 42 42 1 
    

    We now have a very simple expression that is close to giving is just the factors, so let's simplify: p=42 1..p=>a where(a.times(1..p=>b).equal(p)). Notice two big changes, we use the 'silent assignment' p=42 that doesn't return/print any value, and we've added a where(xxxx) function call. The where function doesn't return any value but it doesn't fail. Technically it returns a value of type None that is ignored. But if the expression passed in doesn't give a value it doesn't get called, thus this is a way to select what we want the whole expression to return. We have:

    1 2 3 6 7 14 21 42
    

    Typing equal is consistent however, we provide the short syntax '==' p=42 1..p=>a where(a.times(1..p=>b)==p).

    Expressions "more formally"

    This is summarily description of how we define expressions in Panda, using semi-formal BNF.

    
    line             ::= multi-expression [ where multi-expression ]
                       | /function-name [nonspace-string ...]
                       | fun function-name(argument1[,...]) type ...(see above)
    
    multi-expression ::= expression ...
    
    expression       ::= ( expression )
                       | variable-name
                       | ( integer | quoted-string | tuple )
                       | function-call
                       | [ noexp ... ] expression [ noexp ... ]
                       | special-syntax
    
    special-syntax   ::= expression=>variable-name
                       | expression->tag-name
                       | expression@attr-name
                       | expression/function-name
                       | binding
                       | {{ expression ... }}
                       | aggregator-name{{ expression ... }}
                       | expression/fold-function
    
    binding          ::= symbol: expression
                       | string-expression : expression
    
    tuple            ::= ( expression ... )
    
    noexp            ::= ( assignment | where( multi-expression ))
    
    assignment       ::= name=expression
    
    function-call    ::= function-name( parameters )
                       | expression.function-name[( parameters )]
                       | function-name:nospace-string[( parameters )]
    
    quoted-string    ::= ' char... ' | " char... "
                       | $" char... "$ | $' char... '$
                       | :nospacechar...
    
    parameters       ::= multi-expression
    
    nonspace-string  ::= a string that has no spaces in it
    

    User defined functions

    Panda provides a 'fun' way to define functions. It's a special syntax, but don't worry, it's basically just a function call!

    fun double(a) type integer->integer
      plus(a a)
    

    In essence, we just say 'fun' the name of the function and some 'type'-fluff, then the expression it should return. Using this function 1..3.double gives 2 4 6. This is how we define a simple function. One value in, one value out.

    The 'type'-fluff is currently required but will in many cases be inferred and suggested.

    Now we can define 'factor' it takes a number (42) and give all it's factors.

    fun factor(p) type integer->integer
      1..p=>a
      where p==times(a 1..p)
    
    Here we introduced a simplified syntax, if the 'where' is the rest of the expression we can omit the parenthesis around its arguments. Now call the function by 42.factor gives 1 2 3 6 7 14 21 42. Factor can be said to be a generator function.

    We notice that the generator function is kind of slow. Try using a larger number... We know its quadratic. Can you improve it? Well, it should be linear. By using the 'mod' function it becomes linear

    fun factor(p) type integer->integer
      1..p=>a
      where p.mod(a)==0
    

    A filter function

    If factor was a filter function it would test if a certain second value is a factor of the first and then return the first if that is the case.

    fun factor(n, f) type integer->integer 
      n
      where n.mod(f)==0
    

    A constructor function

    Let's create a card constructor:

    fun card(val, suite) type integer,string->Card
      (value: val suite: suite)
      /doc Generate the VAL of SUITE (card)
      /test it 3.card('hearts') -> (value: 3 suite: 'hearts')
    

    This defines a "constructor" function that returns a Tuple that we say is of type 'Card'. The documentation string enables us to generate a nice doc string like: "Generate the 3 of hearts." from 3.card('hearts')

    An attribute/property function

    fun value(c) type Card->integer c.get('value') will work like card(3 'hearts').value and it gives 3.

    fun suite(c) type Card->string c.get('suite') will work like card(3 'hearts').suit and it gives hearts.

    Note: instead of using the function c.get('suite') it's possible to use the equivalent syntax shorthand of c->suite, the benefit is not having to quote the string.

    Documentation

    Panda integrates documentation for all functions, let's add one to our new function.

    fun factor(p) type integer->integer 
      1..p=>a 
      where p.mod(a)==0 
      /doc Gives factor of P (math, mine)
    
    fun double(a) type integer->integer 
      plus(a a)
      /doc Double it (math, mine)
    

    Documentation is written as a simple sentence, either referring to a constant parameter by it's upper case name equivalent, or just say it if there is only one (first/dot) parameter. The reason for this is to make the [describe] button give nice description. You can now also find the documentation of 'mine' functions by /help mine.

    functions matching 'mine'
      integer.double => integer  Double it (math, mine)
      integer.factor => integer  Gives factor of P (math, mine)
    

    Testing

    Panda has a built in testing frame-work.

    fun factor(p) type integer->integer  
      1..p=>a  
      where p.mod(a)==0  
      /doc Gives factor of P (math, mine)
      /test it 6.factor -> 1 2 3 6
      /test 42 42.factor -> 1 2 3 6 7 14 21 42
      /test err 7.factor -> 1 3 6
    
    Defining/loading this function will enumerate the tests and run them and compare the given output to the expected output.
     [Func:factor_bf__integer_integer] it = spacediff 42 = spacediff err = failed
    

    This is not something magical. What's going on behind the scenes is that the display function for the 'DefinedFunc' type, a subtype of 'Func' enumerates all the tests, and runs them giving a pretty output, reporting any errors.

    Reflection

    func('factor') will find our function. It has a 'test' attribute/function that returns tests func('factor').test and finally to run them func('factor').test().run.

    This is just a small test of reflective capabilities of Panda, more about that in the Reflection section.

    Returning a constant list - generator function

    All functions will generate all values generated. fun nums(n) type integer->integer 1..n will generate n numbers when called: 5.nums gives 1 2 3 4 5.

    But let's say we want to generate a list of constants, since Panda doesn't have a direct syntax of actually writing a list we write a function that generates this list of constants. To do this we explicitly call a function called 'out'. Out is essentially a 'yield' or multiple-'return'-many times from the same function. You can also see it as a 'callback' function that is called once for each value 'returned'. Normally, there is only one expression, thus the first value would be returned automatically.

    fun phone() type ->string
      out('159188')
      out('175991')
      out('017130762')
    

    We simply call it by phone() nl... In general, most list of value should could be specified in a data file or elsewhere thus, the need of these kind of constructs are not needed as much.

    The built-in function named 'or' that does something similar, it does not create a list, it returns a stream of constant values. or(1 2 3) will return the stream 1 2 3. It's typed on the first argument, thus, you can use the values correctly: or(1 4 9 16).sqrt which gives... Please notice that or(1 2 3..6) probably does not give what you first might think! This is the reason for having the explicit 'out' function.

    .

    Function call resolution

    Function calls in Panda are strict

    Advanced programming

    List comprehension

    Panda doesn't need list comprehensions. List comprehension is a compact way to generate a set of values from another set of values. For example in python you would say things like:

    x*y for x in range(10) for y in bar(x))

    In Panda you'd just do this

    0..9=>x.times(x.bar)

    As you can notice, in Panda you can easily describe the code as "for the numbers 0 to 9 named x, multiply it with each of the values from bar(x)".

    You could make it look more pythoneske, naming each item

    x=0..9 y=bar(x) times(x y)

    Operators

    We've previusly said there are kind of operators in Panda. They are really just 'alternative' syntax and mapped to function calls. Here we walk through most of them:

    Colon initializer auto-quoting

    http://yesco.org
    This is a natural syntax for URLs, it's a kind of auto-quoter intializer function
    http("//yesco.org")
    This is what it's rewritten into

    Alternative string parameter passing

    concat('foo' 'bar')
    this is fine
    'foo'.concat('bar')
    this is an alternative
    'foo'.concat:bar
    this is even simplier
    a='foo' b='bar' concat(a b)
    using variables
    a='foo' b='bar' $"$a$b"$
    using long string with variable interpolation

    Command style function calls

    For many interactive commands we need to pass a parameter quoted, like function name, so Panda provides a command syntax to simplify interaction.

    edit('circle')
    this is a command function that will load the circle example code in the editor
    /edit circle
    this is using the command syntax that quotes all the non-number arguments

    Types

    Panda is a strongly typed language. This means it requires the type of everything at compile time. Named values have types and cannot be changed. I.e., values are immutable. Panda is at the same time 'Object Oriented'. This may seem contradictatory. Objects are not updated. Rather think of Panda programs not having state at all, and no IO, but rather than Panda is executed by an environment that has the state. It has functions to read the state and modify the state, these can be seen as messaging to the outside world. The DOM of the browser is one such state, as is the localstorage, filesystem, the web.


    TODO: move part of this up?

    Type hierarchy

    Panda's type system is tangeable. You're able to interactively define new types and modify existing ones to a certain degree. As discussed previously, a type can have several, or even no parent types. A type may also has children types, these are derived from the parent relationships. The type system is not purely hiearchical - it's rather a number of directed graphs. By convention, a type is it's own parent type and it's own child type. This simplifies some reflection and function resolution methods.

    To get the type object you write Type:number this will just return an object of that specific type if it exists. Type:number .describe will allow you to see much more information that is automatically inferred.

    To see what function are available on Type objects you of course write Type:Type .describe. Most notable are .describe .parent .child .ancestor .descendant.

    Type casting?

    Panda doesn't have any explicit time casting for most types. Only sometimes are values promoted to strings. This may or may not be useful. Many values of different types may have a string function, this is a limited type of 'casting'.


    TODO: document more clearly, is the 'TYPE.string' function called on these values?

    Types often have a 'constructor' or initialize function. For simple values this may just be lower case type name. One of the simplies ones are '42.1'.number that will convert a string to a number. If the string is not a correct number, or unparseable, nothing is returned. It's failed, thus yields no value". Thus such functions may be used also as a kind of validators. For example '42.1'.integer does not get an integer, if you want rounding semantics and accept a floating point number, this needs to be explicit: '42.1'.number.round which gives the integer 42.

    Sometimes you need simple 'casting' that doesn't convert the values but just changes it to a subtype or supertype. You can create such a function yourself simply by fun asNEWTYPE(old) type OLDTYPE->NEWTYPE old. Notice that the return type is taken at 'facevalue' and the function and Panda simply is 'forced' to believe it's the return type. More often you can add some predicates to test the value received. For example, '3'.integer gives 3. Where as 'a'.integer does not give anything.

    Here are all given 'asXXX' functions: func:^as .pp nl

    Special types

    All types are created equal, except possibly the NONE type, the Func, and TypeType ;-). Types with literal code form may also be considered special. This means that all other types are 'user-defined'. What makes these three types different is that the compiler knows about them. For a variable of the Func type it knows that it can be called like a function, thus provides the same syntax as a normal function call, if the function name is actually a variable in the expression. This sounds strange but is how most languages work. f=func:plus_bbf__integer_integer_integer f(3 4), here we look up one function using its name and then call it. If the compiler had no special support we would have to write f.call(3 4). The type Type is special in that the compiler reasons about types of expressions and uses this to resolve which function to be called from the name given.

    The NONE

    The NONE type is special as it doesn't return a value. Syntactically/semantically a function returning a NONE type doesn't return a value. There are three functions returning NONE in panda; First, ignore that takes Any and returns NONE: plus(2.ignore 3 4 5.ignore)out that 'yields'/returns a value. out function itself doesn't not return a value. This is need as function bodies are wrapped in an implicit out; Finally, where does not return a value. It's a normal function, that just doesn't return any value. It does however have a special syntax as mentioned before.

    integers, numbers, strings, tuple, binding

    These are special in that they have a literal form in the Panda language. The closest built in facilty is http://pandalang.org which just a simplified calling convention (reading function) for autoquoted string and http() function invocation. It's equivalent to http('//pandalang.org').

    User defined types

    All other types in Panda are implemented natively (in javascript) or using Panda itself. To create a type, just make a function that returns it. fun apple(a) type string->apple a. This is a function that takes a string and returns an 'apple'. apple:astrakan will return 'astrakan'. We can now write a function that takes only an apple and makes a tart fun tart(a) type apple->tart concat('apple-tart-using-' a) and call it by apple:astrakan .tartgives 'apple-tart-using-astrakan'. fun orange(o) type string->orange defines an 'orange' type and initializor.

    Now let's say we want to make a juice function, we don't want to write it for each type, thus we want to define it on a fruit type fun juice(f) type fruit->juice concat(f.string '-juice'). We have no way of calling this function as we have no way to make fruits. Apples and oranges are fruits type('apple' 'fruit') and type('orange' 'fruit'), we also define type('fruit' 'typed') and type('fruit' 'string'), this defines two 'parent-types' of the fruit type. Now when we say apple:astrakan the display is astrakanapple! Panda's interactive environment knows the type of everything and it will augument their display value if they are of type 'typed' with the actual type name in super script. Read more about that in the 'Display function' section.

    Union types

    It's easy to create a union type, Panda uses the type 'integerstring' to handle the type resolution of functions like

    
    3.plus:5
    
    3.plus(5)         # does it mean this?
    3.plus('5')       # or this?
    

    We could say that it clearly is meant to be interpreted as an integer. Instead of having the parser make a string out of an integer when an integer is expected we let it parse it to:

    plus(3 integerstring(3))

    The defininition of the type is:

    
    type:integerstring('integer')
    type:integerstring('string')
    

    The type of the function integerstring is 'integerstring', it's both tagged to be an integer and a string. This allows the parser and function resolver to try to first find plus(integer integer) and then it'll try plus(integer string) in this case.

    Please note that this is not like union types in Elm or Haskell. Currently, Panda does not know the runtime types and does not do dynamic dispatch depending on the type. Thus multi-functions are static. This may change.

    no parametric types

    Parameteric types is a way to templetize types. Panda doesn't have type constructions ala Elm or Haskell that does this. However, Panda supports having templetized functions for specialization purposes. This means that a function like fun startsWith(s,what) type string,string->0 s where s./^$what/ will return the same type as was given as it's dot argument. This is useful for subclassed specialized types like 'filename', a matched non-modified filename is still a filename if it's string interpretation .startsWith('logfile-'). This superficially preserves the type of values accross function calls.

    This maybe generalized later. For now just don't build tree structures in Panda!

    Inheritance

    subtypeing, depth, width....
    TODO: write shit
    
    find better word than inheritence? 
    
    write a guide?
    
    - concrete types?
    
    - when to create a super type
      - compatible structures
      - interface, inherit functionality (requires dynamic/virtual methods)
      
    - when to create a sub type
      - mode (DefinedFunc)
      - ms (instead of just integer)
      - extend with more attributes
    
    Julia: Describing Julia in the lingo of type systems, it is: dynamic,
    nominative and parametric. Generic types can be parameterized, and the
    hierarchical relationships between types are explicitly declared,
    rather than implied by compatible structure. One particularly
    distinctive feature of Julia’s type system is that concrete types may
    not subtype each other: all concrete types are final and may only have
    abstract types as their supertypes. While this might at first seem
    unduly restrictive, it has many beneficial consequences with
    surprisingly few drawbacks. It turns out that being able to inherit
    behavior is much more important than being able to inherit structure,
    and inheriting both causes significant difficulties in traditional
    object-oriented languages. Other high-level aspects of Julia’s type
    system that should be mentioned up front are:
    
    julia> type Foo
             bar
             baz::Int
             qux::Float64
           end
    Fields with no type annotation default to Any, and can accordingly hold any type of value.
    
    New objects of composite type Foo are created by applying the Foo type object like a function to values for its fields:
    
    julia> foo = Foo("Hello, world.", 23, 1.5)
    Foo("Hello, world.",23,1.5)
    
    julia> typeof(foo)
    Foo (constructor with 2 methods)
    

    Parametric types

    Display functions

    The Panda environment/IDE does it best to be friendly and configurable. Noticing that displaying values as their plain types is old-school we've introduced the concept of a display function when something is displayed on the screen by the IDE. This is a function that's found using the displayed value's type. This is happening for every value displayed. For example 3 will display 3. However, type this code in:

    fun display(i) type integer->html
    i.string.<font size=42>

    and 99 will display as

    99

    For a more elaborate case of display function have a look at the cards example /edit cards and to load: /load cards. Then type 3.card:hearts

    3

    The Card type by itself is just a Tuple and if we watch it's string represenation 3.card:hearts .string will give (value: 3 suite: 'hearts'). You can inspect a value by long-press the mouse on it's displayed represenation, it'll show some type information as well as allow you to perform some simple functions on it.

    Some noteworthy types and functions

    It's not possible to build a useful language without adding some conventions and ideas. Panda is an exploration in trying to solve some problems with programming langauges as well as make programming more fun. We see that in functional programming types are often used to solve problems and it often gets very intricate and difficult to explain as well as understand, monads etc. In Panda we try to be more pragmatic and practial. Now we will describe some noteworthy functions, types, names, conventions.

    html

    HTML is of special interest as it can be dangerous to display raw text received by a user or data from a file directly inline. Most often it need to be quoted for special characters, such as < and >. Panda provides a simple way to create html tagged data. 'My <name> is:'.html will return a quoted string. Notice, if you're typing the 'My <name> is:' directly in Panda will quote it for you. This can be bypassed by 'My <name> is:'.unsafeHtml which then will display 'My is;'. These are somewhat practical, there are shortcuts that allows you to take a normal string and wrap it in a tag just like this: 'This is a <header>'.<h1< and it gets automatically quoted. If it's already of type html it does not need to be quoted thus can be nested like this: 'This is a <header>'.<h1>.<div style='background:yellow'>

    Func

    Type

    Q

    ID

    Wouldn't it be nice if objects had an ID that could be used to refer back to it? In many languages pointers are used, but betwen runs there is no way to refer to a named object, also when they are printed in logs and on screens they give little information about the actual object and it's content. In Panda we are using the concept of ID. Object of type 'HasId' have two functions defined on them; obj.ID->ID and obj.id->id. The convention here is that the ID is a string of format 'type:id'. For example all the square functions have func:sqr .ID ID:s that are readable so if you in extactly the string as it's returned by the .ID function you get the underlying object. Functions with the upper case name of the type of the object return only the object in question, if there is no such object it will return nothing. Functions with the lower case name works like 'search' functions that may do regexp search to find matching names. Lowercase function name without arguments will often list all objects of that type.

    . For example, dealing with functions try all these: func() func:sqr Func:sqr_bf__integer_integer Func:sqr

    Notice that 'Func' will return only one function if uniquely full named/defined. If no exact match exists, it fails and no value is returned. 'func' on the other hand will return any function that partially matches on it's signature. This Xxx('foo') and Xxx('foo') is a commonly used pattern.

    Functions that are of 'HasId' will be displayed using their ID function per default. This can be overridden by defining a type specific TYPE.display->html function.

    Scheduler

    Panda was initially made to compile to 'plain' Javascript. However, this does not work well with asynchronious processes as well as long loops over many elements, as can be noticed sometimes in web browsers, where if you have a for-loop that takes long time, it'll block that browser tab's UI until it has finished. Panda's code generation therefore changed to generate code that pushes continuations on a per-process stack, which Ahum, we will call Q (Queue). It can be seen as a variant of Threaded Code generation. Basically, each Q is a stack of queues. The 'Q' works on the top stack item getting oldest continuation of the that queue. The continuation can only perform one task and that is to call another Panda function. If the Panda function gives a value another operation may be sent to the Q. Notice how a continuation can push several things on the Q, as well as do it when it pleases, asynchroniously, based on external events, such as timer, IO, callback etc. Each such value will then generate yet other continuations. In principle the order doesn't matter, however, it makes more sense if values are generated in their 'natural order', as would be expected. 1..5 we would expect to give 1 2 3 4 5. In the case of a secondary calculation that takes more time like 1..5.prime for each value 'iota' pushes a continuation on the Q to calucation 1.prime, 2.prime etc. Since we're functional the order of these does in principle no matter. However, we'd normally expect to get the prime numbers in order. 'prime' is a function that we'll write that tests if an integer is a prime number. As we know, the process of testing if a number is a prime number depends on the values 2.prime is as fast as 222222.prime, as it will first try to divide by 2 and since it's divisible it will not generate any value. However, for a value like 33931.prime it may take quite some time, thus other values/processes may finish in different orders thus the order of results could be quite different.

    Constraints

    The scheduler works under the following constraints and conventions, the goals are to make sure UI actions are not blocked and that it makes progress in processing the given Queues/processes;

    Notice that care is given that no function will directly call work that could be delayed or not finish. It just decides what to do next, then stores that and gives over to next one.

    For most operations this is very workable, it's interactive and doesn't block the UI-thread, thus the browser is resonsive. For larger calcualtions these may take longer time, but will eventually generate the answer in due course.

    Data storage - Panda Database

    Panda can persistently store and query stored data, it's a global streamed message log-based database. This means data is not updated but rather new version are added. For most applications, getting the latest version.

    In practice, data is just messages sent on named ports and some are logged by the communication infrastructure. Messages can be replayed from the beginning, explicit save points or from after a timestamp. On top of this persistence an more advanced API has been built. Messages can be simple strings or JSON objects. If a message has an ID field its used to create a fast lookup index for searching and retrieving the last version of an ID'd message. This forms the basis of a very simple rudimentary database system, similarly to datomic in that it's logging based and you can query the history. Actually, internally Panda actually uses a form of Datalog it just doesn't expose the user directly to this.

    See: wiki:pandabase for small database example
    
    Port
    ----
    URL.port->Port
    
    Port.say(Any)->1
    Port.say(Tuple)->1
    
    Port.shout(Any)->1
    Port.shout(Any)->1
    
    Port.listen
    -- 
    Port.url->URL
    
    URL.port->Port
        port()->Port*
    
    Port.listen->Any
    Port.listenTuple->Tuple
    
    Port.listen(filter)->Any
    Port.listen(regexp)->Any
    Port.listen(string)->Any
    
     Port.say(Any)->1
     Port.say(Tuple)->1
    
      Any.tell(Port)->0
    Tuple.tell(Port)->0
    
    Port.shout(Any)->1
    Port.shout(Tuple)->1
    
    Port.local->Port?
    Port.global->Port?
    Port.stored->Port?
    
    Port.flush=>FlushMessage
    
    RawMessage
    ----------
    Port.listenRaw->RawMessage
    
    PortState
    ---------
    PortState.url->URL
    PortState.timestamp->string
    PortState.serverseq->integer
    PortState.pos->integer
    
    URL.state
    URL.state(pos)->PortState
    URL.state(timestamp, serverseq, pos)->PortState
    
    RawMessage.state->PortState
    RawMessage.port->string
    
    PortState.listenRaw->RawMessage*
    
    Database
    ========
    Port.say(TupleWithId)->Tuple
    Port.get(id)->Tuple
    
    Port.get(id)->Tuple
    URl.get(id)->Tuple
    
    --
    
    Port.get->id*
    URL.get->id*
    
    GetPut.url->URL
    GetPut.user->string
    GetPut.id->string ???
    
    getput(URL, user, id)->GetPut
    getput(URL, id)->GetPut
    getput(id, place)->GetPut // local user, default URL/place
    
    
    GetPut.put(Object)->Tuple
    GetPut.put(Tuple)->Tuple
    GetPut.put(scalar)->Tuple
    
    GetPut.getString->string
    GetPut.getTuple->string
    
    -- versioning
    GetPut.get->GetPutData
    
    GetPutData.port->Port
    GetPutData.id->string
    GetPutData.origtimestamp->string
    GetPutData.timestamp->string
    GetPutData.user->string
    GetPutData.tuple->Tuple
    
    GetPutData.put(Object)->Tuple
    GetPutData.put(Tuple)->Tuple **** missing???
    GetPutData.put(scalar)->Tuple
    
    DATABASE
    ========
    # TODO: THESE MAY NOT WORK!!!????
    
    db:foo
    db:foo .id
    db:foo .id:bar
    db:foo .id:bar .get
    db:foo .get:bar
    
    db:foo .id:bar
    db:foo .id:bar .put(Tuple)
    db:foo .put:id(Tuple)
    db:foo .put(id Tuple)
    db:foo .put(Tuple)
    Tuple.put(db:foo)
    Tuple.put:id(db:foo)
    Tuple.put(id db:foo)
    
    db:foo .all
    db:foo .last:10
    db:foo .first:10
    db:foo .after:DATE
    db:foo .before:DATE
    db:foo .limit:10
    
    db:foo .id:bar .history:3 .all                ???
    db:foo .id:bar .history:100 .after:DATE .all  ???
    
    db:foo (id: foo age: 99)
    db:foo (id: foo age: {?.gt(99)})
    db:foo (id: foo Func:old_bf__Student_Student)
    db:foo (id: foo {?.age.gt(55)} )
    db:foo (Func:old_bf__Student_Student)
    db:foo (Func:old_bf__Student_Student)
    
    select s from student s where s.age > 55
    
    func student() ->db:foo db:foo
    student{?.age.gt(55)}
    student().is:old .all
    student().is{?.age.gt(55)}.all
    
    db:foo .all.old.print
    db:foo .all.agegt(55).print
    
    fun agegt(s, y) type Student,integer->Student s where s.age.gt(y)
    
    fun old(s) type Student->Student s where s.age.gt(55)
    db:foo .all.?[.age.gt(99)].print
    db:foo .all[.age.gt(99)].print
    db:foo .all[age.gt(99)]print
    db:foo .all.age?(55).print
    db:foo .all.[?.age.gt(55)].print
    db:foo .all=>t.age.gt(55).ignore t.print
    db:foo .all.tee{t|t where t.age.gt(55)}.print
    db:foo .all.tee{t|t where t.age.gt(55)}.print
    db:foo .all.where{it.age.gt(55)}.print
    db:foo .all.where[it.age.gt(55)].print
    db:foo .all.where{?.age.gt(55)}.print
    
    
    t=db:foo .all
    t.gt(55)
    t.print
    
    db:foo .value:age(77) .all
    db:foo .value:age:gt(99) .all
    db:foo .value("?.age.gt(99)")
    
    
    db:foo .listen
    db:foo .all_listen
    
    

    Panda IDE

    The Panda IDE started out just as a command line in the browser. It 'top'-post your small programs and their results. The last run code is on top. You can just type in one line of Panda code and press return or click the [Run] button and it'll run your code and display the results.

    Help

    Help is readibly available. Just type /help to see help options. Panda, today (2015-04-01), has 1097 functions (count{{ func() }}). It's rather useless to document them all here. Most cases you can find what you need when you need it. You want to do some math, run /help math to list all functions relating to math. You're looking for a sqr function /help square to view the docs of the function's details and example of specific function type /doc sqr

    Tutorial

    Panda has a built in tutorial just run it: /tutorial. Each example is heighlighted grey and by clicking on it it will be parsed, compiled and run, showing the result. There are a number of different tutorials.

    
    /tutorial 0 - introduction to panda - values and output. 
    /tutorial 1 - giving names to values. 
    /tutorial 2 - looping without loops! 
    /tutorial 3 - how to be choosy in panda. 
    /tutorial 4 - everything is a value, so how to not show it. 
    /tutorial 5 - Generating values. 
    /tutorial 6 - to be or not to be. 
    /tutorial 7 - playing with HTML. 
    /tutorial 8 - how to have fun! 
    /tutorial 9 - asking panda about panda - reflection.
    

    Value inspection

    When a value is displayed as a result of an expression you run, you can right-click, or long-press on the value and an inspector will pop up. It will display the type of the value, a reference to get the value for using it in further expressions as well as it's display form as well as it's plain string representation. Specific menu items can be defined per type and these buttons will be displayed, followed by 'dot-functions'. These are functions that only take the object as an argument and then returns a result. The functions' names and documentation are displayed. Simply click on the function name to run the function. The answer will be given inline. This way, having a value, you can explore what functions are avaible and use them directly. This does currently not include functions that take additional parameters.

    Editing functions

    Panda has a built-in 'wiki', it's an embryo for allowing for code editing, it's the easiest way to define functions and try them out. To list all shared code type /edit, it'll display all the 'wikis'. To choose one, click on it or type /edit NAME and a small editor will pop up. It has a save [s] button in the upper left corner that is activated whenever you've changed the code. To run your code, press the [run] button. The [js] button allowing you to type and run javascript, to define native Panda functions for example. This is a easy way to extend Panda with new functionality. For an example /edit audio. This simple javascript code adds a new type 'Play' and some functions on it 'play' and 'wait'. To play 'a flat' for 1000 ms type play().play(831).wait(1000). This audio API is very simple but can be powerful: play().play(831).play(833).wait(1000).play(300).wait(100) that will tremble the first two tones and then play a short darker tone.

    Loading a library

    To use a library you need to load it. For the Panda community stored libraries you just type /load geo, /load weather then you have a variety of commands available /help weather. Try weather:amsterdam, inspect the returned string and notice that it's actually a multi-facetted object with typed attributes.

    Libraries can be external, to load external javascript library use: /loadjs https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js, and to run any string as panda panda('1..3.sqr.lt(15)'). The string could be gotten through an ajax call, be constructed on the fly, or be part of a dom script element that is part of your application. More about that in the next section.

    Extending the Panda IDE

    TODO: how to add menu items, display functions, modes

    Building a stand alone Panda-app

    Panda core libraries are hosted and easily loadable from a single bundled source at http://yesco.org/panda-app.js and example 'dummy' app showcasing different ways of integrating with Panda and the DOM and html is found on http://yesco.org/Examples/panda-app.html. A simple more real app is the http://yesco.org/Examples/chat.html which implements a simple single room chat for many users. The Panda code is only 21 lines of Panda and then a few other things.

    A Panda Web-app

    Inspired by the go web-app Writing Web Applicatios. We will do the similar functionality - a simple wiki app. But as you'll see the amount of code is much smaller.

    To create a web-app we first need the ability to start a web server and return some text on a request. Using the command line Panda, not inside the web-browser!

    8080.port.listen=>req "You asked for ".concat(req.url).tell(req) or unix> ./panda -e '8080.port.listen=>req "You asked for ".concat(req.url).tell(req)' -i

    Ok, so we need a way to load a page from disk and show it: 8080.port.listen=>req './'.concat(req.url).file.text.tell(req)

    Now we actually want to be able to edit too, so we may need two different paths

    u where(
      req=8080.port.listen('^/view/')
      name=req.url=>u.after('/')
      fn=concat('./' name)
      fn.file.text.tell(req)
     )
    
    u where(
      req=8080.port.listen('^/edit/')
      name=req.url=>u.after('/')
      fn=concat('./' name)
      html(
        $"Edit: name"$.<h2>
        fn.file.text.<textarea name='wiki'>.<form submit='/update/$name'>
      ).tell(req)
     )
    

    Contributing to Panda

    Contributions are gladly taken. Panda is open. One reason to keep the core of the Panda small is that almost everything becomes a plug-in function, as Panda is orthogonally incrementially extensible. This allows extensions and libraries to be built on the base, loaded dynamically from anywhere, thus it does not need to be part of the core platform. Panda provides the means to store and edit code online, thus repository services like github aren't needed. Code is stored in 'wiki'-style format thus are easily shared and potentially editable by everyone. This enables faster iterations and code-sharing than having to download libraries and occasionally (like never) update them, and getting broken dependencies.


    Jonas S Karlsson
    Last modified: Thu Apr 9 02:31:23 HKT 2015