Axon Flashcards

1
Q

Axon Overview

A

Axon is a programming language used for scripting within SkySpark. Most functionality can be accessed through Axon:

  • query Folio tag database
  • perform data transformations such as rollups and normalizations
  • creating your own function libraries
  • rules for the spark engine are written in Axon
  • create custom reports for Report App
  • create scripts for data import
  • create background jobs for data synchronization and maintenance

One of the pivotal features of SkySpark is that all queries to the database/analytics engine are full Axon expressions:

// find all the records with kw tag

kw

// same as above

readAll(kw)

// find all recs with kw and read history data for yesterday readAll(kw).hisRead(yesterday)

// find peak kW for yesterday for all kw recs readAll(kw).hisRead(yesterday).hisRollup(max, 1day)

This design allows great flexibility scaling up from simple queries to powerful data transformation pipelines - all using a single general purpose syntax.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Language Overview

A

Axon is a full general purpose programming language characterized as follows:

  • simple: the language syntax is minimal and designed to be fully learned in a single session
  • functional: everything is a function (the language is not object oriented); closures are heavily used by the language and the library to concisely express data transformations
  • dynamic typing: you do not declare types for variables
  • tag oriented: the language is designed to work in conjunction with the Folio database tag model
  • time-series oriented: the language has dedicated syntax and a rich library of functions for working with time series data such as time, date, and date range literals
  • unit oriented: all numbers in Axon and Folio can be annotated with an explicit unit of measurement which is checked and carried through during arithmetic

Axon draws inspiration from Fantom, Lisp, Ruby, Scala, and Haskell. Experienced programmers should be able to pick up Axon very quickly, however, a grounding in functional programming will definitely help.

The Axon Language chapter digs into the syntax of the Axon language.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Func Recs

A

Function records define new top-level functions with the following tags:

  • name: programmatic name of the function
  • func: marker tag to indicate function
  • src: Axon source code for function stored as text/plain bin
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Top-Level Namespace

A

The top-level namespace of a project is defined by:

  • core functions
  • named func records
  • extensions installed by the project

The following functions are useful for working with the top-level function namespace:

  • funcs: list or match functions defined in the top-level namespace
  • func: resolve a function in the top-level namespace
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Qualified Names

A

The name used in a function call can be either qualified or unqualified. Qualified functions specify an explicit library namespace using double colons:

site: :toPoints // qualified in “site” ext namespace
core: :toHex // core functions live in “core” namespace

Unqualified function names are resolved by the implicit namespace which is searched in this order:

  • local namespace of function (parameter/variable names)
  • core function namespace
  • named func records
  • extensions installed by project
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Function Overrides

A

You can override an extension function by declaring a function record with the same name. Functions resolved in the project take priority over functions defined by extensions. Note you cannot override a core function. You can access the built-in function in your override using its qualified name:

geoTz(val) => do

// handle special case

if (val[“geoCountry”] == “RU”) return “Moscow”

// route back to built-in version

return geo::geoTz(val)

end

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Math Operators

Addition

A
=== Addition === num + num \>\> num date + num \>\> date (num must be number of days) dateTime + num \>\> dateTime (num must have duration unit) dateRange + num \>\> dateRange (num must have duration unit) uri + str \>\> uri concat uri + uri \>\> uri concat str + obj \>\> str concat obj + str \>\> str concat
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Math Operators Subtraction

A
=== Subtraction === num - num \>\> num date - num \>\> date (duration must be number of days) date - date \>\> num (difference in number of days) dateTime - num \>\> dateTime (num must have duration unit) dateTime - dateTime \>\> num (difference in number of hours) dateRange - num \>\> dateRange (num must have duration unit)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Math Operators Multiplication

A

=== Multiplication ===

num * num >> num

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Math Operators Division

A

=== Division ===

num / num >> num

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Get and Trap Operators

A

The indexing operator target[key] is a shortcut for the get function. It can be used to index strings, lists, dicts, and grids:

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

str [index]

A

get unicode char from string

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

str[start..end]

A

slice/substring

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

list[index]

A

get an item at zero based index

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

dict[key]

A

lookup tag value by key

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

grid[num]

A

get row at index

17
Q

grid[start..end]

A

slice of grid rows

18
Q

