Learning Stanza: quick reference

JITX is implemented in the LB Stanza (Stanza) programming language. We like stanza because it supports types to help write correct code, and also allows higher-level programming. We can write Stanza programs inline with JITX code, to generate designs, components, and modules programmatically.

We do not need all of Stanza to develop with JITX; this document provides a short walkthrough of commonly-used Stanza features.

; this is a comment 
This is a block comment 
        block comments can be nested with optional tags. 
defpackage learn-stanza-in-y: 
  import core 
  import collections 

; The basics, things you'd find in most programming languages

; Variables can be mutable (var) or immutable (val)
var mutable = "this one can be" 
val immutable = "this string can't be changed"

; The basic data types (annotations are optional) 
val an-int: Int = 12345
val a-long: Long = 12345L
val a-float: Float = 1.2345f
val a-double: Double = 3.14159
val a-string: String = "this is a string"
val a-multiline-string = \<tag>
    this is a multi-line string literal. 

; print formatted strings with println() and "..." % [...]
println("this is a formatted string %_ %_" % [mutable, immutable])

; Stanza is optionally typed, and has a ? (any) type. 
var anything:? = 0
anything = 3.14159
anything = "a string"

; Stanza has basic collections such as Tuples, Arrays, Vectors, HashTables, etc. 
val tuple: Tuple<?> = [mutable, immutable]

val array = Array<?>(3)
array[0] = "string"
array[1] = 1
array[2] = 1.23455
; array[3] = "out-of-bounds" ; arrays are bounds-checked 

val vector = Vector<?>()
vector[0] = "string"
vector[1] = 1
vector[2] = 3.14159

val hash-table = HashTable<String, ?>()
hash-table["0"] = 0
hash-table["1"] = 1 
hash-table["2"] = 1 

; Functions
; Functions are declared with the `defn` keyword 
defn my-function (arg:?) : ; note the space between identifier and arg list
  println("called my-function with %_" % [arg])

my-function("arg")  ; note the lack of a space to call the function

; functions can be declared inside another function and capture their environemtn
defn outer (arg): 
  defn inner (): 
    println("outer had arg: %_" % [arg])


; functions are "first-class" in stanza, meaning you can assign variables 
; to functions and pass functions as arguments to other functions. 
val a-function = outer 
defn do-n-times (arg, func, n:Int): 
  for i in 0 to n do : 
do-n-times("argument", a-function, 3)

; sometimes you want to define a function inline, or use an anonymous function. 
; for this you can use the syntax: 
;   fn (args): 
;       ... 
do-n-times("hello", fn (arg): println(arg), 2)

; there is a shorthand for writing anonymous functions, the above can be written
do-n-times("hello", { println(_) }, 2)

; the short hand works for multiple arguments as well. 
val multi-lambda = { println(_ + 2 * _) }
multi-lambda(1, 2)

; User defined types
; Structs are declared with the `defstruct` keyword
defstruct MyStruct: 

; constructors are derived automatically
val my-struct = MyStruct("field:value")

; fields are accessed using function-call syntax

; Stanza supports subtyping with a "multimethod" system based on method 
; overloading.
deftype MyType
defmulti a-method (m:MyType)

defstruct Foo <: MyType
defstruct Bar <: MyType
defmethod a-method (a-foo: Foo):
  println("called a-method on a Foo")

defmethod a-method (a-foo: Bar):
  println("called a-method on a Bar")

; The Type System
; True and Falseare types with a single value. 
val a-true: True = true 
val a-false: False = false 

; You can declare a union type, or a value that is one of a set of types 
val a-boolean: True|False = true 
val another-boolean: True|False = false 

; You can pattern match on types 
  (t:True): println("is true")
  (f:False): println("is false")

; You can match against a single possible type
  println("is still true")
  println("is not true")

; You can compose program logic around the type of a variable
if anything is Float :
  println("anything is a float")
else if anything is-not String : 
  println("anything is not an int")
else : 
  println("I don't know what anything is")

; stanza 
; Control Flow 
; stanza has the standard basic control flow 
val condition = [false, false]
if condition[0] : 
  ; do something 
else if condition[1] : 
  ; do another thing
else :
  ; whatever else

; there is also a switch statement, which can be used to pattern match
; on values (as opposed to types)
  "this": false 
  "that": false 
  "the-other-thing": false 
  else: false 

; for and while loops are supported
while condition[0]: 
  println("do stuff")

for i in 0 to 10 do:  
  vector[i] = i

; stanza also supports named labels which can functin as break or return 
; statements
defn another-fn (): 
  label<False> return:
    label<False> break:
      while true: 
        if condition[0] is False: 

; For a comprehensive guide on Stanza's advanced control flow, check out 
; this page: http://lbstanza.org/chapter9.html from Stanza-by-Example

; Sequences 
; for "loops" are sugar for a more powerful syntax. 
val xs = [1, 2, 3] 
val ys = ['a', 'b', 'c']
val zs = ["foo", "bar", "baz"]

for (x in xs, y in ys, z in zs) do : 
  println("x:%_, y:%_, z:%_" % [x, y, z])

;xs, ys, and zs are all "Seqable" meaing they are Seq types (sequences). 
; the `do` identifier is a special function that just applies the body of
; the for loop to each element of the sequence. Sequences are called "iterators"
; in other languages and provide similar abstractions. 
; A common sequence task is concatenating sequences. This is accomplished 
; using the `seq-cat` function. This is analogous to "flattening" iterateors
val concat = to-tuple $ 
  for sequence in [xs, ys, zs] seq-cat: 

; we can also use a variation to interleave the elements of multiple sequences
val interleaved = to-tuple $ 
  for (x in xs, y in ys, z in zs) seq-cat : 
    [x, y, z]

println("[%,] [%,]" % [concat, interleaved])

; Another common task is mapping a sequence to another, for example multiplying
; all the elements of a list of numbers by a constant. To do this we use `seq`. 
var numbers = [1.0, 2.0, 3.0, 4.0] 
numbers = to-tuple $ 
  for n in numbers seq : 
    2.0 * n 
println("%," % [numbers])

if find({_ == 2.0}, numbers) is-not False : 
  println("found it!")

; or maybe we just want to know if there's something in a sequence 
var is-there = 
  for n in numbers any? :
    n == 2.0 

; since this is "syntactic sugar" we can write it explicitly using an 
; anonymous function
is-there = any?({_ == 2.0}, numbers)

; a detailed reference of the sequence library and various adaptors can 
; be found here: http://lbstanza.org/reference.html#anchor439
; Documentation 
; Top level statements can be prefixed with the "doc" field which takes 
; a string value and is used to autogenerate documentation for the package. 
doc: \<doc>
    # Document Strings 

    val you-can = "include code snippets, too" 

    To render documentation as markdown (compatible with mdbook)

    stanza doc source.stanza -o docs 
defn docfn () : false