Trap Operator –>

A

The trap operator -> is a shortcut for the trap function. When used on a Dict, it looks up a tag value by name. You can also use the -> operator on a Ref which will automatically resolve the id using readById:

id-\>foo \>\>\> readById(id)-\>foo id-\>siteRef-\>tag \>\>\> readById(readById(id)-\>siteRef)-\>foo
19
Q

Colon Oprators :

A

The colon operator is used to define a variable as name/value binding:

a: 5 b: 3 a + b // yields 8

You can re-assign the value of a variable using the = operator:

a: 4 a = a + 1

The variable must already be defined via the : operator before it can be assigned, otherwise a runtime exception is raised.

20
Q

Scoping

A

Axon uses lexical scoping with closure support. Each function call creates one scope. Note that do blocks, if blocks, or try blocks do not create new scopes. For example consider this code:

if (true) do a: "foo" end return a

In this case “foo” will be returned by a. Once a variable is defined, it is visible anywhere in the function. This model is more akin to JavaScript or Ruby, then static languages like Java or C#.

Each nested function within a top-level function has visibility to its lexically scoped outer function’s variables. This design is known as closures. Here is a simple example:

num: 0 f: ()=\> num = num+10 3.times(f) return num

In this example the function f passed to times is accessing the num variable from the outer scope. The result will be 30. However, a nested function can define a new variable which hides the outer scope:

x: "outer" f: () =\> do x: "inner" return x end return [f(), x]

In the example above the result will be “[inner, outer]”. The variable x inside f hides the outer scope which remains unchanged.

Variable resolution is searched in the following order:

  1. current function’s variables
  2. lexically scoped outer function’s variables
  3. top-level namespace
21
Q

Lambdas

A

Lambdas are function definitions which take zero or more parameters and calculate a resulting value:

x =\> x \* x // parens optional if single param (x) =\> x \* x // but you can use them () =\> "some val" // no params (x, y) =\> x + y // multiple params

You’ll often use the def operator to create named functions:

add: (x, y) =\> x + y add(2, 3) \>\> 5

You can define a default value for parameters using the colon:

f: (a, b:2) =\> a + b
22
Q

Calls

A

You call a function with the () operator.

The number of parameters a function expects is called its arity. You must pass enough arguments to a function to satisify its arity. If function parameters have defaults, then those arguments may be omitted. It is also permissible to pass more arguments to a function - the additional arguments are ignored:

f: (a, b:2, c:3) =\> a + b + c f() \>\> error! f(10) \>\> 15 f(10, 11) \>\> 24 f(10, 11, 13) \>\> 34 f(10, 11, 13, 14) \>\> 34

If you leave off the () operator, then the expression evaluates to the function itself. This is useful for passing functions around to perform higher order functional programing.

23
Q

Dot Calls

A

I

It is common to pipe the results of one function to another function to build up pipelines much like the Unix | operator. Consider the following expression:

c(b(a()))

In the expression above, we evaluate the function a, then pass the results to b, which in turn gets passed to c. We can write the above using the . dot call operator:

a().b().c() a().b.c // may omit parens if no arguments

Here are some more examples:

year(today()) \>\> today().year toStr(year(today())) \>\> today().year.toStr hisRead(readAll(kw), 2009) \>\> readAll(kw).hisRead(2009)
24
Q

Trailing Lambda

A

Many functions are designed to take other functions as arguments. These functions are calledhigher order functions. Consider a simple example:

// default is to sort by alphabetic order list: ["cape", "ape", "batch"] list.sort \>\> ["ape", "batch", "cape"] // we can pass a function to sort by string length list.sort((x, y) =\> x.size \<=\> y.size) \>\> ["ape", "cape", batch"]

This pattern is so common that code can start to become confusing with the nesting parenthesis (much like Lisp code). Axon borrows a pattern from Fantom and Ruby which allows you to pull the last argument outside of the parenthesis if it is a lambda expression:

list.sort() (x, y) =\> x.size \<=\> y.size \>\> ["ape", "cape", batch"]

Note that when using dot calls you must include empty () parens before specifying the lambda argument. The exception to this rule is if the lamdba takes a single argument with no parens:

list: [3, 4, 1] list.map x =\> -x // ok list.map() x =\> -x // ok list.map() (x) =\> -x // ok list.map (x) =\> -x // not ok

More examples:

eachDay(2010-07, day =\> echo(day)) // inside parens eachDay(2010-07) day =\> echo(day) // trailing outside parens
25
Q

Partial Application

A

When calling a function you can use the _ symbol to perform partial application. Partially applied functions evaluate to another function with one or more parameters bound. For example:

add: (a, b) =\> a + b inc: add(\_, 1) inc(3) \>\> 4

In the example above the expression add(_, 1) created a new function with one parameter which was essentially a shortcut for this:

inc: (x) =\> add(x, 1)
26
Q

Blocks

A

Anywhere an expression is expected you can declare a block which is series of expressions evaluated in order. Blocks are declared with the do and end keywords. Blocks are most often used with lambdas:

f: (a, b) =\> do c: a + b c\*c end f(3, 4) \>\> 49 which is (3+4) \* (3+4)

The entire block evaluates to the last expression, or you can use the return keyword to short circuit and immediately return a value:

f: (x) =\> do if (x \< 10) return "small" if (x \> 90) return "big" "medium" end f(3) \>\> "small" f(93) \>\> "big" f(33) \>\> "medium"

Note that return returns from the inner-most function, not necessarily the top level function.

27
Q

If Expression

A

The if expression is used to evaluate a block only if a given condition is true. You can also use anelse clause to evaluate a block if the condition is false.

Like everything in Axon, if is itself an expression which evaluates to the result of true clause or false clause. If the condition is false and there is no else clause then the if expression evaluates to null:

if (5.isOdd) "odd" \>\> "odd" if (4.isOdd) "odd" \>\> null if (4.isOdd) "odd" else "even" \>\> "even"

You can use a do block if you need to evaluate multiple expressions inside the if expression:

if (cond1) do ... end else if (cond2) do ... end else do ... end

As a convenience you can omit the end keyword if it immediately preceeds the else keyword:

if (cond1) do ... else if (cond2) do ... else do ... end
28
Q

Throw Expression

A

The throw expression is used to raise an exception. Exceptions in Axon are dicts which have theerr marker tag and the dis display message tag. To raise an exception use the throw keyword followed by an expression which evaluates to a dict:

throw {dis:"deep doo-doo!", ts:now()}

Note your dictionary can define any tags you might want to use in exception handling. As a convenience you can also use a string:

throw {dis:"deep doo-doo!"} // as dict throw "deep doo-doo!" // convenience for above

The err tag is automatically added for you.

29
Q

Try/Catch Expression

A

The try/catch expression is used to trap exceptions. Lets look at a simple example:

try doSomethingDangerous() catch handleProblem()

You can declare a variable to store the exception’s dict as follows:

try doSomethingDangerous() catch (ex) handleProblem(ex)

The body of the try and catch clauses can be any expression or a do block.

try do ... end catch (ex) do ... end

As a convenience you can omit the end keyword if it immediately preceed the catch keyword:

try do ... catch (ex) do ... end

Like everything in Axon, try/catch is an expression which evaluates to a result. If no exception is thrown, then the whole try/catch expression evaluates to the try body clause, otherwise the expression evaluates to the catch block:

result: try getCurVal() catch "bad val"

In the expression above if no exception is thrown by getCurVal, then its result is assigned to the variable result. If an exception is thrown, then the string “bad val” is assigned to result.The try/catch expression is used to trap exceptions. Lets look at a simple example:

try doSomethingDangerous() catch handleProblem()

You can declare a variable to store the exception’s dict as follows:

try doSomethingDangerous() catch (ex) handleProblem(ex)

The body of the try and catch clauses can be any expression or a do block.

try do ... end catch (ex) do ... end

As a convenience you can omit the end keyword if it immediately preceed the catch keyword:

try do ... catch (ex) do ... end

Like everything in Axon, try/catch is an expression which evaluates to a result. If no exception is thrown, then the whole try/catch expression evaluates to the try body clause, otherwise the expression evaluates to the catch block:

result: try getCurVal() catch "bad val"

In the expression above if no exception is thrown by getCurVal, then its result is assigned to the variable result. If an exception is thrown, then the string “bad val” is assigned to result.

30
Q
A