List of Programming Language Syntax Designs Not Referred to at this Time
From Learn X in Y Minutes: Scenic Programming Language Tours
Ada:
-- Comments are written with a double hyphen and exist until the end of
-- the line.
-- You do not need to call the entry point "Main" or "main," you should
-- name it based on what the program does.
procedure Empty is
-- This is a declarative part.
begin
-- Statements go here.
null; -- Do nothing here.
end Empty;
-- Ada compilers accept compilation units which can be library packages,
-- tasks, sub-programs, generics, etc.
-- This is where "context clauses" go, these can be pragmas or "with"
-- statements. "with" is equivalent to "include" or "import" in other
-- languages.
with Ada.Text_IO; -- Get access to a library package.
procedure Hello is
begin
Ada.Text_IO.Put_Line ("Hello, world");
Ada.Text_IO.Put ("Hello again, world");
Ada.Text_IO.New_Line;
end Hello;
-- Ada has a real module system. Modules are called packages and are split into
-- two component parts, the specification and a body.
-- It is important to introduce packages early, as you will be using them from
-- the start.
package Stuff is
-- We could add the following line in order to tell the compiler that this
-- package does not have to run any code before the "main" procedure starts.
-- pragma Preelaborate;
-- Packages can be nested within the same file or externally.
-- Nested packages are accessed via dot notation, e.g. Stuff.Things.My.
package Things is
My : constant Integer := 100;
end Things;
-- If there are sub-programs declared within the specification, the body
-- of the sub-program must be declared within the package body.
procedure Do_Something; -- If a subprogram takes no parameters, empty
-- parentheses are not required, unlike other
-- languages.
-- We can also make generic sub-programs.
generic
type Element is (<>); -- The "(<>)" notation specifies that only
-- discrete types can be passed into the generic.
procedure Swap (Left, Right : in out Element);
-- Sometimes we want to hide how a type is defined from the outside world
-- so that nobody can mess with it directly. The full type must be defined
-- within the private section below.
type Blobs is private;
-- We can also make types "limited" by putting this keyword after the "is"
-- keyword, this means that the user cannot copy objects of that type
-- around, like they normally could.
private
type Blobs is new Integer range -25 .. 25;
end Stuff;
package body Stuff is
-- Sub-program body.
procedure Do_Something is
-- We can nest sub-programs too.
-- Parameters are defined with the direction of travel, in, in out, out.
-- If the direction of travel is not specified, they are in by default.
function Times_4 (Value : in Integer) return Integer is
begin
return Value * 4;
end Times_4;
I : Integer := 4;
begin
I := Times_4 (I);
end Do_Something;
-- Generic procedure body.
procedure Swap (Left, Right : in out Element) is
Temp : Element := Left;
begin
Left := Right;
Right := Temp;
end Swap;
begin
-- If we need to initialise something within the package, we can do it
-- here.
Do_Something;
end Stuff;
with Ada.Unchecked_Conversion;
with Ada.Text_IO;
with Stuff;
procedure LearnAdaInY is
-- Indentation is 3 spaces.
-- The most important feature in Ada is the type. Objects have types and an
-- object of one type cannot be assigned to an object of another type.
-- You can, and should, define your own types for the domain you are
-- modelling. But you can use the standard types to start with and then
-- replace them later with your own types, this could be called a form of
-- gradual typing.
-- The standard types would only really be a good starting point for binding
-- to other languages, like C. Ada is the only language with a standardised
-- way to bind with C, Fortran and COBOL! See the links in the References
-- section with more information on binding to these languages.
type Degrees is range 0 .. 360; -- This is a type. Its underlying
-- representation is an Integer.
type Hues is (Red, Green, Blue, Purple, Yellow); -- So is this. Here, we
-- are declaring an
-- Enumeration.
-- This is a modular type. They behave like Integers that automatically
-- wrap around. In this specific case, the range would be 0 .. 359.
-- If we added 1 to a variable containing the value 359, we would receive
-- back 0. They are very useful for arrays.
type Degrees_Wrap is mod 360;
-- You can restrict a type's range using a subtype, this makes them
-- compatible with each other, i.e. the subtype can be assigned to an
-- object of the type, as can be seen below.
subtype Primaries is Hues range Red .. Blue; -- This is a range.
-- You can define variables or constants like this:
-- Var_Name : Type := Value;
-- 10 is a universal integer. These universal numerics can be used with
-- any type which matches the base type.
Angle : Degrees := 10;
Value : Integer := 20;
-- New_Angle : Degrees := Value; -- Incompatible types won't compile.
-- New_Value : Integer := Angle;
Blue_Hue : Primaries := Blue; -- A variable.
Red_Hue : constant Primaries := Red; -- A constant.
Yellow_Hue : constant Hues := Yellow;
Colour_1 : constant Hues := Red_Hue;
-- Colour_2 : constant Primaries := Yellow_Hue; -- uncomment to compile.
-- You can force conversions, but then you are warned by the name of the
-- package that you may be doing something unsafe.
function Degrees_To_Int is new Ada.Unchecked_Conversion
(Source => Degrees, -- Line continuations are indented by 2 spaces.
Target => Integer);
New_Value_2 : Integer := Degrees_To_Int (Angle); -- Note, space before (.
-- GNAT is the GNU Ada Translator (compiler).
-- Ada has a style guide and GNAT will warn you to adhere to it, and has
-- option to check your style so that you can correct it so that all Ada
-- source looks consistent. However, the style can be customized.
-- Yes, you can even define your own floating and fixed point types, this
-- is a very rare and unique ability. "digits" refers to the minimum
-- digit precision that the type should support. "delta" is for fixed
-- point types and refers to the smallest change that the type will support.
type Real_Angles is digits 3 range 0.0 .. 360.0;
type Fixed_Angles is delta 0.01 digits 5 range 0.0 .. 360.0;
RA : constant Real_Angles := 36.45;
FA : constant Fixed_Angles := 360.0; -- ".0" in order to make it a Float.
-- You can have normal Latin 1 based strings by default.
Str : constant String := "This is a constant string";
-- When initialising from a string literal, the compiler knows the bounds,
-- so we don't have to define them.
-- Strings are arrays. Note how parentheses are used to access elements of
-- an array? This is mathematical notation and was used because square
-- brackets were not available on all keyboards at the time Ada was
-- created. Also, because an array can be seen as a function from a
-- mathematical perspective, so it made converting between arrays and
-- functions easier.
Char : constant Character := Str (Str'First); -- "'First" is a type
-- attribute.
-- Ada 2022 includes the use of [] for array initialisation when using
-- containers, which were added in Ada 2012.
-- Arrays are usually always defined as a type.
-- They can be any dimension.
type My_Array_1 is array (1 .. 4, 3 .. 7, -20 .. 20) of Integer;
-- Yes, unlike other languages, you can index arrays with other discrete
-- types such as enumerations and modular types or arbitrary ranges.
type Axes is (X, Y, Z);
-- You can define the array's range using the 'Range attribute from
-- another type.
type Vector is array (Axes'Range) of Float;
V1 : constant Vector := (0.0, 0.0, 1.0);
-- A record is the same as a structure in C, C++.
type Entities is record
Name : String (1 .. 10); -- Always starts at a positive value,
-- inclusive range.
Position : Vector;
end record;
-- In Ada, array bounds are immutable. You therefore have to provide a
-- string literal with a value for every character.
E1 : constant Entities := ("Blob ", (0.0, 0.0, 0.0));
-- An alternative is to use an array aggregate and assign a default value
-- to every element that wasn't previously assigned in this aggregate.
-- "others" is used to indicate anything else that has not been
-- explicitly initialized.
E2 : constant Entities := (('B', 'l', 'o', 'b', others => ' '),
(0.0, 0.0, 0.0));
-- There are dynamic length strings (see references section) available in
-- the standard library.
-- We can make an object be initialised to its default values with the box
-- notation, "<>". "others" is used to indicate anything else that has not
-- been explicitly initialized.
Null_Entity : constant Entities := (others => <>);
-- Object-orientation is accomplished via an extension of record syntax,
-- tagged records, see link above in first paragraph.
-- We can rename objects (aliases) to make readability a bit better.
package IO renames Ada.Text_IO;
begin
-- We can output enumerations as names.
IO.Put_Line ("Blue_Hue = " & -- & is the string concatenation operator.
Blue'Image); -- ' accesses attributes on objects.
-- The Image attribute converts a value to a string.
-- Ada 2022 has extended Image to custom types too.
-- Access this with -gnat2022 compiler flag.
IO.Put_Line ("Yellow_Hue = " &
-- We can use the type's attribute.
Primaries'Image (Yellow_Hue));
-- We can define local variables within a declare block, this can be made
-- more readable by giving it a label.
Enum_IO : declare
package Hue_IO is new IO.Enumeration_IO (Hues);
-- Using a package makes everything inside that package visible within
-- this block, it is good practice to only do this locally and not on
-- a whole package within the context clause.
use Hue_IO;
begin
-- We can print out the enumeration values too.
Put (Purple); -- Note we don't have to prefix the Put procedure with
-- Hue_IO.
IO.New_Line; -- We still need to prefix with IO here.
Put (Red_Hue);
IO.New_Line;
end Enum_IO;
-- Loops have a consistent form. "<form> loop ... end loop".
-- Where "form" can be "while" or "for" or missing as below, if
-- you place the "loop ... end loop;" construct on their own lines,
-- you can comment out or experiment with different loop constructs more
-- easily.
declare
Counter : Positive := Positive'First; -- This is 1.
begin
-- We can label loops so we can exit from them more easily if we need to.
Infinite :
loop
IO.Put_Line ("[Infinite loop] Counter = " & Counter'Image);
Counter := Counter + 1;
-- This next line implements a repeat ... until or do ... while loop construct.
-- Comment it out for an infinite loop.
exit Infinite when Counter = 5; -- Equality tests use a single "=".
end loop Infinite; -- Useful when implementing state machines.
end;
declare -- We don't have to have a label.
Counter : Positive := Positive'First; -- This is 1.
begin
while Counter < 10
loop
IO.Put_Line ("Counter = " & Counter'Image);
Counter := Counter + 1; -- There is no explicit inc/decrement.
-- Ada 2022 introduced @ for LHS, so the above would be written as
-- Counter := @ + 1; -- Try it, -gnat2022.
end loop;
end;
declare
package Hue_IO is new IO.Enumeration_IO (Hues);
-- We can have multiple packages on one line, but I tend to use one
-- package per line for readability.
use IO, Hue_IO;
begin
Put ("Hues : "); -- Note, no prefix.
-- Because we are using the 'Range attribute, the compiler knows it is
-- safe and can omit run-time checks here.
for Hue in Hues'Range
loop
Put (Hue);
-- Types and objects know about their bounds, their First .. Last
-- values. These can be specified as range types.
if Hue /= Hues'Last then -- The /= means "not equal to" like the
-- maths symbol ≠.
Put (", ");
end if;
end loop;
IO.New_Line;
end;
-- All objects know their bounds, including strings.
declare
C : Character := Str (50); -- Warning caused and exception raised at
-- runtime.
-- The exception raised above can only be handled by an outer scope,
-- see wikibook link below.
begin
null; -- We will never get to this point because of the above.
end;
exception
when Constraint_Error =>
IO.Put_Line ("Caught the exception");
end LearnAdaInY;
APL:
⍝ Comments in APL are prefixed by ⍝.
⍝ A list of numbers. (¯ is negative)
2 3e7 ¯4 50.3
⍝ An expression, showing some functions. In APL, there's
⍝ no order of operations: everything is parsed right-to-
⍝ left. This is equal to 5 + (4 × (2 ÷ (5 - 3))) = 9:
5 + 4 × 2 ÷ 5 - 3 ⍝ 9
⍝ These functions work on lists, too:
1 2 3 4 × 5 ⍝ 5 10 15 20
1 2 3 4 × 5 6 7 8 ⍝ 5 12 21 32
⍝ All functions have single-argument and dual-argument
⍝ meanings. For example, "×" applied to two arguments
⍝ means multiply, but when applied to only a right-hand
⍝ side, it returns the sign:
× ¯4 ¯2 0 2 4 ⍝ ¯1 ¯1 0 1 1
⍝ Values can be compared using these operators (1 means
⍝ "true", 0 means "false"):
10 20 30 = 10 20 99 ⍝ 1 1 0
10 20 30 < 10 20 99 ⍝ 0 0 1
⍝ "⍳n" returns a vector containing the first n naturals.
⍝ Matrices can be constructed using ⍴ (reshape):
4 3 ⍴ ⍳5 ⍝ 0 1 2
⍝ 3 4 0
⍝ 1 2 3
⍝ 4 0 1
⍝ Single-argument ⍴ gives you the dimensions back:
⍴ 4 3 ⍴ ⍳5 ⍝ 4 3
⍝ Values can be stored using ←. Let's calculate the mean
⍝ value of a vector of numbers:
A ← 10 60 55 23
⍝ Sum of elements of A (/ is reduce):
+/A ⍝ 148
⍝ Length of A:
⍴A ⍝ 4
⍝ Mean:
(+/A) ÷ (⍴A) ⍝ 37
⍝ We can define this as a function using {} and ⍵:
mean ← {(+/⍵)÷⍴⍵}
mean A ⍝ 37
Arturo:
; this is a comment
; this is another comment
;---------------------------------
; VARIABLES & VALUES
;---------------------------------
; numbers
a1: 2
a2: 3.14
a3: to :complex [1 2.0] ; 1.0+2.0i
; strings
c1: "this is a string"
c2: {
this is a multiline string
that is indentation-agnostic
}
c3: {:
this is
a verbatim
multiline string
which will remain exactly
as the original
:}
; characters
ch: `c`
; blocks/arrays
d: [1 2 3]
; dictionaries
e: #[
name: "John"
surname: "Doe"
age: 34
likes: [pizza spaghetti]
]
; yes, functions are values too
f: function [x][
2 * x
]
; dates
g: now ; 2021-05-03T17:10:48+02:00
; booleans
h1: true
h2: false
;---------------------------------
; BASIC OPERATORS
;---------------------------------
; simple arithmetic
1 + 1 ; => 2
8 - 1 ; => 7
4.2 - 1.1 ; => 3.1
10 * 2 ; => 20
35 / 4 ; => 8
35 // 4 ; => 8.75
2 ^ 5 ; => 32
5 % 3 ; => 2
; bitwise operators
and 3 5 ; => 1
or 3 5 ; => 7
xor 3 5 ; => 6
; pre-defined constants
pi ; => 3.141592653589793
epsilon ; => 2.718281828459045
null ; => null
true ; => true
false ; => false
;---------------------------------
; COMPARISON OPERATORS
;---------------------------------
; equality
1 = 1 ; => true
2 = 1 ; => false
; inequality
1 <> 1 ; => false
2 <> 1 ; => true
; more comparisons
1 < 10 ; => true
1 =< 10 ; => true
10 =< 10 ; => true
1 > 10 ; => false
1 >= 10 ; => false
11 >= 10 ; => true
;---------------------------------
; CONDITIONALS
;---------------------------------
; logical operators
and? true true ; => true
and? true false ; => false
or? true false ; => true
or? false false ; => false
and? [1=2][2<3] ; => false
; (the second block will not be evaluated)
; simple if statements
if 2 > 1 [ print "yes!"] ; yes!
if 3 <> 2 -> print "true!" ; true!
; if/else statements
if? 2 > 3 -> print "2 is greater than 3"
else -> print "2 is not greater than 3" ; 2 is not greater than 3
; switch statements
switch 2 > 3 -> print "2 is greater than 3"
-> print "2 is not greater than 3" ; 2 is not greater than 3
a: (2 > 3)["yes"]["no"] ; a: "no"
a: (2 > 3)? -> "yes" -> "no" ; a: "no" (exactly the same as above)
; case/when statements
case [1]
when? [>2] -> print "1 is greater than 2. what?!"
when? [<0] -> print "1 is less than 0. nope..."
else -> print "here we are!" ; here we are!
;---------------------------------
; LOOPS
;---------------------------------
; with `loop`
arr: [1 4 5 3]
loop arr 'x [
print ["x =" x]
]
; x = 1
; x = 4
; x = 5
; x = 3
; with loop and custom index
loop.with:'i arr 'x [
print ["item at position" i "=>" x]
]
; item at position 0 => 1
; item at position 1 => 4
; item at position 2 => 5
; item at position 3 => 3
; using ranges
loop 1..3 'x -> ; since it's a single statement
print x ; there's no need for [block] notation
; we can wrap it up using the `->` syntactic sugar
loop `a`..`c` 'ch ->
print ch
; a
; b
; c
; picking multiple items
loop 1..10 [x y] ->
print ["x =" x ", y =" y]
; x = 1 , y = 2
; x = 3 , y = 4
; x = 5 , y = 6
; x = 7 , y = 8
; x = 9 , y = 10
; looping through a dictionary
dict: #[name: "John", surname: "Doe", age: 34]
loop dict [key value][
print [key "->" value]
]
; name -> John
; surname -> Doe
; age -> 34
; while loops
i: new 0
while [i<3][
print ["i =" i]
inc 'i
]
; i = 0
; i = 1
; i = 2
;---------------------------------
; STRINGS
;---------------------------------
; case
a: "tHis Is a stRinG"
print upper a ; THIS IS A STRING
print lower a ; this is a string
print capitalize a ; tHis Is a stRinG
; concatenation
a: "Hello " ++ "World!" ; a: "Hello World!"
; strings as an array
split "hello" ; => [h e l l o]
split.words "hello world" ; => [hello world]
print first "hello" ; h
print last "hello" ; o
; conversion
to :string 123 ; => "123"
to :integer "123" ; => 123
; joining strings together
join ["hello" "world"] ; => "helloworld"
join.with:"-" ["hello" "world"] ; => "hello-world"
; string interpolation
x: 2
print ~"x = |x|" ; x = 2
; interpolation with `print`
print ["x =" x] ; x = 2
; (`print` works by calculating the given block
; and joining the different values as strings
; with a single space between them)
; templates
print render.template {
<||= switch x=2 [ ||>
Yes, x = 2
<||][||>
No, x is not 2
<||]||>
} ; Yes, x = 2
; matching
prefix? "hello" "he" ; => true
suffix? "hello" "he" ; => false
contains? "hello" "ll" ; => true
contains? "hello" "he" ; => true
contains? "hello" "x" ; => false
in? "ll" "hello" ; => true
in? "x" "hello" ; => false
;---------------------------------
; BLOCKS
;---------------------------------
; calculate a block
arr: [1 1+1 1+1+1]
@arr ; => [1 2 3]
; execute a block
sth: [print "Hello world"] ; this is perfectly valid,
; could contain *anything*
; and will not be executed...
do sth ; Hello world
; (...until we tell it to)
; array indexing
arr: ["zero" "one" "two" "three"]
print first arr ; zero
print arr\0 ; zero
print last arr ; three
print arr\3 ; three
x: 2
print get arr x ; two
print arr \ 2 ; two
; (using the `\` infix alias for get -
; notice space between the operands!
; otherwise, it'll be parsed as a path)
; setting an array element
arr\0: "nada"
set arr 2 "dos"
print arr ; nada one dos three
; adding elements to an array
arr: new []
'arr ++ "one"
'arr ++ "two"
print arr ; one two
; remove elements from an array
arr: new ["one" "two" "three" "four"]
'arr -- "two" ; arr: ["one" "three" "four"]
remove 'arr .index 0 ; arr: ["three" "four"]
; getting the size of an array
arr: ["one" 2 "three" 4]
print size arr ; 4
; getting a slice of an array
print slice ["one" "two" "three" "four"] 0 1 ; one two
; check if array contains a specific element
print contains? arr "one" ; true
print contains? arr "five" ; false
; sorting array
arr: [1 5 3 2 4]
sort arr ; => [1 2 3 4 5]
sort.descending arr ; => [5 4 3 2 1]
; mapping values
map 1..10 [x][2*x] ; => [2 4 6 8 10 12 14 16 18 20]
map 1..10 'x -> 2*x ; same as above
map 1..10 => [2*&] ; same as above
map 1..10 => [2*] ; same as above
; selecting/filtering array values
select 1..10 [x][odd? x] ; => [1 3 5 7 9]
select 1..10 => odd? ; same as above
filter 1..10 => odd? ; => [2 4 6 8 10]
; (now, we leave out all odd numbers -
; while select keeps them)
; misc operations
arr: ["one" 2 "three" 4]
reverse arr ; => [4 "three" 2 "one"]
shuffle arr ; => [2 4 "three" "one"]
unique [1 2 3 2 3 1] ; => [1 2 3]
permutate [1 2 3] ; => [[1 2 3] [1 3 2] [3 1 2] [2 1 3] [2 3 1] [3 2 1]]
take 1..10 3 ; => [1 2 3]
repeat [1 2] 3 ; => [1 2 1 2 1 2]
;---------------------------------
; FUNCTIONS
;---------------------------------
; declaring a function
f: function [x][ 2*x ]
f: function [x]-> 2*x ; same as above
f: $[x]->2*x ; same as above (only using the `$` alias
; for the `function`... function)
; calling a function
f 10 ; => 20
; returning a value
g: function [x][
if x < 2 -> return 0
res: 0
loop 0..x 'z [
res: res + z
]
return res
]
;---------------------------------
; CUSTOM TYPES
;---------------------------------
; defining a custom type
define :person [ ; define a new custom type "Person"
name ; with fields: name, surname, age
surname
age
][
; with custom post-construction initializer
init: [
this\name: capitalize this\name
]
; custom print function
print: [
render "NAME: |this\name|, SURNAME: |this\surname|, AGE: |this\age|"
]
; custom comparison operator
compare: 'age
]
; create a method for our custom type
sayHello: function [this][
ensure -> is? :person this
print ["Hello" this\name]
]
; create new objects of our custom type
a: to :person ["John" "Doe" 34] ; let's create 2 "Person"s
b: to :person ["jane" "Doe" 33] ; and another one
; call pseudo-inner method
sayHello a ; Hello John
sayHello b ; Hello Jane
; access object fields
print ["The first person's name is:" a\name] ; The first person's name is: John
print ["The second person's name is:" b\name] ; The second person's name is: Jane
; changing object fields
a\name: "Bob"
sayHello a ; Hello Bob
; verifying object type
print type a ; :person
print is? :person a ; true
; printing objects
print a ; NAME: John, SURNAME: Doe, AGE: 34
; sorting user objects (using custom comparator)
sort @[a b] ; Jane..., John...
sort.descending @[a b] ; John..., Jane...
AssemblyScript:
// There are many basic types in AssemblyScript,
let isDone: boolean = false;
let name: string = "Anders";
// but integer type come as signed (sized from 8 to 64 bits)
let lines8: i8 = 42;
let lines16: i16 = 42;
let lines32: i32 = 42;
let lines64: i64 = 42;
// and unsigned (sized from 8 to 64 bits),
let ulines8: u8 = 42;
let ulines16: u16 = 42;
let ulines32: u32 = 42;
let ulines64: u64 = 42;
// and float has two sizes possible (32/64).
let rate32: f32 = 1.0
let rate64: f64 = 1.0
// But you can omit the type annotation if the variables are derived
// from explicit literals
let _isDone = false;
let _lines = 42;
let _name = "Anders";
// Use const keyword for constants
const numLivesForCat = 9;
numLivesForCat = 1; // Error
// For collections, there are typed arrays and generic arrays
let list1: i8[] = [1, 2, 3];
// Alternatively, using the generic array type
let list2: Array<i8> = [1, 2, 3];
// For enumerations:
enum Color { Red, Green, Blue };
let c: Color = Color.Green;
// Functions imported from JavaScript need to be declared as external
// @ts-ignore decorator
@external("alert")
declare function alert(message: string): void;
// and you can also import JS functions in a namespace
declare namespace window {
// @ts-ignore decorator
@external("window", "alert")
function alert(message: string): void;
}
// Lastly, "void" is used in the special case of a function returning nothing
export function bigHorribleAlert(): void {
alert("I'm a little annoying box!"); // calling JS function here
}
// Functions are first class citizens, support the lambda "fat arrow" syntax
// The following are equivalent, the compiler does not offer any type
// inference for functions yet, and same WebAssembly will be emitted.
export function f1 (i: i32): i32 { return i * i; }
// "Fat arrow" syntax
let f2 = (i: i32): i32 => { return i * i; }
// "Fat arrow" syntax, braceless means no return keyword needed
let f3 = (i: i32): i32 => i * i;
// Classes - members are public by default
export class Point {
// Properties
x: f64;
// Constructor - the public/private keywords in this context will generate
// the boiler plate code for the property and the initialization in the
// constructor.
// In this example, "y" will be defined just like "x" is, but with less code
// Default values are also supported
constructor(x: f64, public y: f64 = 0) {
this.x = x;
}
// Functions
dist(): f64 { return Math.sqrt(this.x * this.x + this.y * this.y); }
// Static members
static origin: Point = new Point(0, 0);
}
// Classes can be explicitly marked as extending a parent class.
// Any missing properties will then cause an error at compile-time.
export class PointPerson extends Point {
constructor(x: f64, y: f64, public name: string) {
super(x, y);
}
move(): void {}
}
let p1 = new Point(10, 20);
let p2 = new Point(25); //y will be 0
// Inheritance
export class Point3D extends Point {
constructor(x: f64, y: f64, public z: f64 = 0) {
super(x, y); // Explicit call to the super class constructor is mandatory
}
// Overwrite
dist(): f64 {
let d = super.dist();
return Math.sqrt(d * d + this.z * this.z);
}
}
// Namespaces, "." can be used as separator for sub namespaces
export namespace Geometry {
class Square {
constructor(public sideLength: f64 = 0) {
}
area(): f64 {
return Math.pow(this.sideLength, 2);
}
}
}
let s1 = new Geometry.Square(5);
// Generics
// AssemblyScript compiles generics to one concrete method or function per set
// of unique contextual type arguments, also known as [monomorphisation].
// Implications are that a module only includes and exports concrete functions
// for sets of type arguments actually used and that concrete functions can be
// shortcutted with [static type checks] at compile time, which turned out to
// be quite useful.
// Classes
export class Tuple<T1, T2> {
constructor(public item1: T1, public item2: T2) {
}
}
export class Pair<T> {
item1: T;
item2: T;
}
// And functions
export function pairToTuple <T>(p: Pair<T>): Tuple<T, T> {
return new Tuple(p.item1, p.item2);
};
let tuple = pairToTuple<string>({ item1: "hello", item2: "world" });
// Including references to a TypeScript-only definition file:
/// <reference path="jquery.d.ts" />
// Template Strings (strings that use backticks)
// String Interpolation with Template Strings
let name = 'Tyrone';
let greeting = `Hi ${name}, how are you?`
// Multiline Strings with Template Strings
let multiline = `This is an example
of a multiline string`;
let numbers: Array<i8> = [0, 1, 2, 3, 4];
let moreNumbers: Array<i8> = numbers;
moreNumbers[5] = 5; // Error, elements are read-only
moreNumbers.push(5); // Error, no push method (because it mutates array)
moreNumbers.length = 3; // Error, length is read-only
numbers = moreNumbers; // Error, mutating methods are missing
// Type inference in Arrays
let ints = [0, 1, 2, 3, 4] // will infer as Array<i32>
let floats: f32[] = [0, 1, 2, 3, 4] // will infer as Array<f32>
let doubles = [0.0, 1.0, 2, 3, 4] // will infer as Array<f64>
let bytes1 = [0 as u8, 1, 2, 3, 4] // will infer as Array<u8>
let bytes2 = [0, 1, 2, 3, 4] as u8[] // will infer as Array<u8>
let bytes3: u8[] = [0, 1, 2, 3, 4] // will infer as Array<u8>
ATS:
// Include the standard library
#include "share/atspre_define.hats"
#include "share/atspre_staload.hats"
// To compile, either use
// $ patscc -DATS_MEMALLOC_LIBC program.dats -o program
// or install the ats-acc wrapper https://github.com/sparverius/ats-acc and use
// $ acc pc program.dats
// C-style line comments
/* and C-style block comments */
(* as well as ML-style block comments *)
/*************** Part 1: the ML fragment ****************/
val () = print "Hello, World!\n"
// No currying
fn add (x: int, y: int) = x + y
// fn vs fun is like the difference between let and let rec in OCaml/F#
fun fact (n: int): int = if n = 0 then 1 else n * fact (n-1)
// Multi-argument functions need parentheses when you call them; single-argument
// functions can omit parentheses
val forty_three = add (fact 4, 19)
// let is like let in SML
fn sum_and_prod (x: int, y: int): (int, int) =
let
val sum = x + y
val prod = x * y
in (sum, prod) end
// 'type' is the type of all heap-allocated, non-linear types
// Polymorphic parameters go in {} after the function name
fn id {a:type} (x: a) = x
// ints aren't heap-allocated, so we can't pass them to 'id'
// val y: int = id 7 // doesn't compile
// 't@ype' is the type of all non-linear potentially unboxed types. It is a
// supertype of 'type'.
// Templated parameters go in {} before the function name
fn {a:t@ype} id2 (x: a) = x
val y: int = id2 7 // works
// can't have polymorphic t@ype parameters
// fn id3 {a:t@ype} (x: a) = x // doesn't compile
// explicity specifying type parameters:
fn id4 {a:type} (x: a) = id {a} x // {} for non-template parameters
fn id5 {a:type} (x: a) = id2<a> x // <> for template parameters
fn id6 {a:type} (x: a) = id {..} x // {..} to explicitly infer it
// Heap allocated shareable datatypes
// using datatypes leaks memory
datatype These (a:t@ype, b:t@ype) = This of a
| That of b
| These of (a, b)
// Pattern matching using 'case'
fn {a,b: t@ype} from_these (x: a, y: b, these: These(a,b)): (a, b) =
case these of
| This(x) => (x, y) // Shadowing of variable names is fine; here, x shadows
// the parameter x
| That(y) => (x, y)
| These(x, y) => (x, y)
// Partial pattern match using 'case-'
// Will throw an exception if passed This
fn {a,b:t@ype} unwrap_that (these: These(a,b)): b =
case- these of
| That(y) => y
| These(_, y) => y
/*************** Part 2: refinements ****************/
// Parameterize functions by what values they take and return
fn cool_add {n:int} {m:int} (x: int n, y: int m): int (n+m) = x + y
// list(a, n) is a list of n a's
fun square_all {n:int} (xs: list(int, n)): list(int, n) =
case xs of
| list_nil() => list_nil()
| list_cons(x, rest) => list_cons(x * x, square_all rest)
fn {a:t@ype} get_first {n:int | n >= 1} (xs: list(a, n)): a =
case+ xs of // '+' asks ATS to prove it's total
| list_cons(x, _) => x
// Can't run get_first on lists of length 0
// val x: int = get_first (list_nil()) // doesn't compile
// in the stdlib:
// sortdef nat = {n:int | n >= 0}
// sortdef pos = {n:int | n >= 1}
fn {a:t@ype} also_get_first {n:pos} (xs: list(a, n)): a =
let
val+ list_cons(x, _) = xs // val+ also works
in x end
// tail-recursive reverse
fun {a:t@ype} reverse {n:int} (xs: list(a, n)): list(a, n) =
let
// local functions can use type variables from their enclosing scope
// this one uses both 'a' and 'n'
fun rev_helper {i:nat} (xs: list(a, n-i), acc: list(a, i)): list(a, n) =
case xs of
| list_nil() => acc
| list_cons(x, rest) => rev_helper(rest, list_cons(x, acc))
in rev_helper(xs, list_nil) end
// ATS has three context-dependent namespaces
// the two 'int's mean different things in this example, as do the two 'n's
fn namespace_example {n:int} (n: int n): int n = n
// ^^^ sort namespace
// ^ ^^^ ^ ^^^ ^ statics namespace
// ^^^^^^^^^^^^^^^^^ ^ ^ value namespace
// a termination metric can go in .< >.
// it must decrease on each recursive call
// then ATS will prove it doesn't infinitely recurse
fun terminating_factorial {n:nat} .<n>. (n: int n): int =
if n = 0 then 1 else n * terminating_factorial (n-1)
/*************** Part 3: the LF fragment ****************/
// ATS supports proving theorems in LF (http://twelf.org/wiki/LF)
// Relations are represented by inductive types
// Proofs that the nth fibonacci number is f
dataprop Fib(n:int, m:int) =
| FibZero(0, 0)
| FibOne(1, 1)
| {n, f1, f2: int} FibInd(n, f1 + f2) of (Fib(n-1,f1), Fib(n-2,f2))
// Proved-correct fibonacci implementation
// [A] B is an existential type: "there exists A such that B"
// (proof | value)
fun fib {n:nat} .<n>. (n: int n): [f:int] (Fib(n,f) | int f) =
if n = 0 then (FibZero | 0) else
if n = 1 then (FibOne | 1) else
let
val (proof1 | val1) = fib (n-1)
val (proof2 | val2) = fib (n-2)
// the existential type is inferred
in (FibInd(proof1, proof2) | val1 + val2) end
// Faster proved-correct fibonacci implementation
fn fib_tail {n:nat} (n: int n): [f:int] (Fib(n,f) | int f) =
let
fun loop {i:int | i < n} {f1, f2: int} .<n - i>.
(p1: Fib(i,f1), p2: Fib(i+1,f2)
| i: int i, f1: int f1, f2: int f2, n: int n
): [f:int] (Fib(n,f) | int f) =
if i = n - 1
then (p2 | f2)
else loop (p2, FibInd(p2,p1) | i+1, f2, f1+f2, n)
in if n = 0 then (FibZero | 0) else loop (FibZero, FibOne | 0, 0, 1, n) end
// Proof-level lists of ints, of type 'sort'
datasort IntList = ILNil of ()
| ILCons of (int, IntList)
// ILAppend(x,y,z) iff x ++ y = z
dataprop ILAppend(IntList, IntList, IntList) =
| {y:IntList} AppendNil(ILNil, y, y)
| {a:int} {x,y,z: IntList}
AppendCons(ILCons(a,x), y, ILCons(a,z)) of ILAppend(x,y,z)
// prfuns/prfns are compile-time functions acting on proofs
// metatheorem: append is total
prfun append_total {x,y: IntList} .<x>. (): [z:IntList] ILAppend(x,y,z)
= scase x of // scase lets you inspect static arguments (only in prfuns)
| ILNil() => AppendNil
| ILCons(a,rest) => AppendCons(append_total())
/*************** Part 4: views ****************/
// views are like props, but linear; ie they must be consumed exactly once
// prop is a subtype of view
// 'type @ address' is the most basic view
fn {a:t@ype} read_ptr {l:addr} (pf: a@l | p: ptr l): (a@l | a) =
let
// !p searches for usable proofs that say something is at that address
val x = !p
in (pf | x) end
// oops, tried to dereference a potentially invalid pointer
// fn {a:t@ype} bad {l:addr} (p: ptr l): a = !p // doesn't compile
// oops, dropped the proof (leaked the memory)
// fn {a:t@ype} bad {l:addr} (pf: a@l | p: ptr l): a = !p // doesn't compile
fn inc_at_ptr {l:addr} (pf: int@l | p: ptr l): (int@l | void) =
let
// !p := value writes value to the location at p
// like !p, it implicitly searches for usable proofs that are in scope
val () = !p := !p + 1
in (pf | ()) end
// threading proofs through gets annoying
fn inc_three_times {l:addr} (pf: int@l | p: ptr l): (int@l | void) =
let
val (pf2 | ()) = inc_at_ptr (pf | p)
val (pf3 | ()) = inc_at_ptr (pf2 | p)
val (pf4 | ()) = inc_at_ptr (pf3 | p)
in (pf4 | ()) end
// so there's special syntactic sugar for when you don't consume a proof
fn dec_at_ptr {l:addr} (pf: !int@l | p: ptr l): void =
!p := !p - 1 // ^ note the exclamation point
fn dec_three_times {l:addr} (pf: !int@l | p: ptr l): void =
let
val () = dec_at_ptr (pf | p)
val () = dec_at_ptr (pf | p)
val () = dec_at_ptr (pf | p)
in () end
// dataview is like dataprop, but linear
// A proof that either the address is null, or there is a value there
dataview MaybeNull(a:t@ype, addr) =
| NullPtr(a, null)
| {l:addr | l > null} NonNullPtr(a, l) of (a @ l)
fn maybe_inc {l:addr} (pf: !MaybeNull(int, l) | p: ptr l): void =
if ptr1_is_null p
then ()
else let
// Deconstruct the proof to access the proof of a @ l
prval NonNullPtr(value_exists) = pf
val () = !p := !p + 1
// Reconstruct it again for the caller
prval () = pf := NonNullPtr(value_exists)
in () end
// array_v(a,l,n) represents and array of n a's at location l
// this gets compiled into an efficient for loop, since all proofs are erased
fn sum_array {l:addr}{n:nat} (pf: !array_v(int,l,n) | p: ptr l, n: int n): int =
let
fun loop {l:addr}{n:nat} .<n>. (
pf: !array_v(int,l,n)
| ptr: ptr l,
length: int n,
acc: int
): int = if length = 0
then acc
else let
prval (head, rest) = array_v_uncons(pf)
val result = loop(rest | ptr_add<int>(ptr, 1), length - 1, acc + !ptr)
prval () = pf := array_v_cons(head, rest)
in result end
in loop (pf | p, n, 0) end
// 'var' is used to create stack-allocated (lvalue) variables
val seven: int = let
var res: int = 3
// they can be modified
val () = res := res + 1
// addr@ res is a pointer to it, and view@ res is the associated proof
val (pf | ()) = inc_three_times(view@ res | addr@ res)
// need to give back the view before the variable goes out of scope
prval () = view@ res := pf
in res end
// References let you pass lvalues, like in C++
fn square (x: &int): void =
x := x * x // they can be modified
val sixteen: int = let
var res: int = 4
val () = square res
in res end
fn inc_at_ref (x: &int): void =
let
// like vars, references have views and addresses
val (pf | ()) = inc_at_ptr(view@ x | addr@ x)
prval () = view@ x := pf
in () end
// Like ! for views, & references are only legal as argument types
// fn bad (x: &int): &int = x // doesn't compile
// this takes a proof int n @ l, but returns a proof int (n+1) @ l
// since they're different types, we can't use !int @ l like before
fn refined_inc_at_ptr {n:int}{l:addr} (
pf: int n @ l | p: ptr l
): (int (n+1) @ l | void) =
let
val () = !p := !p + 1
in (pf | ()) end
// special syntactic sugar for returning a proof at a different type
fn refined_dec_at_ptr {n:int}{l:addr} (
pf: !int n @ l >> int (n-1) @ l | p: ptr l
): void =
!p := !p - 1
// legal but very bad code
prfn swap_proofs {v1,v2:view} (a: !v1 >> v2, b: !v2 >> v1): void =
let
prval tmp = a
prval () = a := b
prval () = b := tmp
in () end
// also works with references
fn refined_square {n:int} (x: &int n >> int (n*n)): void =
x := x * x
fn replace {a,b:vtype} (dest: &a >> b, src: b): a =
let
val old = dest
val () = dest := src
in old end
// values can be uninitialized
fn {a:vt@ype} write (place: &a? >> a, value: a): void =
place := value
val forty: int = let
var res: int
val () = write (res, 40)
in res end
// viewtype: a view and a type
viewtypedef MaybeNullPtr(a:t@ype) = [l:addr] (MaybeNull(a, l) | ptr l)
// MaybeNullPtr has type 'viewtype' (aka 'vtype')
// type is a subtype of vtype and t@ype is a subtype of vt@ype
// The most general identity function:
fn {a:vt@ype} id7 (x: a) = x
// since they contain views, viewtypes must be used linearly
// fn {a:vt@ype} duplicate (x: a) = (x, x) // doesn't compile
// fn {a:vt@ype} ignore (x: a) = () // doesn't compile
// arrayptr(a,l,n) is a convenient built-in viewtype
fn easier_sum_array {l:addr}{n:nat} (p: !arrayptr(int,l,n), n: int n): int =
let
fun loop {i:nat | i <= n} (
p: !arrayptr(int,l,n), n: int n, i: int i, acc: int
): int =
if i = n
then acc
else loop(p, n, i+1, acc + p[i])
in loop(p, n, 0, 0) end
/*************** Part 5: dataviewtypes ****************/
// a dataviewtype is a heap-allocated non-shared inductive type
// in the stdlib:
// dataviewtype list_vt(a:vt@ype, int) =
// | list_vt_nil(a, 0)
// | {n:nat} list_vt_cons(a, n+1) of (a, list_vt(a, n))
fn {a:vt@ype} length {n:int} (l: !list_vt(a, n)): int n =
let // ^ since we're not consuming it
fun loop {acc:int} (l: !list_vt(a, n-acc), acc: int acc): int n =
case l of
| list_vt_nil() => acc
| list_vt_cons(head, tail) => loop(tail, acc + 1)
in loop (l, 0) end
// vvvvv not vt@ype, because you can't easily get rid of a vt@ype
fun {a:t@ype} destroy_list {n:nat} (l: list_vt(a,n)): void =
case l of
// ~ pattern match consumes and frees that node
| ~list_vt_nil() => ()
| ~list_vt_cons(_, tail) => destroy_list tail
// unlike a datatype, a dataviewtype can be modified:
fun {a:vt@ype} push_back {n:nat} (
x: a,
l: &list_vt(a,n) >> list_vt(a,n+1)
): void =
case l of
| ~list_vt_nil() => l := list_vt_cons(x, list_vt_nil)
// @ pattern match disassembles/"unfolds" the datavtype's view, so you can
// modify its components
| @list_vt_cons(head, tail) => let
val () = push_back (x, tail)
// reassemble it with fold@
prval () = fold@ l
in () end
fun {a:vt@ype} pop_last {n:pos} (l: &list_vt(a,n) >> list_vt(a,n-1)): a =
let
val+ @list_vt_cons(head, tail) = l
in case tail of
| list_vt_cons _ => let
val res = pop_last tail
prval () = fold@ l
in res end
| ~list_vt_nil() => let
val head = head
// Deallocate empty datavtype nodes with free@
val () = free@{..}{0} l
val () = l := list_vt_nil()
in head end
/** Equivalently:
* | ~list_vt_nil() => let
* prval () = fold@ l
* val+ ~list_vt_cons(head, ~list_vt_nil()) = l
* val () = l := list_vt_nil()
* in head end
*/
end
// "holes" (ie uninitialized memory) can be created with _ on the RHS
// This function uses destination-passing-style to copy the list in a single
// tail-recursive pass.
fn {a:t@ype} copy_list {n:nat} (l: !list_vt(a, n)): list_vt(a, n) =
let
var res: ptr
fun loop {k:nat} (l: !list_vt(a, k), hole: &ptr? >> list_vt(a, k)): void =
case l of
| list_vt_nil() => hole := list_vt_nil
| list_vt_cons(first, rest) => let
val () = hole := list_vt_cons{..}{k-1}(first, _)
// ^ on RHS: a hole
val+list_vt_cons(_, new_hole) = hole
// ^ on LHS: wildcard pattern (not a hole)
val () = loop (rest, new_hole)
prval () = fold@ hole
in () end
val () = loop (l, res)
in res end
// Reverse a linked-list *in place* -- no allocations or frees
fn {a:vt@ype} in_place_reverse {n:nat} (l: list_vt(a, n)): list_vt(a, n) =
let
fun loop {k:nat} (l: list_vt(a, n-k), acc: list_vt(a, k)): list_vt(a, n) =
case l of
| ~list_vt_nil() => acc
| @list_vt_cons(x, tail) => let
val rest = replace(tail, acc)
// the node 'l' is now part of acc instead of the original list
prval () = fold@ l
in loop (rest, l) end
in loop (l, list_vt_nil) end
/*************** Part 6: miscellaneous extras ****************/
// a record
// Point has type 't@ype'
typedef Point = @{ x= int, y= int }
val origin: Point = @{ x= 0, y= 0 }
// tuples and records are normally unboxed, but there are boxed variants
// BoxedPoint has type 'type'
typedef BoxedPoint = '{ x= int, y= int }
val boxed_pair: '(int,int) = '(5, 3)
// When passing a pair as the single argument to a function, it needs to be
// written @(a,b) to avoid ambiguity with multi-argument functions
val six_plus_seven = let
fun sum_of_pair (pair: (int,int)) = pair.0 + pair.1
in sum_of_pair @(6, 7) end
// When a constructor has no associated data, such as None(), the parentheses
// are optional in expressions. However, they are mandatory in patterns
fn inc_option (opt: Option int) =
case opt of
| Some(x) => Some(x+1)
| None() => None
// ATS has a simple FFI, since it compiles to C and (mostly) uses the C ABI
%{
// Inline C code
int scanf_wrapper(void *format, void *value) {
return scanf((char *) format, (int *) value);
}
%}
// If you wanted to, you could define a custom dataviewtype more accurately
// describing the result of scanf
extern fn scanf (format: string, value: &int): int = "scanf_wrapper"
fn get_input_number (): Option int =
let
var x: int = 0
in
if scanf("%d\n", x) = 1
then Some(x)
else None
end
// extern is also used for separate declarations and definitions
extern fn say_hi (): void
// later on, or in another file:
implement say_hi () = print "hi\n"
// if you implement main0, it will run as the main function
// implmnt is an alias for implement
implmnt main0 () = ()
// as well as for axioms:
extern praxi view_id {a:view} (x: a): a
// you don't need to actually implement the axioms, but you could
primplmnt view_id x = x
// primplmnt is an alias for primplement
// Some standard aliases are:
// List0(a) = [n:nat] list(a,n) and List0_vt(a) = [n:nat] list_vt(a,n)
// t0p = t@ype and vt0p = vt@ype
fun {a:t0p} append (xs: List0 a, ys: List0 a): List0 a =
case xs of
| list_nil() => ys
| list_cons(x, xs) => list_cons(x, append(xs, ys))
// there are many ways of doing things after one another
val let_in_example = let
val () = print "thing one\n"
val () = print "thing two\n"
in () end
val parens_example = (print "thing one\n"; print "thing two\n")
val begin_end_example = begin
print "thing one\n";
print "thing two\n"; // optional trailing semicolon
end
// and many ways to use local variables
fun times_four_let x =
let
fun times_two (x: int) = x * 2
in times_two (times_two x) end
local
fun times_two (x: int) = x * 2
in
fun times_four_local x = times_two (times_two x)
end
fun times_four_where x = times_two (times_two x)
where {
fun times_two (x: int) = x * 2
}
//// The last kind of comment in ATS is an end-of-file comment.
Anything between the four slashes and the end of the file is ignored.
Have fun with ATS!
Ballerina:
// Single-line comment
// Import modules into the current source file
import ballerina/io;
import ballerina/time;
import ballerina/http;
import ballerinax/java.jdbc;
import ballerina/lang.'int as ints;
import ballerinax/awslambda;
// Module alias "af" used in code in place of the full module name
import ballerinax/azure.functions as af;
http:Client clientEP = new ("https://freegeoip.app/");
jdbc:Client accountsDB = new ({url: "jdbc:mysql://localhost:3306/AccountsDB",
username: "test", password: "test"});
// A service is a first-class concept in Ballerina, and is one of the
// entrypoints to a Ballerina program.
// The Ballerina platform also provides support for easy deployment to
// environments such as Kubernetes (https://ballerina.io/learn/deployment/kubernetes/).
service geoservice on new http:Listener(8080) {
@http:ResourceConfig {
path: "/geoip/{ip}"
}
resource function geoip(http:Caller caller, http:Request request,
string ip) returns @tainted error? {
http:Response resp = check clientEP->get("/json/" + <@untainted>ip);
check caller->respond(<@untainted> check resp.getTextPayload());
}
}
// Serverless Function-as-a-Service support with AWS Lambda.
// The Ballerina compiler automatically generates the final deployment
// artifact to be deployed.
@awslambda:Function
public function echo(awslambda:Context ctx, json input) returns json {
return input;
}
@awslambda:Function
public function notifyS3(awslambda:Context ctx,
awslambda:S3Event event) returns json {
return event.Records[0].s3.'object.key;
}
// Serverless Function-as-a-Service support with Azure Functions.
// Similar to AWS Lambda, the compiler generates the deployment artifacts.
@af:Function
public function fromQueueToQueue(af:Context ctx,
@af:QueueTrigger { queueName: "queue1" } string inMsg,
@af:QueueOutput { queueName: "queue2" } af:StringOutputBinding outMsg) {
outMsg.value = inMsg;
}
// A custom record type
public type Person record {
string id; // required field
string name;
int age?; // optional field
string country = "N/A"; // default value
};
@af:Function
public function fromHttpTriggerCosmosDBInput(
@af:HTTPTrigger { route: "c1/{country}" } af:HTTPRequest httpReq,
@af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection",
databaseName: "db1", collectionName: "c1",
sqlQuery: "select * from c1 where c1.country = {country}" }
Person[] dbReq)
returns @af:HTTPOutput string|error {
return dbReq.toString();
}
public function main() returns @tainted error? {
int a = 10; // 64-bit signed integer
float b = 1.56; // 64-bit IEEE 754-2008 binary floating point number
string c = "hello"; // a unicode string
boolean d = true; // true, false
decimal e = 15.335; // decimal floating point number
var f = 20; // type inference with 'var' - 'f' is an int
int[] intArray = [1, 2, 3, 4, 5, 6];
int x = intArray.shift(); // similar to a dequeue operation
x = intArray.pop(); // removes the last element
intArray.push(10); // add to the end
// Tuples - similar to a fixed length array with a distinct type for each slot
[string, int] p1 = ["Jack", 1990];
[string, int] p2 = ["Tom", 1986];
io:println("Name: ", p1[0], " Birth Year: ", p1[1]);
string name1;
int birthYear1;
[name1, birthYear1] = p1; // tuple destructuring
var [name2, birthYear2] = p2; // declare and assign values in the same statement
// If statements
int ix = 10;
if ix < 10 {
io:println("value is less than 10");
} else if ix == 10 {
io:println("value equals to 10");
} else {
io:println("value is greater than 10");
}
// Loops
int count = 10;
int i = 0;
while i < 10 {
io:println(i);
}
// Loop from 0 to count (inclusive)
foreach var j in 0...count {
io:println(j);
}
// Loop from 0 to count (non-inclusive)
foreach var j in 0..<count {
io:println(j);
}
// Loop a list
foreach var j in intArray {
io:println(j);
}
json j1 = { "name" : name1, "birthYear" : birthYear1, "zipcode" : 90210 };
io:println(j1.name, " - ", j1.zipcode);
// New fields are added to a JSON value through "mergeJson"
var j2 = j1.mergeJson({ "id" : "90400593053"});
// XML namespace declaration
xmlns "http://example.com/ns1" as ns1;
xmlns "http://example.com/default";
// XML variable from a literal value
xml x1 = xml `<ns1:entry><name>{{name1}}</name><birthYear>{{birthYear1}}</birthYear></ns1:entry>`;
io:println(x1);
// Access specific elements in the XML value
io:println(x1/<name>);
// List all child items in the XML value
io:println(x1/*);
// Function invocations
x = add(1, 2);
io:println(multiply(2, 4));
// Invocation providing value for the defaultable parameter
io:println(multiply(3, 4, true));
// Invocation with values to a rest parameter (multi-valued)
io:println(addAll(1, 2, 3));
io:println(addAll(1, 2, 3, 4, 5));
// Function pointers
(function (int, int) returns int) op1 = getOperation("add");
(function (int, int) returns int) op2 = getOperation("mod");
io:println(op1(5, 10));
io:println(op2(13, 10));
// Closures
(function (int x) returns int) add5 = getAdder(5);
(function (int x) returns int) add10 = getAdder(10);
io:println(add5(10));
io:println(add10(10));
int[] numbers = [1, 2, 3, 4, 5, 6, 7, 8];
// Functional iteration
int[] evenNumbers = numbers.filter(function (int x) returns boolean { return x % 2 == 0; });
// Union types - "input" is of type either string or byte[]
string|byte[] uval = "XXX";
// A type test expression ("uval is string") can be used to check the
// runtime type of a variable.
if uval is string {
// In the current scope, "uval" is a string value
string data = "data:" + uval;
} else {
// Since the expression in the "if" statement ruled out that it's not a string,
// the only type left is "byte[]"; so in the current scope, "uval" will always
// be a "byte[]".
int inputLength = uval.length();
}
// Error handling
string input = io:readln("Enter number: ");
int|error result = ints:fromString(input);
if result is int {
io:println("Number: ", result);
} else {
io:println("Invalid number: ", input);
}
// A check expression can be used to directly return the error from
// the current function if its subexpression evaluated to an error
// value in the runtime.
int number = check ints:fromString(input);
// Concurrent execution using workers in a function
doWorkers();
// Asynchronous execution with futures
future<int> f10 = start fib(10);
var webresult = clientEP->get("/");
int fresult = wait f10;
if webresult is http:Response {
io:println(webresult.getTextPayload());
io:println(fresult);
}
// Mapping types
map<int> ageMap = {};
ageMap["Peter"] = 25;
ageMap["John"] = 30;
int? agePeter = ageMap["Peter"]; // int? is the union type int|() - int or nill
if agePeter is int {
io:println("Peter's age is ", agePeter);
} else {
io:println("Peter's age is not found");
}
Person person1 = { id: "p1", name : "Anne", age: 28, country: "Sri Lanka" };
Scores score1 = { physics : 80, mathematics: 95 };
score1["chemistry"] = 75;
io:println(score1["chemistry"]);
Student student1 = { id: "s1", name: "Jack", age: 25, country: "Japan" };
student1.college = "Stanford";
string? jacksCollege = student1?.college; // optional field access
if jacksCollege is string {
io:println("Jack's college is ", jacksCollege);
}
// Due to the structural type system, "student1" can be assigned to "person2",
// since the student1's structure is compatible with person2's,
// where we can say, a "Student" is a "Person" as well.
Person person2 = student1;
map<int> grades = {"Jack": 95, "Anne": 90, "John": 80, "Bill": 55};
Person px1 = {id: "px1", name: "Jack", age: 30, country: "Canada"};
Person px2 = {id: "px2", name: "John", age: 25};
Person px3 = {id: "px3", name: "Anne", age: 17, country: "UK"};
Person px4 = {id: "px4", name: "Bill", age: 15, country: "USA"};
Person[] persons = [];
persons.push(px1);
persons.push(px2);
persons.push(px3);
persons.push(px4);
// Query expressions used to execute complex queries for list data
Result[] results = from var person in persons
let int lgrade = (grades[person.name] ?: 0)
where lgrade > 75
let string targetCollege = "Stanford"
select {
name: person.name,
college: targetCollege,
grade: lgrade
};
// Compile-time taint checking for handling untrusted data
string s1 = "abc";
mySecureFunction(s1);
// Explicitely make "s2" a tainted value. External input to a Ballerina
// program such as command-line arguments and network input are by-default
// marked as tainted data.
string s2 = <@tainted> s1;
// "s2x" is now a tainted value, since its value is derived using a
// tainted value (s1).
string s2x = s2 + "abc";
// The following line uncommented will result in a compilation error,
// since we are passing a tainted value (s2x) to a function which
// exepects an untainted value.
// mySecureFunction(s2x);
// Instantiating objects
Employee emp1 = new("E0001", "Jack Smith", "Sales", 2009);
io:println("The company service duration of ", emp1.name,
" is ", emp1.serviceDuration());
// Supported operations can be executed in a transaction by enclosing the actions
// in a "transaction" block.
transaction {
// Executes the below database operations in a single local transactions
var r1 = accountsDB->update("UPDATE Employee SET balance = balance + ? WHERE id = ?", 5500.0, "ID001");
var r2 = accountsDB->update("UPDATE Employee SET balance = balance + ? WHERE id = ?", 5500.0, "ID001");
}
}
// An object is a behavioural type, which encapsulates both data and functionality.
type Employee object {
// Private fields are only visible within the object and its methods
private string empId;
// Public fields can be accessed by anyone
public string name;
public string department;
// The default qualifier is a "protected" field,
// which are accessible only within the module.
int yearJoined;
// The object initialization function; automatically called when an object is instantiated.
public function __init(string empId, string name, string department, int yearJoined) {
self.empId = empId;
self.name = name;
self.department = department;
self.yearJoined = yearJoined;
}
// An object method
public function serviceDuration() returns int {
time:Time ct = time:currentTime();
return time:getYear(ct) - self.yearJoined;
}
};
// Student is a subtype of Person
type Student record {
string id;
string name;
int age;
string college?;
string country;
};
type Scores record {
int physics;
int mathematics;
};
type Result record {
string name;
string college;
int grade;
};
public function getOperation(string op) returns (function (int, int) returns int) {
if op == "add" {
return add;
} else if op == "mod" {
return function (int a, int b) returns int { // anonymous function
return a % b;
};
} else {
return (x, y) => 0; // single expression anonymous no-op function
}
}
// Two required parameters
public function add(int a, int b) returns int {
return a + b;
}
// 'log' is a defaultable parameter
public function multiply(int a, int b, boolean log = false) returns int {
if log {
io:println("Multiplying ", a, " with ", b);
}
return a * b;
}
// 'numbers' is a rest parameter - it can have multiple values,
// similar to an array.
public function addAll(int... numbers) returns int {
int result = 0;
foreach int number in numbers {
result += number;
}
return result;
}
public function getAdder(int n) returns (function (int x) returns int) {
return function (int x) returns int { // returns closure
return x + n;
};
}
function fib(int n) returns int {
if n <= 2 {
return 1;
} else {
return fib(n - 1) + fib(n - 2);
}
}
// The code in worker blocks "w1" and "w2" are executed concurrency
// when this function is invoked. The "wait" expressions waits for
// the given workers to finish to retrieve their results.
public function doWorkers() {
worker w1 returns int {
int j = 10;
j -> w2;
int b;
b = <- w2;
return b * b;
}
worker w2 returns int {
int a;
a = <- w1;
a * 2 -> w1;
return a + 2;
}
record {int w1; int w2;} x = wait {w1, w2};
io:println(x);
}
// A function which takes in only an untainted string value.
public function mySecureFunction(@untainted string input) {
io:println(input);
}
BrainFuck:
Any character not "><+-.,[]" (excluding quotation marks) is ignored.
Brainfuck is represented by an array with 30,000 cells initialized to zero
and a data pointer pointing at the current cell.
There are eight commands:
+ : Increments the value at the current cell by one.
- : Decrements the value at the current cell by one.
> : Moves the data pointer to the next cell (cell on the right).
< : Moves the data pointer to the previous cell (cell on the left).
. : Prints the ASCII value at the current cell (i.e. 65 = 'A').
, : Reads a single input character into the current cell.
[ : If the value at the current cell is zero, skips to the corresponding ] .
Otherwise, move to the next instruction.
] : If the value at the current cell is zero, move to the next instruction.
Otherwise, move backwards in the instructions to the corresponding [ .
[ and ] form a while loop. Obviously, they must be balanced.
Let's look at some basic brainfuck programs.
++++++ [ > ++++++++++ < - ] > +++++ .
This program prints out the letter 'A'. First, it increments cell #1 to 6.
Cell #1 will be used for looping. Then, it enters the loop ([) and moves
to cell #2. It increments cell #2 10 times, moves back to cell #1, and
decrements cell #1. This loop happens 6 times (it takes 6 decrements for
cell #1 to reach 0, at which point it skips to the corresponding ] and
continues on).
At this point, we're on cell #1, which has a value of 0, while cell #2 has a
value of 60. We move on cell #2, increment 5 times, for a value of 65, and then
print cell #2's value. 65 is 'A' in ASCII, so 'A' is printed to the terminal.
, [ > + < - ] > .
This program reads a character from the user input and copies the character into
cell #1. Then we start a loop. Move to cell #2, increment the value at cell #2,
move back to cell #1, and decrement the value at cell #1. This continues on
until cell #1 is 0, and cell #2 holds cell #1's old value. Because we're on
cell #1 at the end of the loop, move to cell #2, and then print out the value
in ASCII.
Also keep in mind that the spaces are purely for readability purposes. You
could just as easily write it as:
,[>+<-]>.
Try and figure out what this program does:
,>,< [ > [ >+ >+ << -] >> [- << + >>] <<< -] >>
This program takes two numbers for input, and multiplies them.
The gist is it first reads in two inputs. Then it starts the outer loop,
conditioned on cell #1. Then it moves to cell #2, and starts the inner
loop conditioned on cell #2, incrementing cell #3. However, there comes a
problem: At the end of the inner loop, cell #2 is zero. In that case,
inner loop won't work anymore since next time. To solve this problem,
we also increment cell #4, and then recopy cell #4 into cell #2.
Then cell #3 is the result.
BQN:
# This is a comment.
# The characters ',' and `⋄` are statement separators.
##################
# Main datatypes #
##################
# Numbers
1,2,3,4
¯1,¯2,¯3 # Negative numbers are written with a high minus
π,∞,¯π,¯∞ # Pi and Infinity are defined constants
1_234_456 # You can add underscores in between numbers
# This does not change their value
1.3E4 # Scientific notation is supported
# Characters
'a','⥊'
'
' # Yes, you can put *any* character in a character literal
@ # Null character ('\0' in C)
# Arrays
1‿2‿3 # Stranding, good for simple lists
⟨1,2,3⟩ # General list notation
⟨1‿2,2‿3⟩ # Both can be mixed
[1‿2,2‿3] # Array notation
# An array is multidimensional, as opposed to containing sublists.
# It must be rectangular in shape (a grid structure rather than a tree structure)
[1‿2‿3,4‿5] # This is hence invalid
# May be familiar coming from Numpy, MATLAB and similar languages.
"asdf" # Character array (String)
"newline
separated" # Allows newlines
"quo""tes" # Escape a double quote by typing it twice
# Functions
1{𝕨+𝕩}3 # All functions are infix
# 𝕨 is left argument, 𝕩 is right argument
{-𝕩}5 # 𝕨 can be omitted
1+3 # Same as the above
{𝕊𝕩} # 𝕊 is a recursive call
# (this function will loop forever)
{𝕨 𝕊 𝕩: 𝕨+𝕩} # Functions can have headers (too many cases to discuss here)
# Headers can define arity
{𝕊 a‿b: a}1‿2 # and also do basic pattern matching
# (returns 1)
# Modifiers (higher order functions)
{𝕗,𝔽,𝕘,𝔾} # 𝔽 and 𝔾 are the operands as callable functions
# 𝕗 and 𝕘 are the operands as values
{𝔽𝕩} # 1-modifiers use 𝔽/𝕗 ONLY
˜,˘,¨,⁼,⌜ # primitive 1-modifiers are superscripts
{𝕨𝔽𝔾𝕩} # 2-modifiers MUST use both 𝔽/𝕗 and 𝔾/𝕘 in body or header
⊸,∘,○,⟜ # primitive 2-modifiers all have circles
+{⟨𝕗⟩} # returns ⟨ + ⟩
1-{𝔽 𝕨 𝔾 𝕩 }×2 # returns ¯2 (operators are *also* infix)
# (same as 1 -○× 2)
# Trains (Special form of function composition)
(+´÷≠) # Average (but how?)
# The above train is an F G H train, where
# (F G H) 𝕩 → (F 𝕩) G (H 𝕩)
# F ← +´, G ← ÷, H ← ≠
# In explicit form, this is
{(+´𝕩)÷≠𝕩}
# The second pattern is (f g) 𝕩 → f g 𝕩.
# longer trains are complex arrangements of these patterns, involving constants and Nothing (·).
# Read more about trains at https://mlochbaum.github.io/BQN/doc/train.html
# Evaluation order:
# BQN evaluates functions right to left with no precedence rules governing *functions*. Functions are what
# one would call operators in a mainstream language.
1÷2+3 # 1÷(2+3) = 0.2
(1÷2)+3 # ((1÷2)+3) = 1.5
# Modifiers:
# Modifiers are higher order functions, and bind tighter than functions. Modifiers execute left to right.
# Modifiers can take non-function arguments e.g. Constant (`˙`)
+
1+˜2+○-∘×3 # 1(+˜)(2((+○-)∘×)3)
# Variables
# Since the case of a variable matters to determine what it means, BQN variables are *case insensitive*
# The case that a variable is written in can change the way it is interpreted by BQN.
# Eg. `F` refers to a value as a callable function, whereas `f` refers to the same variable as just a value.
# Variable assignment is done with `←`. Variables have naming conventions based on their value:
subject ← 1‿2‿3 # Arrays, single values, namespaces come under this
# name must start with with a lowercase letter
Function ← {𝕨+𝕩} # Primitive and user defined functions come under this, both monadic and dyadic
# Starts with an uppercase letter
_1modifier ← {𝕨𝔽𝕩} # Starts with an underscore
_2modifier_ ← {𝔽𝕨𝔾𝕩} # Starts and ends with an underscore
# Variable modification is done with `↩`. An existing name cannot be reassigned with `←`.
Func ↩ {"Hello"∾𝕩}
array_or_atom +↩ 2 # You can use a dyadic function for modification
#≡ 3‿4‿5
array_or_atom -↩ # Or a monadic function.
#≡ ¯3‿¯4‿¯5
# Due to all functions being infix, you can use your own functions for modification as well:
array_or_atom {2⋆𝕩}↩ #≡ ⟨ 0.125, 0.0625, 0.03125 ⟩
##################
# BQN Primitives #
##################
# All of BQN's base primitives are a single character long. Refer to https://mlochbaum.github.io/BQN/help/index.html for
# examples.
# Here we will look at a few primitives from each section. You will want to consult the docs for detailed explanations.
# Primitive Functions
# All BQN functions are variadic, and can take one or two arguments. The base functions have both monadic and dyadic overloads.
# Usually the two overloads for a function are related.
## Arithmetic Functions
+, -, ×, ÷ # Add, Subtract, Signum/Multiply, Reciprocal/Divide , '*' does NOT do multiplication
# ⌊∘÷ does floor division
√, ⋆ # Square root/Nth root, e^x/Power
# All Arithmetic functions vectorize:
1 + 2‿3‿4 #≡ 3‿4‿5
1‿2‿3 + 2‿3‿4 #≡ 3‿5‿7
# Character arithmetic(+ and - only):
"abc"+3 #≡ "def"
'a'-'d' #≡ ¯3
## Logic Functions
∧, ∨, ¬ # For Booleans, return 1 or 0
≤, <, >, ≥, = # Vectorizing comparisons
≡, ≢ # Nonvectorizing comparisons
## Array manipulation Functions
↕ # Make a range
∾, ≍, ⋈ # Joining arrays together
a←1‿2‿3,b←4‿5 # Let us take a and b.
a∾b #≡ 1‿2‿3‿4‿5
a≍b # Same as previous, since a and b are not multidimensional
# Adds an extra dimension, similar to a ⋈ for multidimensional arrays.
a⋈b #≡ ⟨1‿2‿3, 4‿5⟩
⊑, ⊏ # Indexing
1⊑1‿2‿3 #≡ 2 (BQN is 0-indexed)
1‿2⊏1‿2‿3 #≡ 2‿3 (for multiple indices)
↑, ↓ # Getting a prefix, suffix of an array.
# together they can be used for slicing
⥊ # Reshape/repeat items to create a new array
# Primitive 1-Modifiers
## Looping combinators
¨, ˘, ⌜ # Mapping/Zipping
´, ˝ # Fold from right
` # Scan from left
## General combinators
˜ # duplicate argument/swap args - Very useful!
˙ # Create constant function
1 -˜ 2 #≡ 2 - 1
+˜ 2 #≡ 2 + 2
# Primitive 2-modifiers
## Control Flow
◶ # Choose from a list of funcs
⍟ # Repeat n times
## General Combinators
⊸, ⟜ # hook, hookf
∘, ○ # simple function composition
##########
# Blocks #
##########
# Code delimited by {}
# Lexically scoped
# For more info: https://mlochbaum.github.io/BQN/doc/block.html
# Can have headers, which are ways to explicitly define what a block should be.
# A block without headers is automatically inferred from its special variables (𝕨, 𝕩, ...).
# Function blocks
# Implicit variables(Capitals are functions):
# - 𝕨, 𝕎 left argument
# - 𝕩, 𝕏 right argument
# - 𝕤, 𝕊 represent the block itself
# Optional: one or more headers that trigger based on
# - pattern match (':') o
# - condition ('?') (similar to if-then-else)
{ # A factorial using headers:
𝕊 0: 1;
𝕊 𝕩: 𝕩×𝕊 𝕩-1
}
{ # Factorial with predicates
𝕩<2 ? 1; # Similar to an if-else pattern.
𝕩×𝕊 𝕩-1
}
# Modifier blocks
# create 1-modifiers and 2-modifiers, which have separate types
# Implicit variables(Capitals are functions):
# - has 𝕨 and 𝕩 if needed
# - 𝕗, 𝔽 left operand
# - 𝕘, 𝔾 right operand (only in 2-modifiers)
# - 𝕣 represents the block itself* (requires underscores as per convention)
# Same header rules as functions.
{ 𝕨=0 ? 𝔽 𝕩; 𝔾 𝕩 } # execute 𝔽 or 𝔾 based on whether left argument is 0.
# Namespace blocks
# Create immutable namespaces with fields
# Require exports (`⇐`) for accessible fields.
# Use '.' for field access
n←{
A←+
b⇐4
}
n.b #≡ 4
n.a # ERROR
# Immediate Blocks
# No arguments taken
# Run the code inside and return the last statement
# Often responsible for strange errors.
# Can be mistaken for other blocks easily
# Good for avoiding scoping issues
{
1‿2‿3
}
{+} # Trick for returning a function as a value
####################
# Basic constructs #
####################
# Functional programming
# `¨` is used for mapping, as discussed before:
{𝕩∾2}¨1‿2‿3 #≡ ⟨1‿2,2‿2,3‿2⟩
# ⋈¨ is a plain zip, which produces pairs.
# `¨` acts as a zipWith when used with two arguments:
1‿2‿3 {⟨𝕩+2,2⥊𝕨⟩} 4‿5‿6 #≡ ⟨⟨6,1‿1⟩,⟨7,2‿2⟩,⟨8,3‿3⟩⟩
# `/` is replicate, which serves several purposes *including* filtering.
# elements in 𝕩 are repeated by the corresponding number in 𝕨.
1‿2‿3‿0/4‿5‿6‿7 #≡ 4‿5‿5‿6‿6‿6
# a simple filter idiom is F⊸/:
{2|𝕩}⊸/67‿42‿83 # keep the odd elements
#≡ 67‿83
# Conditionals
# There are two main ways to define a conditional.
## Predicate headers
{
𝕩 > 2: "greater than 2";
𝕩 < 2: "lesser than 2";
"equal to 2"
}
## Choose (function-based)
# - 2-modifier
# - 𝔾: list of functions that serve as bodies
# - 𝔽: condition function that specifies which function from 𝔾 to select
# The same conditional as above would be:
{⊑/⟨𝕩>2, 𝕩<2, 𝕩=2⟩}◶⟨
{𝕊: "greater than 2"}
{𝕊: "lesser than 2"}
{𝕊: "equal to 2"}
⟩
## Some helpers for conditionals
If ← {𝕏⍟𝕎@}´ # Used as If ⟨Condition, Block⟩
IfElse ← {c‿T‿F: c◶F‿T@} # Used as IfElse ⟨Condition, Block, ElseBlock⟩
# Looping
# The primary form of unbounded looping is recursion (performed with 𝕊).
# BQN does not eliminate tail calls, but the while idiom can be used to work around this:
While ← {𝕩{𝔽⍟𝔾∘𝔽_𝕣_𝔾∘𝔽⍟𝔾𝕩}𝕨@}´ # While 1‿{... to run forever
DoWhile ← {𝕏@ ⋄ While 𝕨‿𝕩}´
# A For loop can be done with ¨, functions need not be pure.
Chapel:
/*
Learn Chapel in Y Minutes
This primer will go over basic syntax and concepts in Chapel.
Last sync with official page: Sun, 08 Mar 2020 08:05:53 +0000
*/
// Comments are C-family style
// one line comment
/*
multi-line comment
*/
/*
Basic printing
*/
write("Hello, ");
writeln("World!");
// ``write`` and ``writeln`` can take a list of things to print.
// Each thing is printed right next to the others, so include your spacing!
writeln("There are ", 3, " commas (\",\") in this line of code");
// Different output channels:
use IO; // Required for accessing the alternative output channels
stdout.writeln("This goes to standard output, just like plain writeln() does");
stderr.writeln("This goes to standard error");
/*
Variables
*/
// Variables don't have to be explicitly typed as long as
// the compiler can figure out the type that it will hold.
// 10 is an ``int``, so ``myVar`` is implicitly an ``int``
var myVar = 10;
myVar = -10;
var mySecondVar = myVar;
// ``var anError;`` would be a compile-time error.
// We can (and should) explicitly type things.
var myThirdVar: real;
var myFourthVar: real = -1.234;
myThirdVar = myFourthVar;
/*
Types
*/
// There are a number of basic types.
var myInt: int = -1000; // Signed ints
var myUint: uint = 1234; // Unsigned ints
var myReal: real = 9.876; // Floating point numbers
var myImag: imag = 5.0i; // Imaginary numbers
var myCplx: complex = 10 + 9i; // Complex numbers
myCplx = myInt + myImag; // Another way to form complex numbers
var myBool: bool = false; // Booleans
var myStr: string = "Some string..."; // Strings
var singleQuoteStr = 'Another string...'; // String literal with single quotes
// Some types can have sizes.
var my8Int: int(8) = 10; // 8 bit (one byte) sized int;
var my64Real: real(64) = 1.516; // 64 bit (8 bytes) sized real
// Typecasting.
var intFromReal = myReal : int;
var intFromReal2: int = myReal : int;
// Type aliasing.
type chroma = int; // Type of a single hue
type RGBColor = 3*chroma; // Type representing a full color
var black: RGBColor = (0,0,0);
var white: RGBColor = (255, 255, 255);
/*
Constants and Parameters
*/
// A ``const`` is a constant, and cannot be changed after set in runtime.
const almostPi: real = 22.0/7.0;
// A ``param`` is a constant whose value must be known statically at
// compile-time.
param compileTimeConst: int = 16;
// The ``config`` modifier allows values to be set at the command line.
// Set with ``--varCmdLineArg=Value`` or ``--varCmdLineArg Value`` at runtime.
config var varCmdLineArg: int = -123;
config const constCmdLineArg: int = 777;
// ``config param`` can be set at compile-time.
// Set with ``--set paramCmdLineArg=value`` at compile-time.
config param paramCmdLineArg: bool = false;
writeln(varCmdLineArg, ", ", constCmdLineArg, ", ", paramCmdLineArg);
/*
References
*/
// ``ref`` operates much like a reference in C++. In Chapel, a ``ref`` cannot
// be made to alias a variable other than the variable it is initialized with.
// Here, ``refToActual`` refers to ``actual``.
var actual = 10;
ref refToActual = actual;
writeln(actual, " == ", refToActual); // prints the same value
actual = -123; // modify actual (which refToActual refers to)
writeln(actual, " == ", refToActual); // prints the same value
refToActual = 99999999; // modify what refToActual refers to (which is actual)
writeln(actual, " == ", refToActual); // prints the same value
/*
Operators
*/
// Math operators:
var a: int, thisInt = 1234, thatInt = 5678;
a = thisInt + thatInt; // Addition
a = thisInt * thatInt; // Multiplication
a = thisInt - thatInt; // Subtraction
a = thisInt / thatInt; // Division
a = thisInt ** thatInt; // Exponentiation
a = thisInt % thatInt; // Remainder (modulo)
// Logical operators:
var b: bool, thisBool = false, thatBool = true;
b = thisBool && thatBool; // Logical and
b = thisBool || thatBool; // Logical or
b = !thisBool; // Logical negation
// Relational operators:
b = thisInt > thatInt; // Greater-than
b = thisInt >= thatInt; // Greater-than-or-equal-to
b = thisInt < a && a <= thatInt; // Less-than, and, less-than-or-equal-to
b = thisInt != thatInt; // Not-equal-to
b = thisInt == thatInt; // Equal-to
// Bitwise operators:
a = thisInt << 10; // Left-bit-shift by 10 bits;
a = thatInt >> 5; // Right-bit-shift by 5 bits;
a = ~thisInt; // Bitwise-negation
a = thisInt ^ thatInt; // Bitwise exclusive-or
// Compound assignment operators:
a += thisInt; // Addition-equals (a = a + thisInt;)
a *= thatInt; // Times-equals (a = a * thatInt;)
b &&= thatBool; // Logical-and-equals (b = b && thatBool;)
a <<= 3; // Left-bit-shift-equals (a = a << 10;)
// Unlike other C family languages, there are no
// pre/post-increment/decrement operators, such as:
//
// ``++j``, ``--j``, ``j++``, ``j--``
// Swap operator:
var old_this = thisInt;
var old_that = thatInt;
thisInt <=> thatInt; // Swap the values of thisInt and thatInt
writeln((old_this == thatInt) && (old_that == thisInt));
// Operator overloads can also be defined, as we'll see with procedures.
/*
Tuples
*/
// Tuples can be of the same type or different types.
var sameTup: 2*int = (10, -1);
var sameTup2 = (11, -6);
var diffTup: (int,real,complex) = (5, 1.928, myCplx);
var diffTupe2 = (7, 5.64, 6.0+1.5i);
// Tuples can be accessed using square brackets or parentheses, and are
// 1-indexed.
writeln("(", sameTup[1], ",", sameTup(2), ")");
writeln(diffTup);
// Tuples can also be written into.
diffTup(1) = -1;
// Tuple values can be expanded into their own variables.
var (tupInt, tupReal, tupCplx) = diffTup;
writeln(diffTup == (tupInt, tupReal, tupCplx));
// They are also useful for writing a list of variables, as is common in debugging.
writeln((a,b,thisInt,thatInt,thisBool,thatBool));
/*
Control Flow
*/
// ``if`` - ``then`` - ``else`` works just like any other C-family language.
if 10 < 100 then
writeln("All is well");
if -1 < 1 then
writeln("Continuing to believe reality");
else
writeln("Send mathematician, something's wrong");
// You can use parentheses if you prefer.
if (10 > 100) {
writeln("Universe broken. Please reboot universe.");
}
if a % 2 == 0 {
writeln(a, " is even.");
} else {
writeln(a, " is odd.");
}
if a % 3 == 0 {
writeln(a, " is even divisible by 3.");
} else if a % 3 == 1 {
writeln(a, " is divided by 3 with a remainder of 1.");
} else {
writeln(b, " is divided by 3 with a remainder of 2.");
}
// Ternary: ``if`` - ``then`` - ``else`` in a statement.
var maximum = if thisInt < thatInt then thatInt else thisInt;
// ``select`` statements are much like switch statements in other languages.
// However, ``select`` statements don't cascade like in C or Java.
var inputOption = "anOption";
select inputOption {
when "anOption" do writeln("Chose 'anOption'");
when "otherOption" {
writeln("Chose 'otherOption'");
writeln("Which has a body");
}
otherwise {
writeln("Any other Input");
writeln("the otherwise case doesn't need a do if the body is one line");
}
}
// ``while`` and ``do``-``while`` loops also behave like their C counterparts.
var j: int = 1;
var jSum: int = 0;
while (j <= 1000) {
jSum += j;
j += 1;
}
writeln(jSum);
do {
jSum += j;
j += 1;
} while (j <= 10000);
writeln(jSum);
// ``for`` loops are much like those in python in that they iterate over a
// range. Ranges (like the ``1..10`` expression below) are a first-class object
// in Chapel, and as such can be stored in variables.
for i in 1..10 do write(i, ", ");
writeln();
var iSum: int = 0;
for i in 1..1000 {
iSum += i;
}
writeln(iSum);
for x in 1..10 {
for y in 1..10 {
write((x,y), "\t");
}
writeln();
}
/*
Ranges and Domains
*/
// For-loops and arrays both use ranges and domains to define an index set that
// can be iterated over. Ranges are single dimensional integer indices, while
// domains can be multi-dimensional and represent indices of different types.
// They are first-class citizen types, and can be assigned into variables.
var range1to10: range = 1..10; // 1, 2, 3, ..., 10
var range2to11 = 2..11; // 2, 3, 4, ..., 11
var rangeThisToThat: range = thisInt..thatInt; // using variables
var rangeEmpty: range = 100..-100; // this is valid but contains no indices
// Ranges can be unbounded.
var range1toInf: range(boundedType=BoundedRangeType.boundedLow) = 1.. ; // 1, 2, 3, 4, 5, ...
var rangeNegInfTo1 = ..1; // ..., -4, -3, -2, -1, 0, 1
// Ranges can be strided (and reversed) using the ``by`` operator.
var range2to10by2: range(stridable=true) = 2..10 by 2; // 2, 4, 6, 8, 10
var reverse2to10by2 = 2..10 by -2; // 10, 8, 6, 4, 2
var trapRange = 10..1 by -1; // Do not be fooled, this is still an empty range
writeln("Size of range '", trapRange, "' = ", trapRange.size);
// Note: ``range(boundedType= ...)`` and ``range(stridable= ...)`` are only
// necessary if we explicitly type the variable.
// The end point of a range can be computed by specifying the total size
// of the range using the count (``#``) operator.
var rangeCount: range = -5..#12; // range from -5 to 6
// Operators can be mixed.
var rangeCountBy: range(stridable=true) = -5..#12 by 2; // -5, -3, -1, 1, 3, 5
writeln(rangeCountBy);
// Properties of the range can be queried.
// In this example, printing the first index, last index, number of indices,
// stride, and if 2 is include in the range.
writeln((rangeCountBy.first, rangeCountBy.last, rangeCountBy.size,
rangeCountBy.stride, rangeCountBy.contains(2)));
for i in rangeCountBy {
write(i, if i == rangeCountBy.last then "\n" else ", ");
}
// Rectangular domains are defined using the same range syntax,
// but they are required to be bounded (unlike ranges).
var domain1to10: domain(1) = {1..10}; // 1D domain from 1..10;
var twoDimensions: domain(2) = {-2..2,0..2}; // 2D domain over product of ranges
var thirdDim: range = 1..16;
var threeDims: domain(3) = {thirdDim, 1..10, 5..10}; // using a range variable
// Domains can also be resized
var resizedDom = {1..10};
writeln("before, resizedDom = ", resizedDom);
resizedDom = {-10..#10};
writeln("after, resizedDom = ", resizedDom);
// Indices can be iterated over as tuples.
for idx in twoDimensions do
write(idx, ", ");
writeln();
// These tuples can also be destructured.
for (x,y) in twoDimensions {
write("(", x, ", ", y, ")", ", ");
}
writeln();
// Associative domains act like sets.
var stringSet: domain(string); // empty set of strings
stringSet += "a";
stringSet += "b";
stringSet += "c";
stringSet += "a"; // Redundant add "a"
stringSet -= "c"; // Remove "c"
writeln(stringSet.sorted());
// Associative domains can also have a literal syntax
var intSet = {1, 2, 4, 5, 100};
// Both ranges and domains can be sliced to produce a range or domain with the
// intersection of indices.
var rangeA = 1.. ; // range from 1 to infinity
var rangeB = ..5; // range from negative infinity to 5
var rangeC = rangeA[rangeB]; // resulting range is 1..5
writeln((rangeA, rangeB, rangeC));
var domainA = {1..10, 5..20};
var domainB = {-5..5, 1..10};
var domainC = domainA[domainB];
writeln((domainA, domainB, domainC));
/*
Arrays
*/
// Arrays are similar to those of other languages.
// Their sizes are defined using domains that represent their indices.
var intArray: [1..10] int;
var intArray2: [{1..10}] int; // equivalent
// They can be accessed using either brackets or parentheses
for i in 1..10 do
intArray[i] = -i;
writeln(intArray);
// We cannot access ``intArray[0]`` because it exists outside
// of the index set, ``{1..10}``, we defined it to have.
// ``intArray[11]`` is illegal for the same reason.
var realDomain: domain(2) = {1..5,1..7};
var realArray: [realDomain] real;
var realArray2: [1..5,1..7] real; // equivalent
var realArray3: [{1..5,1..7}] real; // equivalent
for i in 1..5 {
for j in realDomain.dim(2) { // Only use the 2nd dimension of the domain
realArray[i,j] = -1.61803 * i + 0.5 * j; // Access using index list
var idx: 2*int = (i,j); // Note: 'index' is a keyword
realArray[idx] = - realArray[(i,j)]; // Index using tuples
}
}
// Arrays have domains as members, and can be iterated over as normal.
for idx in realArray.domain { // Again, idx is a 2*int tuple
realArray[idx] = 1 / realArray[idx[1], idx[2]]; // Access by tuple and list
}
writeln(realArray);
// The values of an array can also be iterated directly.
var rSum: real = 0;
for value in realArray {
rSum += value; // Read a value
value = rSum; // Write a value
}
writeln(rSum, "\n", realArray);
// Associative arrays (dictionaries) can be created using associative domains.
var dictDomain: domain(string) = { "one", "two", "three"};
var dict: [dictDomain] int = ["one" => 1, "two" => 2, "three" => 3];
for key in dictDomain.sorted() do
writeln(dict[key]);
// Arrays can be assigned to each other in a few different ways.
// These arrays will be used in the example.
var thisArray : [0..5] int = [0,1,2,3,4,5];
var thatArray : [0..5] int;
// First, simply assign one to the other. This copies ``thisArray`` into
// ``thatArray``, instead of just creating a reference. Therefore, modifying
// ``thisArray`` does not also modify ``thatArray``.
thatArray = thisArray;
thatArray[1] = -1;
writeln((thisArray, thatArray));
// Assign a slice from one array to a slice (of the same size) in the other.
thatArray[4..5] = thisArray[1..2];
writeln((thisArray, thatArray));
// Operations can also be promoted to work on arrays. 'thisPlusThat' is also
// an array.
var thisPlusThat = thisArray + thatArray;
writeln(thisPlusThat);
// Moving on, arrays and loops can also be expressions, where the loop
// body's expression is the result of each iteration.
var arrayFromLoop = for i in 1..10 do i;
writeln(arrayFromLoop);
// An expression can result in nothing, such as when filtering with an if-expression.
var evensOrFives = for i in 1..10 do if (i % 2 == 0 || i % 5 == 0) then i;
writeln(arrayFromLoop);
// Array expressions can also be written with a bracket notation.
// Note: this syntax uses the ``forall`` parallel concept discussed later.
var evensOrFivesAgain = [i in 1..10] if (i % 2 == 0 || i % 5 == 0) then i;
// They can also be written over the values of the array.
arrayFromLoop = [value in arrayFromLoop] value + 1;
/*
Procedures
*/
// Chapel procedures have similar syntax functions in other languages.
proc fibonacci(n : int) : int {
if n <= 1 then return n;
return fibonacci(n-1) + fibonacci(n-2);
}
// Input parameters can be untyped to create a generic procedure.
proc doublePrint(thing): void {
write(thing, " ", thing, "\n");
}
// The return type can be inferred, as long as the compiler can figure it out.
proc addThree(n) {
return n + 3;
}
doublePrint(addThree(fibonacci(20)));
// It is also possible to take a variable number of parameters.
proc maxOf(x ...?k) {
// x refers to a tuple of one type, with k elements
var maximum = x[1];
for i in 2..k do maximum = if maximum < x[i] then x[i] else maximum;
return maximum;
}
writeln(maxOf(1, -10, 189, -9071982, 5, 17, 20001, 42));
// Procedures can have default parameter values, and
// the parameters can be named in the call, even out of order.
proc defaultsProc(x: int, y: real = 1.2634): (int,real) {
return (x,y);
}
writeln(defaultsProc(10));
writeln(defaultsProc(x=11));
writeln(defaultsProc(x=12, y=5.432));
writeln(defaultsProc(y=9.876, x=13));
// The ``?`` operator is called the query operator, and is used to take
// undetermined values like tuple or array sizes and generic types.
// For example, taking arrays as parameters. The query operator is used to
// determine the domain of ``A``. This is useful for defining the return type,
// though it's not required.
proc invertArray(A: [?D] int): [D] int{
for a in A do a = -a;
return A;
}
writeln(invertArray(intArray));
// We can query the type of arguments to generic procedures.
// Here we define a procedure that takes two arguments of
// the same type, yet we don't define what that type is.
proc genericProc(arg1 : ?valueType, arg2 : valueType): void {
select(valueType) {
when int do writeln(arg1, " and ", arg2, " are ints");
when real do writeln(arg1, " and ", arg2, " are reals");
otherwise writeln(arg1, " and ", arg2, " are somethings!");
}
}
genericProc(1, 2);
genericProc(1.2, 2.3);
genericProc(1.0+2.0i, 3.0+4.0i);
// We can also enforce a form of polymorphism with the ``where`` clause
// This allows the compiler to decide which function to use.
// Note: That means that all information needs to be known at compile-time.
// The param modifier on the arg is used to enforce this constraint.
proc whereProc(param N : int): void
where (N > 0) {
writeln("N is greater than 0");
}
proc whereProc(param N : int): void
where (N < 0) {
writeln("N is less than 0");
}
whereProc(10);
whereProc(-1);
// ``whereProc(0)`` would result in a compiler error because there
// are no functions that satisfy the ``where`` clause's condition.
// We could have defined a ``whereProc`` without a ``where`` clause
// that would then have served as a catch all for all the other cases
// (of which there is only one).
// ``where`` clauses can also be used to constrain based on argument type.
proc whereType(x: ?t) where t == int {
writeln("Inside 'int' version of 'whereType': ", x);
}
proc whereType(x: ?t) {
writeln("Inside general version of 'whereType': ", x);
}
whereType(42);
whereType("hello");
/*
Intents
*/
/* Intent modifiers on the arguments convey how those arguments are passed to the procedure.
* in: copy arg in, but not out
* out: copy arg out, but not in
* inout: copy arg in, copy arg out
* ref: pass arg by reference
*/
proc intentsProc(in inarg, out outarg, inout inoutarg, ref refarg) {
writeln("Inside Before: ", (inarg, outarg, inoutarg, refarg));
inarg = inarg + 100;
outarg = outarg + 100;
inoutarg = inoutarg + 100;
refarg = refarg + 100;
writeln("Inside After: ", (inarg, outarg, inoutarg, refarg));
}
var inVar: int = 1;
var outVar: int = 2;
var inoutVar: int = 3;
var refVar: int = 4;
writeln("Outside Before: ", (inVar, outVar, inoutVar, refVar));
intentsProc(inVar, outVar, inoutVar, refVar);
writeln("Outside After: ", (inVar, outVar, inoutVar, refVar));
// Similarly, we can define intents on the return type.
// ``refElement`` returns a reference to an element of array.
// This makes more practical sense for class methods where references to
// elements in a data-structure are returned via a method or iterator.
proc refElement(array : [?D] ?T, idx) ref : T {
return array[idx];
}
var myChangingArray : [1..5] int = [1,2,3,4,5];
writeln(myChangingArray);
ref refToElem = refElement(myChangingArray, 5); // store reference to element in ref variable
writeln(refToElem);
refToElem = -2; // modify reference which modifies actual value in array
writeln(refToElem);
writeln(myChangingArray);
/*
Operator Definitions
*/
// Chapel allows for operators to be overloaded.
// We can define the unary operators:
// ``+ - ! ~``
// and the binary operators:
// ``+ - * / % ** == <= >= < > << >> & | ˆ by``
// ``+= -= *= /= %= **= &= |= ˆ= <<= >>= <=>``
// Boolean exclusive or operator.
proc ^(left : bool, right : bool): bool {
return (left || right) && !(left && right);
}
writeln(true ^ true);
writeln(false ^ true);
writeln(true ^ false);
writeln(false ^ false);
// Define a ``*`` operator on any two types that returns a tuple of those types.
proc *(left : ?ltype, right : ?rtype): (ltype, rtype) {
writeln("\tIn our '*' overload!");
return (left, right);
}
writeln(1 * "a"); // Uses our ``*`` operator.
writeln(1 * 2); // Uses the default ``*`` operator.
// Note: You could break everything if you get careless with your overloads.
// This here will break everything. Don't do it.
/*
proc +(left: int, right: int): int {
return left - right;
}
*/
/*
Iterators
*/
// Iterators are sisters to the procedure, and almost everything about
// procedures also applies to iterators. However, instead of returning a single
// value, iterators may yield multiple values to a loop.
//
// This is useful when a complicated set or order of iterations is needed, as
// it allows the code defining the iterations to be separate from the loop
// body.
iter oddsThenEvens(N: int): int {
for i in 1..N by 2 do
yield i; // yield values instead of returning.
for i in 2..N by 2 do
yield i;
}
for i in oddsThenEvens(10) do write(i, ", ");
writeln();
// Iterators can also yield conditionally, the result of which can be nothing
iter absolutelyNothing(N): int {
for i in 1..N {
if N < i { // Always false
yield i; // Yield statement never happens
}
}
}
for i in absolutelyNothing(10) {
writeln("Woa there! absolutelyNothing yielded ", i);
}
// We can zipper together two or more iterators (who have the same number
// of iterations) using ``zip()`` to create a single zipped iterator, where each
// iteration of the zipped iterator yields a tuple of one value yielded
// from each iterator.
for (positive, negative) in zip(1..5, -5..-1) do
writeln((positive, negative));
// Zipper iteration is quite important in the assignment of arrays,
// slices of arrays, and array/loop expressions.
var fromThatArray : [1..#5] int = [1,2,3,4,5];
var toThisArray : [100..#5] int;
// Some zipper operations implement other operations.
// The first statement and the loop are equivalent.
toThisArray = fromThatArray;
for (i,j) in zip(toThisArray.domain, fromThatArray.domain) {
toThisArray[i] = fromThatArray[j];
}
// These two chunks are also equivalent.
toThisArray = [j in -100..#5] j;
writeln(toThisArray);
for (i, j) in zip(toThisArray.domain, -100..#5) {
toThisArray[i] = j;
}
writeln(toThisArray);
// This is very important in understanding why this statement exhibits a runtime error.
/*
var iterArray : [1..10] int = [i in 1..10] if (i % 2 == 1) then i;
*/
// Even though the domain of the array and the loop-expression are
// the same size, the body of the expression can be thought of as an iterator.
// Because iterators can yield nothing, that iterator yields a different number
// of things than the domain of the array or loop, which is not allowed.
/*
Classes
*/
// Classes are similar to those in C++ and Java, allocated on the heap.
class MyClass {
// Member variables
var memberInt : int;
var memberBool : bool = true;
// By default, any class that doesn't define an initializer gets a
// compiler-generated initializer, with one argument per field and
// the field's initial value as the argument's default value.
// Alternatively, the user can define initializers manually as shown
// in the following commented-out routine:
//
/* // proc init(val : real) {
// this.memberInt = ceil(val): int;
// }
*/
// Explicitly defined deinitializer.
// If we did not write one, we would get the compiler-generated deinitializer,
// which has an empty body.
proc deinit() {
writeln("MyClass deinitializer called ", (this.memberInt, this.memberBool));
}
// Class methods.
proc setMemberInt(val: int) {
this.memberInt = val;
}
proc setMemberBool(val: bool) {
this.memberBool = val;
}
proc getMemberInt(): int{
return this.memberInt;
}
proc getMemberBool(): bool {
return this.memberBool;
}
} // end MyClass
// Call compiler-generated initializer, using default value for memberBool.
{
var myObject = new owned MyClass(10);
myObject = new owned MyClass(memberInt = 10); // Equivalent
writeln(myObject.getMemberInt());
// Same, but provide a memberBool value explicitly.
var myDiffObject = new owned MyClass(-1, true);
myDiffObject = new owned MyClass(memberInt = -1,
memberBool = true); // Equivalent
writeln(myDiffObject);
// Similar, but rely on the default value of memberInt, passing in memberBool.
var myThirdObject = new owned MyClass(memberBool = true);
writeln(myThirdObject);
// If the user-defined initializer above had been uncommented, we could
// make the following calls:
//
/* // var myOtherObject = new MyClass(1.95);
// myOtherObject = new MyClass(val = 1.95);
// writeln(myOtherObject.getMemberInt());
*/
// We can define an operator on our class as well, but
// the definition has to be outside the class definition.
proc +(A : MyClass, B : MyClass) : owned MyClass {
return
new owned MyClass(memberInt = A.getMemberInt() + B.getMemberInt(),
memberBool = A.getMemberBool() || B.getMemberBool());
}
var plusObject = myObject + myDiffObject;
writeln(plusObject);
// Destruction of an object: calls the deinit() routine and frees its memory.
// ``unmanaged`` variables should have ``delete`` called on them.
// ``owned`` variables are destroyed when they go out of scope.
}
// Classes can inherit from one or more parent classes
class MyChildClass : MyClass {
var memberComplex: complex;
}
// Here's an example of generic classes.
class GenericClass {
type classType;
var classDomain: domain(1);
var classArray: [classDomain] classType;
// Explicit initializer.
proc init(type classType, elements : int) {
this.classType = classType;
this.classDomain = {1..elements};
// all generic and const fields must be initialized in "phase 1" prior
// to a call to the superclass initializer.
}
// Copy-style initializer.
// Note: We include a type argument whose default is the type of the first
// argument. This lets our initializer copy classes of different
// types and cast on the fly.
proc init(other : GenericClass(?),
type classType = other.classType) {
this.classType = classType;
this.classDomain = other.classDomain;
this.classArray = for o in other do o: classType; // copy and cast
}
// Define bracket notation on a GenericClass
// object so it can behave like a normal array
// i.e. ``objVar[i]`` or ``objVar(i)``
proc this(i : int) ref : classType {
return this.classArray[i];
}
// Define an implicit iterator for the class
// to yield values from the array to a loop
// i.e. ``for i in objVar do ...``
iter these() ref : classType {
for i in this.classDomain do
yield this[i];
}
} // end GenericClass
// Allocate an owned instance of our class
var realList = new owned GenericClass(real, 10);
// We can assign to the member array of the object using the bracket
// notation that we defined.
for i in realList.classDomain do realList[i] = i + 1.0;
// We can iterate over the values in our list with the iterator
// we defined.
for value in realList do write(value, ", ");
writeln();
// Make a copy of realList using the copy initializer.
var copyList = new owned GenericClass(realList);
for value in copyList do write(value, ", ");
writeln();
// Make a copy of realList and change the type, also using the copy initializer.
var copyNewTypeList = new owned GenericClass(realList, int);
for value in copyNewTypeList do write(value, ", ");
writeln();
/*
Modules
*/
// Modules are Chapel's way of managing name spaces.
// The files containing these modules do not need to be named after the modules
// (as in Java), but files implicitly name modules.
// For example, this file implicitly names the ``learnChapelInYMinutes`` module
module OurModule {
// We can use modules inside of other modules.
// Time is one of the standard modules.
use Time;
// We'll use this procedure in the parallelism section.
proc countdown(seconds: int) {
for i in 1..seconds by -1 {
writeln(i);
sleep(1);
}
}
// It is possible to create arbitrarily deep module nests.
// i.e. submodules of OurModule
module ChildModule {
proc foo() {
writeln("ChildModule.foo()");
}
}
module SiblingModule {
proc foo() {
writeln("SiblingModule.foo()");
}
}
} // end OurModule
// Using ``OurModule`` also uses all the modules it uses.
// Since ``OurModule`` uses ``Time``, we also use ``Time``.
use OurModule;
// At this point we have not used ``ChildModule`` or ``SiblingModule`` so
// their symbols (i.e. ``foo``) are not available to us. However, the module
// names are available, and we can explicitly call ``foo()`` through them.
SiblingModule.foo();
OurModule.ChildModule.foo();
// Now we use ``ChildModule``, enabling unqualified calls.
use ChildModule;
foo();
/*
Parallelism
*/
// In other languages, parallelism is typically done with
// complicated libraries and strange class structure hierarchies.
// Chapel has it baked right into the language.
// We can declare a main procedure, but all the code above main still gets
// executed.
proc main() {
// A ``begin`` statement will spin the body of that statement off
// into one new task.
// A ``sync`` statement will ensure that the progress of the main
// task will not progress until the children have synced back up.
sync {
begin { // Start of new task's body
var a = 0;
for i in 1..1000 do a += 1;
writeln("Done: ", a);
} // End of new tasks body
writeln("spun off a task!");
}
writeln("Back together");
proc printFibb(n: int) {
writeln("fibonacci(",n,") = ", fibonacci(n));
}
// A ``cobegin`` statement will spin each statement of the body into one new
// task. Notice here that the prints from each statement may happen in any
// order.
cobegin {
printFibb(20); // new task
printFibb(10); // new task
printFibb(5); // new task
{
// This is a nested statement body and thus is a single statement
// to the parent statement, executed by a single task.
writeln("this gets");
writeln("executed as");
writeln("a whole");
}
}
// A ``coforall`` loop will create a new task for EACH iteration.
// Again we see that prints happen in any order.
// NOTE: ``coforall`` should be used only for creating tasks!
// Using it to iterating over a structure is very a bad idea!
var num_tasks = 10; // Number of tasks we want
coforall taskID in 1..num_tasks {
writeln("Hello from task# ", taskID);
}
// ``forall`` loops are another parallel loop, but only create a smaller number
// of tasks, specifically ``--dataParTasksPerLocale=`` number of tasks.
forall i in 1..100 {
write(i, ", ");
}
writeln();
// Here we see that there are sections that are in order, followed by
// a section that would not follow (e.g. 1, 2, 3, 7, 8, 9, 4, 5, 6,).
// This is because each task is taking on a chunk of the range 1..10
// (1..3, 4..6, or 7..9) doing that chunk serially, but each task happens
// in parallel. Your results may depend on your machine and configuration
// For both the ``forall`` and ``coforall`` loops, the execution of the
// parent task will not continue until all the children sync up.
// ``forall`` loops are particularly useful for parallel iteration over arrays.
// Lets run an experiment to see how much faster a parallel loop is
use Time; // Import the Time module to use Timer objects
var timer: Timer;
var myBigArray: [{1..4000,1..4000}] real; // Large array we will write into
// Serial Experiment:
timer.start(); // Start timer
for (x,y) in myBigArray.domain { // Serial iteration
myBigArray[x,y] = (x:real) / (y:real);
}
timer.stop(); // Stop timer
writeln("Serial: ", timer.elapsed()); // Print elapsed time
timer.clear(); // Clear timer for parallel loop
// Parallel Experiment:
timer.start(); // start timer
forall (x,y) in myBigArray.domain { // Parallel iteration
myBigArray[x,y] = (x:real) / (y:real);
}
timer.stop(); // Stop timer
writeln("Parallel: ", timer.elapsed()); // Print elapsed time
timer.clear();
// You may have noticed that (depending on how many cores you have)
// the parallel loop went faster than the serial loop.
// The bracket style loop-expression described
// much earlier implicitly uses a ``forall`` loop.
[val in myBigArray] val = 1 / val; // Parallel operation
// Atomic variables, common to many languages, are ones whose operations
// occur uninterrupted. Multiple threads can therefore modify atomic
// variables and can know that their values are safe.
// Chapel atomic variables can be of type ``bool``, ``int``,
// ``uint``, and ``real``.
var uranium: atomic int;
uranium.write(238); // atomically write a variable
writeln(uranium.read()); // atomically read a variable
// Atomic operations are described as functions, so you can define your own.
uranium.sub(3); // atomically subtract a variable
writeln(uranium.read());
var replaceWith = 239;
var was = uranium.exchange(replaceWith);
writeln("uranium was ", was, " but is now ", replaceWith);
var isEqualTo = 235;
if uranium.compareAndSwap(isEqualTo, replaceWith) {
writeln("uranium was equal to ", isEqualTo,
" so replaced value with ", replaceWith);
} else {
writeln("uranium was not equal to ", isEqualTo,
" so value stays the same... whatever it was");
}
sync {
begin { // Reader task
writeln("Reader: waiting for uranium to be ", isEqualTo);
uranium.waitFor(isEqualTo);
writeln("Reader: uranium was set (by someone) to ", isEqualTo);
}
begin { // Writer task
writeln("Writer: will set uranium to the value ", isEqualTo, " in...");
countdown(3);
uranium.write(isEqualTo);
}
}
// ``sync`` variables have two states: empty and full.
// If you read an empty variable or write a full variable, you are waited
// until the variable is full or empty again.
var someSyncVar$: sync int; // varName$ is a convention not a law.
sync {
begin { // Reader task
writeln("Reader: waiting to read.");
var read_sync = someSyncVar$;
writeln("Reader: value is ", read_sync);
}
begin { // Writer task
writeln("Writer: will write in...");
countdown(3);
someSyncVar$ = 123;
}
}
// ``single`` vars can only be written once. A read on an unwritten ``single``
// results in a wait, but when the variable has a value it can be read indefinitely.
var someSingleVar$: single int; // varName$ is a convention not a law.
sync {
begin { // Reader task
writeln("Reader: waiting to read.");
for i in 1..5 {
var read_single = someSingleVar$;
writeln("Reader: iteration ", i,", and the value is ", read_single);
}
}
begin { // Writer task
writeln("Writer: will write in...");
countdown(3);
someSingleVar$ = 5; // first and only write ever.
}
}
// Here's an example using atomics and a ``sync`` variable to create a
// count-down mutex (also known as a multiplexer).
var count: atomic int; // our counter
var lock$: sync bool; // the mutex lock
count.write(2); // Only let two tasks in at a time.
lock$.writeXF(true); // Set lock$ to full (unlocked)
// Note: The value doesn't actually matter, just the state
// (full:unlocked / empty:locked)
// Also, writeXF() fills (F) the sync var regardless of its state (X)
coforall task in 1..5 { // Generate tasks
// Create a barrier
do {
lock$; // Read lock$ (wait)
} while (count.read() < 1); // Keep waiting until a spot opens up
count.sub(1); // decrement the counter
lock$.writeXF(true); // Set lock$ to full (signal)
// Actual 'work'
writeln("Task #", task, " doing work.");
sleep(2);
count.add(1); // Increment the counter
lock$.writeXF(true); // Set lock$ to full (signal)
}
// We can define the operations ``+ * & | ^ && || min max minloc maxloc``
// over an entire array using scans and reductions.
// Reductions apply the operation over the entire array and
// result in a scalar value.
var listOfValues: [1..10] int = [15,57,354,36,45,15,456,8,678,2];
var sumOfValues = + reduce listOfValues;
var maxValue = max reduce listOfValues; // 'max' give just max value
// ``maxloc`` gives max value and index of the max value.
// Note: We have to zip the array and domain together with the zip iterator.
var (theMaxValue, idxOfMax) = maxloc reduce zip(listOfValues,
listOfValues.domain);
writeln((sumOfValues, maxValue, idxOfMax, listOfValues[idxOfMax]));
// Scans apply the operation incrementally and return an array with the
// values of the operation at that index as it progressed through the
// array from ``array.domain.low`` to ``array.domain.high``.
var runningSumOfValues = + scan listOfValues;
var maxScan = max scan listOfValues;
writeln(runningSumOfValues);
writeln(maxScan);
} // end main()
CHICKEN:
;; #!/usr/bin/env csi -s
;; Run the CHICKEN REPL in the commandline as follows :
;; $ csi
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 0. Syntax
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Single line comments start with a semicolon
#| Block comments
can span multiple lines and...
#| can be nested
|#
|#
;; S-expression comments are used to comment out expressions
#; (display "nothing") ; discard this expression
;; CHICKEN has two fundamental pieces of syntax: Atoms and S-expressions
;; an atom is something that evaluates to itself
;; all builtin data types viz. numbers, chars, booleans, strings etc. are atoms
;; Furthermore an atom can be a symbol, an identifier, a keyword, a procedure
;; or the empty list (also called null)
'athing ;; => athing
'+ ;; => +
+ ;; => <procedure C_plus>
;; S-expressions (short for symbolic expressions) consists of one or more atoms
(quote +) ;; => + ; another way of writing '+
(+ 1 2 3) ;; => 6 ; this S-expression evaluates to a function call
'(+ 1 2 3) ;; => (+ 1 2 3) ; evaluates to a list
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 1. Primitive Datatypes and Operators
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Numbers
99999999999999999999 ;; integers
#b1010 ;; binary ; => 10
#o10 ;; octal ; => 8
#x8ded ;; hexadecimal ; => 36333
3.14 ;; real
6.02e+23
3/4 ;; rational
;;Characters and Strings
#\A ;; A char
"Hello, World!" ;; strings are fixed-length arrays of characters
;; Booleans
#t ;; true
#f ;; false
;; Function call is written as (f x y z ...)
;; where f is a function and x,y,z, ... are arguments
(print "Hello, World!") ;; => Hello, World!
;; formatted output
(printf "Hello, ~a.\n" "World") ;; => Hello, World.
;; print commandline arguments
(map print (command-line-arguments))
(list 'foo 'bar 'baz) ;; => (foo bar baz)
(string-append "pine" "apple") ;; => "pineapple"
(string-ref "tapioca" 3) ;; => #\i;; character 'i' is at index 3
(string->list "CHICKEN") ;; => (#\C #\H #\I #\C #\K #\E #\N)
(string-intersperse '("1" "2") ":") ;; => "1:2"
(string-split "1:2:3" ":") ;; => ("1" "2" "3")
;; Predicates are special functions that return boolean values
(atom? #t) ;; => #t
(symbol? #t) ;; => #f
(symbol? '+) ;; => #t
(procedure? +) ;; => #t
(pair? '(1 2)) ;; => #t
(pair? '(1 2 . 3)) ;; => #t
(pair? '()) ;; => #f
(list? '()) ;; => #t
;; Some arithmetic operations
(+ 1 1) ;; => 2
(- 8 1) ;; => 7
(* 10 2) ;; => 20
(expt 2 3) ;; => 8
(remainder 5 2) ;; => 1
(/ 35 5) ;; => 7
(/ 1 3) ;; => 0.333333333333333
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 2. Variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; You can create variables with define
;; A variable name can use any character except: ()[]{}",'`;#\
(define myvar 5)
myvar ;; => 5
;; Alias to a procedure
(define ** expt)
(** 2 3) ;; => 8
;; Accessing an undefined variable raises an exception
s ;; => Error: unbound variable: s
;; Local binding
(let ((me "Bob"))
(print me)) ;; => Bob
(print me) ;; => Error: unbound variable: me
;; Assign a new value to previously defined variable
(set! myvar 10)
myvar ;; => 10
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 3. Collections
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Pairs
;; 'cons' constructs pairs,
;; 'car' extracts the first element, 'cdr' extracts the rest of the elements
(cons 'subject 'verb) ;; => '(subject . verb)
(car (cons 'subject 'verb)) ;; => subject
(cdr (cons 'subject 'verb)) ;; => verb
;; Lists
;; cons creates a new list if the second item is a list
(cons 0 '()) ;; => (0)
(cons 1 (cons 2 (cons 3 '()))) ;; => (1 2 3)
;; 'list' is a convenience variadic constructor for lists
(list 1 2 3) ;; => (1 2 3)
;; Use 'append' to append lists together
(append '(1 2) '(3 4)) ;; => (1 2 3 4)
;; Some basic operations on lists
(map add1 '(1 2 3)) ;; => (2 3 4)
(reverse '(1 3 4 7)) ;; => (7 4 3 1)
(sort '(11 22 33 44) >) ;; => (44 33 22 11)
(define days '(SUN MON FRI))
(list-ref days 1) ;; => MON
(set! (list-ref days 1) 'TUE)
days ;; => (SUN TUE FRI)
;; Vectors
;; Vectors are heterogeneous structures whose elements are indexed by integers
;; A Vector typically occupies less space than a list of the same length
;; Random access of an element in a vector is faster than in a list
#(1 2 3) ;; => #(1 2 3) ;; literal syntax
(vector 'a 'b 'c) ;; => #(a b c)
(vector? #(1 2 3)) ;; => #t
(vector-length #(1 (2) "a")) ;; => 3
(vector-ref #(1 (2) (3 3)) 2);; => (3 3)
(define vec #(1 2 3))
(vector-set! vec 2 4)
vec ;; => #(1 2 4)
;; Vectors can be created from lists and vice-verca
(vector->list #(1 2 4)) ;; => '(1 2 4)
(list->vector '(a b c)) ;; => #(a b c)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 4. Functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Use 'lambda' to create functions.
;; A function always returns the value of its last expression
(lambda () "Hello World") ;; => #<procedure (?)>
;; Use extra parens around function definition to execute
((lambda () "Hello World")) ;; => Hello World ;; argument list is empty
;; A function with an argument
((lambda (x) (* x x)) 3) ;; => 9
;; A function with two arguments
((lambda (x y) (* x y)) 2 3) ;; => 6
;; assign a function to a variable
(define sqr (lambda (x) (* x x)))
sqr ;; => #<procedure (sqr x)>
(sqr 3) ;; => 9
;; We can shorten this using the function definition syntactic sugar
(define (sqr x) (* x x))
(sqr 3) ;; => 9
;; We can redefine existing procedures
(foldl cons '() '(1 2 3 4 5)) ;; => (((((() . 1) . 2) . 3) . 4) . 5)
(define (foldl func accu alist)
(if (null? alist)
accu
(foldl func (func (car alist) accu) (cdr alist))))
(foldl cons '() '(1 2 3 4 5)) ;; => (5 4 3 2 1)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 5. Equality
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; For numbers use '='
(= 3 3.0) ;; => #t
(= 2 1) ;; => #f
;; 'eq?' returns #t if two arguments refer to the same object in memory
;; In other words, it's a simple pointer comparison.
(eq? '() '()) ;; => #t ;; there's only one empty list in memory
(eq? (list 3) (list 3)) ;; => #f ;; not the same object
(eq? 'yes 'yes) ;; => #t
(eq? 3 3) ;; => #t ;; don't do this even if it works in this case
(eq? 3 3.0) ;; => #f ;; it's better to use '=' for number comparisons
(eq? "Hello" "Hello") ;; => #f
;; 'eqv?' is same as 'eq?' all datatypes except numbers and characters
(eqv? 3 3.0) ;; => #f
(eqv? (expt 2 3) (expt 2 3)) ;; => #t
(eqv? 'yes 'yes) ;; => #t
;; 'equal?' recursively compares the contents of pairs, vectors, and strings,
;; applying eqv? on other objects such as numbers and symbols.
;; A rule of thumb is that objects are generally equal? if they print the same.
(equal? '(1 2 3) '(1 2 3)) ;; => #t
(equal? #(a b c) #(a b c)) ;; => #t
(equal? 'a 'a) ;; => #t
(equal? "abc" "abc") ;; => #t
;; In Summary:
;; eq? tests if objects are identical
;; eqv? tests if objects are operationally equivalent
;; equal? tests if objects have same structure and contents
;; Comparing strings for equality
(string=? "Hello" "Hello") ;; => #t
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 6. Control Flow
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Conditionals
(if #t ;; test expression
"True" ;; then expression
"False") ;; else expression
;; => "True"
(if (> 3 2)
"yes"
"no") ;; => "yes"
;; In conditionals, all values that are not '#f' are treated as true.
;; 0, '(), #() "" , are all true values
(if 0
"0 is not false"
"0 is false") ;; => "0 is not false"
;; 'cond' chains a series of tests and returns as soon as it encounters a true condition
;; 'cond' can be used to simulate 'if/elseif/else' statements
(cond ((> 2 2) "not true so don't return this")
((< 2 5) "true, so return this")
(else "returning default")) ;; => "true, so return this"
;; A case expression is evaluated as follows:
;; The key is evaluated and compared with each datum in sense of 'eqv?',
;; The corresponding clause in the matching datum is evaluated and returned as result
(case (* 2 3) ;; the key is 6
((2 3 5 7) 'prime) ;; datum 1
((1 4 6 8) 'composite)) ;; datum 2; matched!
;; => composite
;; case with else clause
(case (car '(c d))
((a e i o u) 'vowel)
((w y) 'semivowel)
(else 'consonant)) ;; => consonant
;; Boolean expressions
;; 'and' returns the first expression that evaluates to #f
;; otherwise, it returns the result of the last expression
(and #t #f (= 2 2.0)) ;; => #f
(and (< 2 5) (> 2 0) "0 < 2 < 5") ;; => "0 < 2 < 5"
;; 'or' returns the first expression that evaluates to #t
;; otherwise the result of the last expression is returned
(or #f #t #f) ;; => #t
(or #f #f #f) ;; => #f
;; 'when' is like 'if' without the else expression
(when (positive? 5) "I'm positive") ;; => "I'm positive"
;; 'unless' is equivalent to (when (not <test>) <expr>)
(unless (null? '(1 2 3)) "not null") ;; => "not null"
;; Loops
;; loops can be created with the help of tail-recursions
(define (loop count)
(unless (= count 0)
(print "hello")
(loop (sub1 count))))
(loop 4) ;; => hello, hello ...
;; Or with a named let
(let loop ((i 0) (limit 5))
(when (< i limit)
(printf "i = ~a\n" i)
(loop (add1 i) limit))) ;; => i = 0, i = 1....
;; 'do' is another iteration construct
;; It initializes a set of variables and updates them in each iteration
;; A final expression is evaluated after the exit condition is met
(do ((x 0 (add1 x ))) ;; initialize x = 0 and add 1 in each iteration
((= x 10) (print "done")) ;; exit condition and final expression
(print x)) ;; command to execute in each step
;; => 0,1,2,3....9,done
;; Iteration over lists
(for-each (lambda (a) (print (* a a)))
'(3 5 7)) ;; => 9, 25, 49
;; 'map' is like for-each but returns a list
(map add1 '(11 22 33)) ;; => (12 23 34)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 7. Extensions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The CHICKEN core is very minimal, but additional features are provided by library extensions known as Eggs.
;; You can install Eggs with 'chicken-install <eggname>' command.
;; complex numbers
3+4i ;; => 3+2i
;; Supports fractions without falling back to inexact flonums
1/3 ;; => 1/3
;; provides support for large integers through bignums
(expt 9 20) ;; => 12157665459056928801
;; And other 'extended' functions
(log 10 (exp 1)) ;; => 2.30258509299405
(numerator 2/3) ;; => 2
;; 'utf8' provides unicode support
(import utf8)
"\u03BBx:(\u03BC\u0251.\u0251\u2192\u0251).xx" ;; => "λx:(μɑ.ɑ→ɑ).xx"
;; 'posix' provides file I/O and lots of other services for unix-like operating systems
;; Some of the functions are not available in Windows system,
;; See http://wiki.call-cc.org/man/5/Module%20(chicken%20file%20posix) for more details
;; Open a file to append, open "write only" and create file if it does not exist
(define outfn (file-open "chicken-hen.txt" (+ open/append open/wronly open/creat)))
;; write some text to the file
(file-write outfn "Did chicken came before hen?")
;; close the file
(file-close outfn)
;; Open the file "read only"
(define infn (file-open "chicken-hen.txt" open/rdonly))
;; read some text from the file
(file-read infn 30) ;; => ("Did chicken came before hen? ", 28)
(file-close infn)
;; CHICKEN also supports SRFI (Scheme Requests For Implementation) extensions
;; See 'http://srfi.schemers.org/srfi-implementers.html" to see srfi's supported by CHICKEN
(import srfi-1) ;; list library
(filter odd? '(1 2 3 4 5 6 7)) ;; => (1 3 5 7)
(count even? '(1 2 3 4 5)) ;; => 2
(take '(12 24 36 48 60) 3) ;; => (12 24 36)
(drop '(12 24 36 48 60) 2) ;; => (36 48 60)
(circular-list 'z 'q) ;; => z q z q ...
(import srfi-13) ;; string library
(string-reverse "pan") ;; => "nap"
(string-index "Turkey" #\k) ;; => 3
(string-every char-upper-case? "CHICKEN") ;; => #t
(string-join '("foo" "bar" "baz") ":") ;; => "foo:bar:baz"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 8. Macros
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; A 'for .. in ..' iteration like python, for lists
(define-syntax for
(syntax-rules (in)
((for elem in alist body ...)
(for-each (lambda (elem) body ...) alist))))
(for x in '(2 4 8 16)
(print x)) ;; => 2, 4, 8, 16
(for chr in (string->list "PENCHANT")
(print chr)) ;; => P, E, N, C, H, A, N, T
;; While loop
(define-syntax while
(syntax-rules ()
((while cond body ...)
(let loop ()
(when cond
body ...
(loop))))))
(let ((str "PENCHANT") (i 0))
(while (< i (string-length str)) ;; while (condition)
(print (string-ref str i)) ;; body
(set! i (add1 i))))
;; => P, E, N, C, H, A, N, T
;; Advanced Syntax-Rules Primer -> http://petrofsky.org/src/primer.txt
;; Macro system in chicken -> http://lists.gnu.org/archive/html/chicken-users/2008-04/msg00013.html
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 9. Modules
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Also See http://wiki.call-cc.org/man/5/Modules
;; The 'test' module exports a value named 'hello' and a macro named 'greet'
(module test (hello greet)
(import scheme)
(define-syntax greet
(syntax-rules ()
((_ whom)
(begin
(display "Hello, ")
(display whom)
(display " !\n") ) ) ) )
(define (hello)
(greet "world") ) )
;; we can define our modules in a separate file (say test.scm) and load them to the interpreter with
;; (load "test.scm")
;; import the module
(import test)
(hello) ;; => Hello, world !
(greet "schemers") ;; => Hello, schemers !
;; We can compile the module files in to shared libraries by using following command,
;; csc -s test.scm
;; (load "test.so")
;; Functors
;; Functors are high level modules that can be parameterized by other modules
;; Following functor requires another module named 'M' that provides a function called 'multiply'
;; The functor itself exports a generic function 'square'
(functor (squaring-functor (M (multiply))) (square)
(import scheme M)
(define (square x) (multiply x x)))
;; Module 'nums' can be passed as a parameter to 'squaring-functor'
(module nums (multiply)
(import scheme) ;; predefined modules
(define (multiply x y) (* x y)))
;; the final module can be imported and used in our program
(module number-squarer = (squaring-functor nums))
(import number-squarer)
(square 3) ;; => 9
;; We can instantiate the functor for other inputs
;; Here's another example module that can be passed to squaring-functor
(module stars (multiply)
(import chicken scheme) ;; chicken module for the 'use' keyword
(use srfi-1) ;; we can use external libraries in our module
(define (multiply x y)
(list-tabulate x (lambda _ (list-tabulate y (lambda _ '*))))))
(module star-squarer = (squaring-functor stars))
(import star-squarer)
(square 3) ;; => ((* * *)(* * *)(* * *))
citron:
# Comments start with a '#'
# All comments encompass a single line
###########################################
## 1. Primitive Data types and Operators
###########################################
# You have numbers
3. # 3
# Numbers are all doubles in interpreted mode
# Mathematical operator precedence is not respected.
# binary 'operators' are evaluated in ltr order
1 + 1. # 2
8 - 4. # 4
10 + 2 * 3. # 36
# Division is always floating division
35 / 2 # 17.5.
# Integer division is non-trivial, you may use floor
(35 / 2) floor # 17.
# Booleans are primitives
True.
False.
# Boolean messages
True not. # False
False not. # True
1 = 1. # True
1 !=: 1. # False
1 < 10. # True
# Here, `not` is a unary message to the object `Boolean`
# Messages are comparable to instance method calls
# And they have three different forms:
# 1. Unary messages: Length > 1, and they take no arguments:
False not.
# 2. Binary Messages: Length = 1, and they take a single argument:
False & True.
# 3. Keyword messages: must have at least one ':', they take as many arguments
# as they have `:` s
False either: 1 or: 2. # 2
# Strings
'This is a string'.
'There are no character types exposed to the user'.
# "You cannot use double quotes for strings" <- Error
# Strins can be summed
'Hello, ' + 'World!'. # 'Hello, World!'
# Strings allow access to their characters
'This is a beautiful string' at: 0. # 'T'
###########################################
## intermission: Basic Assignment
###########################################
# You may assign values to the current scope:
var name is value. # assigns `value` into `name`
# You may also assign values into the current object's namespace
my name is value. # assigns `value` into the current object's `name` property
# Please note that these names are checked at compile (read parse if in interpreted mode) time
# but you may treat them as dynamic assignments anyway
###########################################
## 2. Lists(Arrays?) and Tuples
###########################################
# Arrays are allowed to have multiple types
Array new < 1 ; 2 ; 'string' ; Nil. # Array new < 1 ; 2 ; 'string' ; Nil
# Tuples act like arrays, but are immutable.
# Any shenanigans degrade them to arrays, however
[1, 2, 'string']. # [1, 2, 'string']
# They can interoperate with arrays
[1, 'string'] + (Array new < 'wat'). # Array new < 1 ; 'string' ; 'wat'
# Indexing into them
[1, 2, 3] at: 1. # 2
# Some array operations
var arr is Array new < 1 ; 2 ; 3.
arr head. # 1
arr tail. # Array new < 2 ; 3.
arr init. # Array new < 1 ; 2.
arr last. # 3
arr push: 4. # Array new < 1 ; 2 ; 3 ; 4.
arr pop. # 4
arr pop: 1. # 2, `arr` is rebound to Array new < 1 ; 3.
# List comprehensions
[x * 2 + y,, arr, arr + [4, 5],, x > 1]. # Array ← 7 ; 9 ; 10 ; 11
# fresh variable names are bound as they are encountered,
# so `x` is bound to the values in `arr`
# and `y` is bound to the values in `arr + [4, 5]`
#
# The general format is: [expr,, bindings*,, predicates*]
####################################
## 3. Functions
####################################
# A simple function that takes two variables
var add is {:a:b ^a + b.}.
# this function will resolve all its names except the formal arguments
# in the context it is called in.
# Using the function
add applyTo: 3 and: 5. # 8
add applyAll: [3, 5]. # 8
# Also a (customizable -- more on this later) pseudo-operator allows for a shorthand
# of function calls
# By default it is REF[args]
add[3, 5]. # 8
# To customize this behaviour, you may simply use a compiler pragma:
#:callShorthand ()
# And then you may use the specified operator.
# Note that the allowed 'operator' can only be made of any of these: []{}()
# And you may mix-and-match (why would anyone do that?)
add(3, 5). # 8
# You may also use functions as operators in the following way:
3 `add` 5. # 8
# This call binds as such: add[(3), 5]
# because the default fixity is left, and the default precedence is 1
# You may change the precedence/fixity of this operator with a pragma
#:declare infixr 1 add
3 `add` 5. # 8
# now this binds as such: add[3, (5)].
# There is another form of functions too
# So far, the functions were resolved in a dynamic fashion
# But a lexically scoped block is also possible
var sillyAdd is {\:x:y add[x,y].}.
# In these blocks, you are not allowed to declare new variables
# Except with the use of Object::'letEqual:in:`
# And the last expression is implicitly returned.
# You may also use a shorthand for lambda expressions
var mul is \:x:y x * y.
# These capture the named bindings that are not present in their
# formal parameters, and retain them. (by ref)
###########################################
## 5. Control Flow
###########################################
# inline conditional-expressions
var citron is 1 = 1 either: 'awesome' or: 'awful'. # citron is 'awesome'
# multiple lines is fine too
var citron is 1 = 1
either: 'awesome'
or: 'awful'.
# looping
10 times: {:x
Pen writeln: x.
}. # 10. -- side effect: 10 lines in stdout, with numbers 0 through 9 in them
# Citron properly supports tail-call recursion in lexically scoped blocks
# So use those to your heart's desire
# mapping most data structures is as simple as `fmap:`
[1, 2, 3, 4] fmap: \:x x + 1. # [2, 3, 4, 5]
# You can use `foldl:accumulator:` to fold a list/tuple
[1, 2, 3, 4] foldl: (\:acc:x acc * 2 + x) accumulator: 4. # 90
# That expression is the same as
(2 * (2 * (2 * (2 * 4 + 1) + 2) + 3) + 4)
###################################
## 6. IO
###################################
# IO is quite simple
# With `Pen` being used for console output
# and Program::'input' and Program::'waitForInput' being used for console input
Pen writeln: 'Hello, ocean!' # prints 'Hello, ocean!\n' to the terminal
Pen writeln: Program waitForInput. # reads a line and prints it back
Clojure:
; Comments start with semicolons.
; Clojure is written in "forms", which are just
; lists of things inside parentheses, separated by whitespace.
;
; The clojure reader assumes that the first thing is a
; function or macro to call, and the rest are arguments.
; The first call in a file should be ns, to set the namespace
(ns learnclojure)
; More basic examples:
; str will create a string out of all its arguments
(str "Hello" " " "World") ; => "Hello World"
; Math is straightforward
(+ 1 1) ; => 2
(- 2 1) ; => 1
(* 1 2) ; => 2
(/ 2 1) ; => 2
; Equality is =
(= 1 1) ; => true
(= 2 1) ; => false
; You need not for logic, too
(not true) ; => false
; Nesting forms works as you expect
(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2
; Types
;;;;;;;;;;;;;
; Clojure uses Java's object types for booleans, strings and numbers.
; Use `class` to inspect them.
(class 1) ; Integer literals are java.lang.Long by default
(class 1.); Float literals are java.lang.Double
(class ""); Strings always double-quoted, and are java.lang.String
(class false) ; Booleans are java.lang.Boolean
(class nil); The "null" value is called nil
; If you want to create a literal list of data, use ' to stop it from
; being evaluated
'(+ 1 2) ; => (+ 1 2)
; (shorthand for (quote (+ 1 2)))
; You can eval a quoted list
(eval '(+ 1 2)) ; => 3
; Collections & Sequences
;;;;;;;;;;;;;;;;;;;
; Lists are linked-list data structures, while Vectors are array-backed.
; Vectors and Lists are java classes too!
(class [1 2 3]); => clojure.lang.PersistentVector
(class '(1 2 3)); => clojure.lang.PersistentList
; A list would be written as just (1 2 3), but we have to quote
; it to stop the reader thinking it's a function.
; Also, (list 1 2 3) is the same as '(1 2 3)
; "Collections" are just groups of data
; Both lists and vectors are collections:
(coll? '(1 2 3)) ; => true
(coll? [1 2 3]) ; => true
; "Sequences" (seqs) are abstract descriptions of lists of data.
; Only lists are seqs.
(seq? '(1 2 3)) ; => true
(seq? [1 2 3]) ; => false
; A seq need only provide an entry when it is accessed.
; So, seqs which can be lazy -- they can define infinite series:
(range 4) ; => (0 1 2 3)
(range) ; => (0 1 2 3 4 ...) (an infinite series)
(take 4 (range)) ; (0 1 2 3)
; Use cons to add an item to the beginning of a list or vector
(cons 4 [1 2 3]) ; => (4 1 2 3)
(cons 4 '(1 2 3)) ; => (4 1 2 3)
; Conj will add an item to a collection in the most efficient way.
; For lists, they insert at the beginning. For vectors, they insert at the end.
(conj [1 2 3] 4) ; => [1 2 3 4]
(conj '(1 2 3) 4) ; => (4 1 2 3)
; Use concat to add lists or vectors together
(concat [1 2] '(3 4)) ; => (1 2 3 4)
; Use filter, map to interact with collections
(map inc [1 2 3]) ; => (2 3 4)
(filter even? [1 2 3]) ; => (2)
; Use reduce to reduce them
(reduce + [1 2 3 4])
; = (+ (+ (+ 1 2) 3) 4)
; => 10
; Reduce can take an initial-value argument too
(reduce conj [] '(3 2 1))
; = (conj (conj (conj [] 3) 2) 1)
; => [3 2 1]
; Functions
;;;;;;;;;;;;;;;;;;;;;
; Use fn to create new functions. A function always returns
; its last statement.
(fn [] "Hello World") ; => fn
; (You need extra parens to call it)
((fn [] "Hello World")) ; => "Hello World"
; You can create a var using def
(def x 1)
x ; => 1
; Assign a function to a var
(def hello-world (fn [] "Hello World"))
(hello-world) ; => "Hello World"
; You can shorten this process by using defn
(defn hello-world [] "Hello World")
; The [] is the list of arguments for the function.
(defn hello [name]
(str "Hello " name))
(hello "Steve") ; => "Hello Steve"
; You can also use this shorthand to create functions:
(def hello2 #(str "Hello " %1))
(hello2 "Julie") ; => "Hello Julie"
; You can have multi-variadic functions, too
(defn hello3
([] "Hello World")
([name] (str "Hello " name)))
(hello3 "Jake") ; => "Hello Jake"
(hello3) ; => "Hello World"
; Functions can pack extra arguments up in a seq for you
(defn count-args [& args]
(str "You passed " (count args) " args: " args))
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"
; You can mix regular and packed arguments
(defn hello-count [name & args]
(str "Hello " name ", you passed " (count args) " extra args"))
(hello-count "Finn" 1 2 3)
; => "Hello Finn, you passed 3 extra args"
; Maps
;;;;;;;;;;
; Hash maps and array maps share an interface. Hash maps have faster lookups
; but don't retain key order.
(class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap
(class (hash-map :a 1 :b 2 :c 3)) ; => clojure.lang.PersistentHashMap
; Arraymaps will automatically become hashmaps through most operations
; if they get big enough, so you don't need to worry.
; Maps can use any hashable type as a key, but usually keywords are best
; Keywords are like strings with some efficiency bonuses
(class :a) ; => clojure.lang.Keyword
(def stringmap {"a" 1, "b" 2, "c" 3})
stringmap ; => {"a" 1, "b" 2, "c" 3}
(def keymap {:a 1, :b 2, :c 3})
keymap ; => {:a 1, :c 3, :b 2}
; By the way, commas are always treated as whitespace and do nothing.
; Retrieve a value from a map by calling it as a function
(stringmap "a") ; => 1
(keymap :a) ; => 1
; Keywords can be used to retrieve their value from a map, too!
(:b keymap) ; => 2
; Don't try this with strings.
;("a" stringmap)
; => Exception: java.lang.String cannot be cast to clojure.lang.IFn
; Retrieving a non-present key returns nil
(stringmap "d") ; => nil
; Use assoc to add new keys to hash-maps
(def newkeymap (assoc keymap :d 4))
newkeymap ; => {:a 1, :b 2, :c 3, :d 4}
; But remember, clojure types are immutable!
keymap ; => {:a 1, :b 2, :c 3}
; Use dissoc to remove keys
(dissoc keymap :a :b) ; => {:c 3}
; Sets
;;;;;;
(class #{1 2 3}) ; => clojure.lang.PersistentHashSet
(set [1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3}
; Add a member with conj
(conj #{1 2 3} 4) ; => #{1 2 3 4}
; Remove one with disj
(disj #{1 2 3} 1) ; => #{2 3}
; Test for existence by using the set as a function:
(#{1 2 3} 1) ; => 1
(#{1 2 3} 4) ; => nil
; There are more functions in the clojure.sets namespace.
; Useful forms
;;;;;;;;;;;;;;;;;
; Logic constructs in clojure are just macros, and look like
; everything else
(if false "a" "b") ; => "b"
(if false "a") ; => nil
; Use let to create temporary bindings
(let [a 1 b 2]
(> a b)) ; => false
; Group statements together with do
(do
(print "Hello")
"World") ; => "World" (prints "Hello")
; Functions have an implicit do
(defn print-and-say-hello [name]
(print "Saying hello to " name)
(str "Hello " name))
(print-and-say-hello "Jeff") ;=> "Hello Jeff" (prints "Saying hello to Jeff")
; So does let
(let [name "Urkel"]
(print "Saying hello to " name)
(str "Hello " name)) ; => "Hello Urkel" (prints "Saying hello to Urkel")
; Use the threading macros (-> and ->>) to express transformations of
; data more clearly.
; The "Thread-first" macro (->) inserts into each form the result of
; the previous, as the first argument (second item)
(->
{:a 1 :b 2}
(assoc :c 3) ;=> (assoc {:a 1 :b 2} :c 3)
(dissoc :b)) ;=> (dissoc (assoc {:a 1 :b 2} :c 3) :b)
; This expression could be written as:
; (dissoc (assoc {:a 1 :b 2} :c 3) :b)
; and evaluates to {:a 1 :c 3}
; The double arrow does the same thing, but inserts the result of
; each line at the *end* of the form. This is useful for collection
; operations in particular:
(->>
(range 10)
(map inc) ;=> (map inc (range 10))
(filter odd?) ;=> (filter odd? (map inc (range 10)))
(into [])) ;=> (into [] (filter odd? (map inc (range 10))))
; Result: [1 3 5 7 9]
; When you are in a situation where you want more freedom as where to
; put the result of previous data transformations in an
; expression, you can use the as-> macro. With it, you can assign a
; specific name to transformations' output and use it as a
; placeholder in your chained expressions:
(as-> [1 2 3] input
(map inc input);=> You can use last transform's output at the last position
(nth input 2) ;=> and at the second position, in the same expression
(conj [4 5 6] input 8 9 10)) ;=> or in the middle !
; Result: [4 5 6 4 8 9 10]
; Modules
;;;;;;;;;;;;;;;
; Use "use" to get all functions from the module
(use 'clojure.set)
; Now we can use set operations
(intersection #{1 2 3} #{2 3 4}) ; => #{2 3}
(difference #{1 2 3} #{2 3 4}) ; => #{1}
; You can choose a subset of functions to import, too
(use '[clojure.set :only [intersection]])
; Use require to import a module
(require 'clojure.string)
; Use / to call functions from a module
; Here, the module is clojure.string and the function is blank?
(clojure.string/blank? "") ; => true
; You can give a module a shorter name on import
(require '[clojure.string :as str])
(str/replace "This is a test." #"[a-o]" str/upper-case) ; => "THIs Is A tEst."
; (#"" denotes a regular expression literal)
; You can use require (and use, but don't) from a namespace using :require.
; You don't need to quote your modules if you do it this way.
(ns test
(:require
[clojure.string :as str]
[clojure.set :as set]))
; Java
;;;;;;;;;;;;;;;;;
; Java has a huge and useful standard library, so
; you'll want to learn how to get at it.
; Use import to load a java module
(import java.util.Date)
; You can import from an ns too.
(ns test
(:import java.util.Date
java.util.Calendar))
; Use the class name with a "." at the end to make a new instance
(Date.) ; <a date object>
; Use . to call methods. Or, use the ".method" shortcut
(. (Date.) getTime) ; <a timestamp>
(.getTime (Date.)) ; exactly the same thing.
; Use / to call static methods
(System/currentTimeMillis) ; <a timestamp> (system is always present)
; Use doto to make dealing with (mutable) classes more tolerable
(import java.util.Calendar)
(doto (Calendar/getInstance)
(.set 2000 1 1 0 0 0)
.getTime) ; => A Date. set to 2000-01-01 00:00:00
; STM
;;;;;;;;;;;;;;;;;
; Software Transactional Memory is the mechanism clojure uses to handle
; persistent state. There are a few constructs in clojure that use this.
; An atom is the simplest. Pass it an initial value
(def my-atom (atom {}))
; Update an atom with swap!.
; swap! takes a function and calls it with the current value of the atom
; as the first argument, and any trailing arguments as the second
(swap! my-atom assoc :a 1) ; Sets my-atom to the result of (assoc {} :a 1)
(swap! my-atom assoc :b 2) ; Sets my-atom to the result of (assoc {:a 1} :b 2)
; Use '@' to dereference the atom and get the value
my-atom ;=> Atom<#...> (Returns the Atom object)
@my-atom ; => {:a 1 :b 2}
; Here's a simple counter using an atom
(def counter (atom 0))
(defn inc-counter []
(swap! counter inc))
(inc-counter)
(inc-counter)
(inc-counter)
(inc-counter)
(inc-counter)
@counter ; => 5
; Other STM constructs are refs and agents.
; Refs: http://clojure.org/refs
; Agents: http://clojure.org/agents
Clojure:
;; Define a macro using defmacro. Your macro should output a list that can
;; be evaluated as clojure code.
;;
;; This macro is the same as if you wrote (reverse "Hello World")
(defmacro my-first-macro []
(list reverse "Hello World"))
;; Inspect the result of a macro using macroexpand or macroexpand-1.
;;
;; Note that the call must be quoted.
(macroexpand '(my-first-macro))
;; -> (#<core$reverse clojure.core$reverse@xxxxxxxx> "Hello World")
;; You can eval the result of macroexpand directly:
(eval (macroexpand '(my-first-macro)))
; -> (\d \l \o \r \W \space \o \l \l \e \H)
;; But you should use this more succinct, function-like syntax:
(my-first-macro) ; -> (\d \l \o \r \W \space \o \l \l \e \H)
;; You can make things easier on yourself by using the more succinct quote syntax
;; to create lists in your macros:
(defmacro my-first-quoted-macro []
'(reverse "Hello World"))
(macroexpand '(my-first-quoted-macro))
;; -> (reverse "Hello World")
;; Notice that reverse is no longer function object, but a symbol.
;; Macros can take arguments.
(defmacro inc2 [arg]
(list + 2 arg))
(inc2 2) ; -> 4
;; But, if you try to do this with a quoted list, you'll get an error, because
;; the argument will be quoted too. To get around this, clojure provides a
;; way of quoting macros: `. Inside `, you can use ~ to get at the outer scope
(defmacro inc2-quoted [arg]
`(+ 2 ~arg))
(inc2-quoted 2)
;; You can use the usual destructuring args. Expand list variables using ~@
(defmacro unless [arg & body]
`(if (not ~arg)
(do ~@body))) ; Remember the do!
(macroexpand '(unless true (reverse "Hello World")))
;; ->
;; (if (clojure.core/not true) (do (reverse "Hello World")))
;; (unless) evaluates and returns its body if the first argument is false.
;; Otherwise, it returns nil
(unless true "Hello") ; -> nil
(unless false "Hello") ; -> "Hello"
;; Used without care, macros can do great evil by clobbering your vars
(defmacro define-x []
'(do
(def x 2)
(list x)))
(def x 4)
(define-x) ; -> (2)
(list x) ; -> (2)
;; To avoid this, use gensym to get a unique identifier
(gensym 'x) ; -> x1281 (or some such thing)
(defmacro define-x-safely []
(let [sym (gensym 'x)]
`(do
(def ~sym 2)
(list ~sym))))
(def x 4)
(define-x-safely) ; -> (2)
(list x) ; -> (4)
;; You can use # within ` to produce a gensym for each symbol automatically
(defmacro define-x-hygienically []
`(do
(def x# 2)
(list x#)))
(def x 4)
(define-x-hygienically) ; -> (2)
(list x) ; -> (4)
;; It's typical to use helper functions with macros. Let's create a few to
;; help us support a (dumb) inline arithmetic syntax
(declare inline-2-helper)
(defn clean-arg [arg]
(if (seq? arg)
(inline-2-helper arg)
arg))
(defn apply-arg
"Given args [x (+ y)], return (+ x y)"
[val [op arg]]
(list op val (clean-arg arg)))
(defn inline-2-helper
[[arg1 & ops-and-args]]
(let [ops (partition 2 ops-and-args)]
(reduce apply-arg (clean-arg arg1) ops)))
;; We can test it immediately, without creating a macro
(inline-2-helper '(a + (b - 2) - (c * 5))) ; -> (- (+ a (- b 2)) (* c 5))
; However, we'll need to make it a macro if we want it to be run at compile time
(defmacro inline-2 [form]
(inline-2-helper form))
(macroexpand '(inline-2 (1 + (3 / 2) - (1 / 2) + 1)))
; -> (+ (- (+ 1 (/ 3 2)) (/ 1 2)) 1)
(inline-2 (1 + (3 / 2) - (1 / 2) + 1))
; -> 3 (actually, 3N, since the number got cast to a rational fraction with /)
COBOL:
*COBOL. Coding like it's 1985.
*Compiles with GnuCOBOL in OpenCobolIDE 4.7.6.
*COBOL has significant differences between legacy (COBOL-85)
*and modern (COBOL-2002 and COBOL-2014) versions.
*Legacy versions require columns 1-6 to be blank (they are used
*to store the index number of the punched card).
*A '*' in column 7 means a comment.
*In legacy COBOL, a comment can only be a full line.
*Modern COBOL doesn't require fixed columns and uses *> for
*a comment, which can appear in the middle of a line.
*Legacy COBOL also imposes a limit on maximum line length.
*Keywords have to be in capitals in legacy COBOL,
*but are case insensitive in modern.
*Although modern COBOL allows you to use mixed-case characters
*it is still common to use all caps when writing COBOL code.
*This is what most professional COBOL developers do.
*COBOL statements end with a period.
*COBOL code is broken up into 4 divisions.
*Those divisions, in order, are:
*IDENTIFICATION DIVISION.
*ENVIRONMENT DIVISION.
*DATA DIVISION.
*PROCEDURE DIVISION.
*First, we must give our program an ID.
*The IDENTIFICATION DIVISION can include other values too,
*but they are comments only. PROGRAM-ID is the only one that
*is mandatory.
IDENTIFICATION DIVISION.
PROGRAM-ID. LEARN.
AUTHOR. JOHN DOE.
DATE-WRITTEN. 05/02/2020.
*Let's declare some variables.
*We do this in the WORKING-STORAGE section within the DATA DIVISION.
*Each data item (aka variable) starts with a level number,
*then the name of the item, followed by a PICTURE clause
*describing the type of data that the variable will contain.
*Almost every COBOL programmer will abbreviate PICTURE as PIC.
*A is for alphabetic, X is for alphanumeric, and 9 is for numeric.
*example:
01 MYNAME PIC XXXXXXXXXX. *> A 10 character string.
*But counting all those Xs can lead to errors,
*so the above code can be re-written as
01 MYNAME PIC X(10).
*Here are some more examples:
01 AGE PICTURE 9(3). *> A number up to 3 digits.
01 BIRTH_YEAR PIC S9(7). *> A signed number up to 7 digits.
01 LAST_NAME PIC X(10). *> A string up to 10 characters.
*In COBOL, multiple spaces are the same as a single space, so it
*is common to use multiple spaces to line up your code so that it
*is easier for other coders to read.
*Now let's write some code. Here is a simple, Hello World program.
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 THE-MESSAGE PIC X(20).
PROCEDURE DIVISION.
DISPLAY "STARTING PROGRAM".
MOVE "HELLO WORLD" TO THE-MESSAGE.
DISPLAY THE-MESSAGE.
STOP RUN.
*The above code will output:
*STARTING PROGRAM
*HELLO WORLD
********COBOL can perform math***************
ADD 1 TO AGE GIVING NEW-AGE.
SUBTRACT 1 FROM COUNT.
DIVIDE VAR-1 INTO VAR-2 GIVING VAR-3.
COMPUTE TOTAL-COUNT = COUNT1 PLUS COUNT2.
*********PERFORM********************
*The PERFORM keyword allows you to jump to another specified
*section of the code, and then to return to the next executable
*statement once the specified section of code is completed.
*You must write the full word, PERFORM, you cannot abbreviate it.
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLOCOBOL.
PROCEDURE DIVISION.
FIRST-PARA.
DISPLAY 'THIS IS IN FIRST-PARA'.
*skip SECOND-PARA and perform 3rd & 4th
*then after performing THIRD-PARA and FOURTH-PARA,
*return here and continue the program until STOP RUN.
PERFORM THIRD-PARA THRU FOURTH-PARA.
SECOND-PARA.
DISPLAY 'THIS IS IN SECOND-PARA'.
STOP RUN.
THIRD-PARA.
DISPLAY 'THIS IS IN THIRD-PARA'.
FOURTH-PARA.
DISPLAY 'THIS IS IN FOURTH-PARA'.
*When you compile and execute the above program, it produces the
*following result (note the order):
*THIS IS IN FIRST-PARA
*THIS IS IN THIRD-PARA
*THIS IS IN FOURTH-PARA
*THIS IS IN SECOND-PARA
**********Combining variables together using STRING ***********
*Now it is time to learn about two related COBOL verbs: STRING and
*UNSTRING.
*The STRING verb is used to concatenate, or put together, two or
*more strings.
*UNSTRING is used, not surprisingly, to separate a
*string into two or more smaller strings.
*It is important that you remember to use DELIMITED BY when you
*are using STRING or UNSTRING in your program.
IDENTIFICATION DIVISION.
PROGRAM-ID. LEARNING.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 FULL-NAME PIC X(20).
01 FIRST-NAME PIC X(13) VALUE "BOB GIBBERISH".
01 LAST-NAME PIC X(5) VALUE "COBB".
PROCEDURE DIVISION.
STRING FIRST-NAME DELIMITED BY SPACE
" "
LAST-NAME DELIMITED BY SIZE
INTO FULL-NAME
END-STRING.
DISPLAY "THE FULL NAME IS: "FULL-NAME.
STOP RUN.
*The above code will output:
*THE FULL NAME IS: BOB COBB
*Let's examine it to see why.
*First, we declared all of our variables, including the one that
*we are creating by the string command, in the DATA DIVISION.
*The action takes place down in the PROCEDURE DIVISION.
*We start with the STRING keyword and end with END-STRING. In
*between we list what we want to combine together into the larger,
*master variable. Here, we are combining FIRST-NAME, a space, and
*LAST-NAME.
*The DELIMITED BY phrase that follows FIRST-NAME and
*LAST-NAME tells the program how much of each variable we want to
*capture.
*DELIMITED BY SPACE tells the program to start at the beginning,
*and capture the variable until it runs into a space.
*DELIMITED BY SIZE tells the program to capture the full size of
*the variable.
*Since we have DELIMITED BY SPACE after FIRST-NAME, the GIBBERISH
*part is ignored.
*To make this clearer, change line 10 in the above code to
STRING FIRST-NAME DELIMITED BY SIZE
*and then re-run the program. This time the output is:
*THE FULL NAME IS: BOB GIBBERISH COBB
CoffeeScript:
# Comments are similar to Ruby and Python, using the hash symbol `#`
###
Block comments are like these, and they translate directly to '/ *'s and '* /'s
for the resulting JavaScript code.
You should understand most of JavaScript semantics
before continuing.
###
# Assignment:
number = 42 #=> var number = 42;
opposite = true #=> var opposite = true;
# Conditions:
number = -42 if opposite #=> if(opposite) { number = -42; }
# Functions:
square = (x) -> x * x #=> var square = function(x) { return x * x; }
fill = (container, liquid = "coffee") ->
"Filling the #{container} with #{liquid}..."
#=>var fill;
#
#fill = function(container, liquid) {
# if (liquid == null) {
# liquid = "coffee";
# }
# return "Filling the " + container + " with " + liquid + "...";
#};
# Ranges:
list = [1..5] #=> var list = [1, 2, 3, 4, 5];
# Objects:
math =
root: Math.sqrt
square: square
cube: (x) -> x * square x
#=> var math = {
# "root": Math.sqrt,
# "square": square,
# "cube": function(x) { return x * square(x); }
# };
# Splats:
race = (winner, runners...) ->
print winner, runners
#=>race = function() {
# var runners, winner;
# winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
# return print(winner, runners);
# };
# Existence:
alert "I knew it!" if elvis?
#=> if(typeof elvis !== "undefined" && elvis !== null) { alert("I knew it!"); }
# Array comprehensions:
cubes = (math.cube num for num in list)
#=>cubes = (function() {
# var _i, _len, _results;
# _results = [];
# for (_i = 0, _len = list.length; _i < _len; _i++) {
# num = list[_i];
# _results.push(math.cube(num));
# }
# return _results;
# })();
foods = ['broccoli', 'spinach', 'chocolate']
eat food for food in foods when food isnt 'chocolate'
#=>foods = ['broccoli', 'spinach', 'chocolate'];
#
#for (_k = 0, _len2 = foods.length; _k < _len2; _k++) {
# food = foods[_k];
# if (food !== 'chocolate') {
# eat(food);
# }
#}
Common Lisp:
;;;-----------------------------------------------------------------------------
;;; 0. Syntax
;;;-----------------------------------------------------------------------------
;;; General form
;;; CL has two fundamental pieces of syntax: ATOM and S-EXPRESSION.
;;; Typically, grouped S-expressions are called `forms`.
10 ; an atom; it evaluates to itself
:thing ; another atom; evaluating to the symbol :thing
t ; another atom, denoting true
(+ 1 2 3 4) ; an s-expression
'(4 :foo t) ; another s-expression
;;; Comments
;;; Single-line comments start with a semicolon; use four for file-level
;;; comments, three for section descriptions, two inside definitions, and one
;;; for single lines. For example,
;;;; life.lisp
;;; Foo bar baz, because quu quux. Optimized for maximum krakaboom and umph.
;;; Needed by the function LINULUKO.
(defun meaning (life)
"Return the computed meaning of LIFE"
(let ((meh "abc"))
;; Invoke krakaboom
(loop :for x :across meh
:collect x))) ; store values into x, then return it
;;; Block comments, on the other hand, allow for free-form comments. They are
;;; delimited with #| and |#
#| This is a block comment which
can span multiple lines and
#|
they can be nested!
|#
|#
;;; Environment
;;; A variety of implementations exist; most are standards-conformant. SBCL
;;; is a good starting point. Third party libraries can be easily installed with
;;; Quicklisp
;;; CL is usually developed with a text editor and a Read Eval Print
;;; Loop (REPL) running at the same time. The REPL allows for interactive
;;; exploration of the program while it is running "live".
;;;-----------------------------------------------------------------------------
;;; 1. Primitive datatypes and operators
;;;-----------------------------------------------------------------------------
;;; Symbols
'foo ; => FOO Notice that the symbol is upper-cased automatically.
;;; INTERN manually creates a symbol from a string.
(intern "AAAA") ; => AAAA
(intern "aaa") ; => |aaa|
;;; Numbers
9999999999999999999999 ; integers
#b111 ; binary => 7
#o111 ; octal => 73
#x111 ; hexadecimal => 273
3.14159s0 ; single
3.14159d0 ; double
1/2 ; ratios
#C(1 2) ; complex numbers
;;; Function application are written as (f x y z ...) where f is a function and
;;; x, y, z, ... are the arguments.
(+ 1 2) ; => 3
;;; If you want to create literal data, use QUOTE to prevent it from being
;;; evaluated
(quote (+ 1 2)) ; => (+ 1 2)
(quote a) ; => A
;;; The shorthand for QUOTE is '
'(+ 1 2) ; => (+ 1 2)
'a ; => A
;;; Basic arithmetic operations
(+ 1 1) ; => 2
(- 8 1) ; => 7
(* 10 2) ; => 20
(expt 2 3) ; => 8
(mod 5 2) ; => 1
(/ 35 5) ; => 7
(/ 1 3) ; => 1/3
(+ #C(1 2) #C(6 -4)) ; => #C(7 -2)
;;; Booleans
t ; true; any non-NIL value is true
nil ; false; also, the empty list: ()
(not nil) ; => T
(and 0 t) ; => T
(or 0 nil) ; => 0
;;; Characters
#\A ; => #\A
#\λ ; => #\GREEK_SMALL_LETTER_LAMDA
#\u03BB ; => #\GREEK_SMALL_LETTER_LAMDA
;;; Strings are fixed-length arrays of characters
"Hello, world!"
"Benjamin \"Bugsy\" Siegel" ; backslash is an escaping character
;;; Strings can be concatenated
(concatenate 'string "Hello, " "world!") ; => "Hello, world!"
;;; A string can be treated like a sequence of characters
(elt "Apple" 0) ; => #\A
;;; FORMAT is used to create formatted output, which ranges from simple string
;;; interpolation to loops and conditionals. The first argument to FORMAT
;;; determines where will the formatted string go. If it is NIL, FORMAT
;;; simply returns the formatted string as a value; if it is T, FORMAT outputs
;;; to the standard output, usually the screen, then it returns NIL.
(format nil "~A, ~A!" "Hello" "world") ; => "Hello, world!"
(format t "~A, ~A!" "Hello" "world") ; => NIL
;;;-----------------------------------------------------------------------------
;;; 2. Variables
;;;-----------------------------------------------------------------------------
;;; You can create a global (dynamically scoped) variable using DEFVAR and
;;; DEFPARAMETER. The variable name can use any character except: ()",'`;#|\
;;; The difference between DEFVAR and DEFPARAMETER is that re-evaluating a
;;; DEFVAR expression doesn't change the value of the variable. DEFPARAMETER,
;;; on the other hand, does.
;;; By convention, dynamically scoped variables have earmuffs in their name.
(defparameter *some-var* 5)
*some-var* ; => 5
;;; You can also use unicode characters.
(defparameter *AΛB* nil)
;;; Accessing a previously unbound variable results in an UNBOUND-VARIABLE
;;; error, however it is defined behavior. Don't do it.
;;; You can create local bindings with LET. In the following snippet, `me` is
;;; bound to "dance with you" only within the (let ...). LET always returns
;;; the value of the last `form` in the LET form.
(let ((me "dance with you")) me) ; => "dance with you"
;;;-----------------------------------------------------------------------------;
;;; 3. Structs and collections
;;;-----------------------------------------------------------------------------;
;;; Structs
(defstruct dog name breed age)
(defparameter *rover*
(make-dog :name "rover"
:breed "collie"
:age 5))
*rover* ; => #S(DOG :NAME "rover" :BREED "collie" :AGE 5)
(dog-p *rover*) ; => T
(dog-name *rover*) ; => "rover"
;;; DOG-P, MAKE-DOG, and DOG-NAME are all automatically created by DEFSTRUCT
;;; Pairs
;;; CONS constructs pairs. CAR and CDR return the head and tail of a CONS-pair.
(cons 'SUBJECT 'VERB) ; => '(SUBJECT . VERB)
(car (cons 'SUBJECT 'VERB)) ; => SUBJECT
(cdr (cons 'SUBJECT 'VERB)) ; => VERB
;;; Lists
;;; Lists are linked-list data structures, made of CONS pairs and end with a
;;; NIL (or '()) to mark the end of the list
(cons 1 (cons 2 (cons 3 nil))) ; => '(1 2 3)
;;; LIST is a convenience variadic constructor for lists
(list 1 2 3) ; => '(1 2 3)
;;; When the first argument to CONS is an atom and the second argument is a
;;; list, CONS returns a new CONS-pair with the first argument as the first
;;; item and the second argument as the rest of the CONS-pair
(cons 4 '(1 2 3)) ; => '(4 1 2 3)
;;; Use APPEND to join lists
(append '(1 2) '(3 4)) ; => '(1 2 3 4)
;;; Or CONCATENATE
(concatenate 'list '(1 2) '(3 4)) ; => '(1 2 3 4)
;;; Lists are a very central type, so there is a wide variety of functionality for
;;; them, a few examples:
(mapcar #'1+ '(1 2 3)) ; => '(2 3 4)
(mapcar #'+ '(1 2 3) '(10 20 30)) ; => '(11 22 33)
(remove-if-not #'evenp '(1 2 3 4)) ; => '(2 4)
(every #'evenp '(1 2 3 4)) ; => NIL
(some #'oddp '(1 2 3 4)) ; => T
(butlast '(subject verb object)) ; => (SUBJECT VERB)
;;; Vectors
;;; Vector's literals are fixed-length arrays
#(1 2 3) ; => #(1 2 3)
;;; Use CONCATENATE to add vectors together
(concatenate 'vector #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6)
;;; Arrays
;;; Both vectors and strings are special-cases of arrays.
;;; 2D arrays
(make-array (list 2 2)) ; => #2A((0 0) (0 0))
(make-array '(2 2)) ; => #2A((0 0) (0 0))
(make-array (list 2 2 2)) ; => #3A(((0 0) (0 0)) ((0 0) (0 0)))
;;; Caution: the default initial values of MAKE-ARRAY are implementation-defined.
;;; To explicitly specify them:
(make-array '(2) :initial-element 'unset) ; => #(UNSET UNSET)
;;; To access the element at 1, 1, 1:
(aref (make-array (list 2 2 2)) 1 1 1) ; => 0
;;; This value is implementation-defined:
;;; NIL on ECL, 0 on SBCL and CCL.
;;; Adjustable vectors
;;; Adjustable vectors have the same printed representation as
;;; fixed-length vector's literals.
(defparameter *adjvec* (make-array '(3) :initial-contents '(1 2 3)
:adjustable t :fill-pointer t))
*adjvec* ; => #(1 2 3)
;;; Adding new elements
(vector-push-extend 4 *adjvec*) ; => 3
*adjvec* ; => #(1 2 3 4)
;;; Sets, naively, are just lists:
(set-difference '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1)
(intersection '(1 2 3 4) '(4 5 6 7)) ; => 4
(union '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1 4 5 6 7)
(adjoin 4 '(1 2 3 4)) ; => (1 2 3 4)
;;; However, you'll need a better data structure than linked lists when working
;;; with larger data sets
;;; Dictionaries are implemented as hash tables.
;;; Create a hash table
(defparameter *m* (make-hash-table))
;;; Set value
(setf (gethash 'a *m*) 1)
;;; Retrieve value
(gethash 'a *m*) ; => 1, T
;;; CL expressions have the ability to return multiple values.
(values 1 2) ; => 1, 2
;;; which can be bound with MULTIPLE-VALUE-BIND
(multiple-value-bind (x y)
(values 1 2)
(list y x))
; => '(2 1)
;;; GETHASH is an example of a function that returns multiple values. The first
;;; value it return is the value of the key in the hash table; if the key is
;;; not found it returns NIL.
;;; The second value determines if that key is indeed present in the hash
;;; table. If a key is not found in the table it returns NIL. This behavior
;;; allows us to check if the value of a key is actually NIL.
;;; Retrieving a non-present value returns nil
(gethash 'd *m*) ;=> NIL, NIL
;;; You can provide a default value for missing keys
(gethash 'd *m* :not-found) ; => :NOT-FOUND
;;; Let's handle the multiple return values here in code.
(multiple-value-bind (a b)
(gethash 'd *m*)
(list a b))
; => (NIL NIL)
(multiple-value-bind (a b)
(gethash 'a *m*)
(list a b))
; => (1 T)
;;;-----------------------------------------------------------------------------
;;; 3. Functions
;;;-----------------------------------------------------------------------------
;;; Use LAMBDA to create anonymous functions. Functions always returns the
;;; value of the last expression. The exact printable representation of a
;;; function varies between implementations.
(lambda () "Hello World") ; => #<FUNCTION (LAMBDA ()) {1004E7818B}>
;;; Use FUNCALL to call anonymous functions
(funcall (lambda () "Hello World")) ; => "Hello World"
(funcall #'+ 1 2 3) ; => 6
;;; A call to FUNCALL is also implied when the lambda expression is the CAR of
;;; an unquoted list
((lambda () "Hello World")) ; => "Hello World"
((lambda (val) val) "Hello World") ; => "Hello World"
;;; FUNCALL is used when the arguments are known beforehand. Otherwise, use APPLY
(apply #'+ '(1 2 3)) ; => 6
(apply (lambda () "Hello World") nil) ; => "Hello World"
;;; To name a function, use DEFUN
(defun hello-world () "Hello World")
(hello-world) ; => "Hello World"
;;; The () in the definition above is the list of arguments
(defun hello (name) (format nil "Hello, ~A" name))
(hello "Steve") ; => "Hello, Steve"
;;; Functions can have optional arguments; they default to NIL
(defun hello (name &optional from)
(if from
(format t "Hello, ~A, from ~A" name from)
(format t "Hello, ~A" name)))
(hello "Jim" "Alpacas") ; => Hello, Jim, from Alpacas
;;; The default values can also be specified
(defun hello (name &optional (from "The world"))
(format nil "Hello, ~A, from ~A" name from))
(hello "Steve") ; => Hello, Steve, from The world
(hello "Steve" "the alpacas") ; => Hello, Steve, from the alpacas
;;; Functions also have keyword arguments to allow non-positional arguments
(defun generalized-greeter (name &key (from "the world") (honorific "Mx"))
(format t "Hello, ~A ~A, from ~A" honorific name from))
(generalized-greeter "Jim")
; => Hello, Mx Jim, from the world
(generalized-greeter "Jim" :from "the alpacas you met last summer" :honorific "Mr")
; => Hello, Mr Jim, from the alpacas you met last summer
;;;-----------------------------------------------------------------------------
;;; 4. Equality
;;;-----------------------------------------------------------------------------
;;; CL has a sophisticated equality system. Some are covered here.
;;; For numbers, use `='
(= 3 3.0) ; => T
(= 2 1) ; => NIL
;;; For object identity (approximately) use EQL
(eql 3 3) ; => T
(eql 3 3.0) ; => NIL
(eql (list 3) (list 3)) ; => NIL
;;; for lists, strings, and bit-vectors use EQUAL
(equal (list 'a 'b) (list 'a 'b)) ; => T
(equal (list 'a 'b) (list 'b 'a)) ; => NIL
;;;-----------------------------------------------------------------------------
;;; 5. Control Flow
;;;-----------------------------------------------------------------------------
;;; Conditionals
(if t ; test expression
"this is true" ; then expression
"this is false") ; else expression
; => "this is true"
;;; In conditionals, all non-NIL values are treated as true
(member 'Groucho '(Harpo Groucho Zeppo)) ; => '(GROUCHO ZEPPO)
(if (member 'Groucho '(Harpo Groucho Zeppo))
'yep
'nope)
; => 'YEP
;;; COND chains a series of tests to select a result
(cond ((> 2 2) (error "wrong!"))
((< 2 2) (error "wrong again!"))
(t 'ok)) ; => 'OK
;;; TYPECASE switches on the type of the value
(typecase 1
(string :string)
(integer :int))
; => :int
;;; Looping
;;; Recursion
(defun fact (n)
(if (< n 2)
1
(* n (fact(- n 1)))))
(fact 5) ; => 120
;;; Iteration
(defun fact (n)
(loop :for result = 1 :then (* result i)
:for i :from 2 :to n
:finally (return result)))
(fact 5) ; => 120
(loop :for x :across "abcd" :collect x)
; => (#\a #\b #\c #\d)
(dolist (i '(1 2 3 4))
(format t "~A" i))
; => 1234
;;;-----------------------------------------------------------------------------
;;; 6. Mutation
;;;-----------------------------------------------------------------------------
;;; Use SETF to assign a new value to an existing variable. This was
;;; demonstrated earlier in the hash table example.
(let ((variable 10))
(setf variable 2))
; => 2
;;; Good Lisp style is to minimize the use of destructive functions and to avoid
;;; mutation when reasonable.
;;;-----------------------------------------------------------------------------
;;; 7. Classes and objects
;;;-----------------------------------------------------------------------------
;;; No more animal classes. Let's have Human-Powered Mechanical
;;; Conveyances.
(defclass human-powered-conveyance ()
((velocity
:accessor velocity
:initarg :velocity)
(average-efficiency
:accessor average-efficiency
:initarg :average-efficiency))
(:documentation "A human powered conveyance"))
;;; The arguments to DEFCLASS, in order are:
;;; 1. class name
;;; 2. superclass list
;;; 3. slot list
;;; 4. optional specifiers
;;; When no superclass list is set, the empty list defaults to the
;;; standard-object class. This *can* be changed, but not until you
;;; know what you're doing. Look up the Art of the Metaobject Protocol
;;; for more information.
(defclass bicycle (human-powered-conveyance)
((wheel-size
:accessor wheel-size
:initarg :wheel-size
:documentation "Diameter of the wheel.")
(height
:accessor height
:initarg :height)))
(defclass recumbent (bicycle)
((chain-type
:accessor chain-type
:initarg :chain-type)))
(defclass unicycle (human-powered-conveyance) nil)
(defclass canoe (human-powered-conveyance)
((number-of-rowers
:accessor number-of-rowers
:initarg :number-of-rowers)))
;;; Calling DESCRIBE on the HUMAN-POWERED-CONVEYANCE class in the REPL gives:
(describe 'human-powered-conveyance)
; COMMON-LISP-USER::HUMAN-POWERED-CONVEYANCE
; [symbol]
;
; HUMAN-POWERED-CONVEYANCE names the standard-class #<STANDARD-CLASS
; HUMAN-POWERED-CONVEYANCE>:
; Documentation:
; A human powered conveyance
; Direct superclasses: STANDARD-OBJECT
; Direct subclasses: UNICYCLE, BICYCLE, CANOE
; Not yet finalized.
; Direct slots:
; VELOCITY
; Readers: VELOCITY
; Writers: (SETF VELOCITY)
; AVERAGE-EFFICIENCY
; Readers: AVERAGE-EFFICIENCY
; Writers: (SETF AVERAGE-EFFICIENCY)
;;; Note the reflective behavior available. CL was designed to be an
;;; interactive system
;;; To define a method, let's find out what our circumference of the
;;; bike wheel turns out to be using the equation: C = d * pi
(defmethod circumference ((object bicycle))
(* pi (wheel-size object)))
;;; PI is defined as a built-in in CL
;;; Let's suppose we find out that the efficiency value of the number
;;; of rowers in a canoe is roughly logarithmic. This should probably be set
;;; in the constructor/initializer.
;;; To initialize your instance after CL gets done constructing it:
(defmethod initialize-instance :after ((object canoe) &rest args)
(setf (average-efficiency object) (log (1+ (number-of-rowers object)))))
;;; Then to construct an instance and check the average efficiency...
(average-efficiency (make-instance 'canoe :number-of-rowers 15))
; => 2.7725887
;;;-----------------------------------------------------------------------------
;;; 8. Macros
;;;-----------------------------------------------------------------------------
;;; Macros let you extend the syntax of the language. CL doesn't come
;;; with a WHILE loop, however, it's trivial to write one. If we obey our
;;; assembler instincts, we wind up with:
(defmacro while (condition &body body)
"While `condition` is true, `body` is executed.
`condition` is tested prior to each execution of `body`"
(let ((block-name (gensym)) (done (gensym)))
`(tagbody
,block-name
(unless ,condition
(go ,done))
(progn
,@body)
(go ,block-name)
,done)))
;;; Let's look at the high-level version of this:
(defmacro while (condition &body body)
"While `condition` is true, `body` is executed.
`condition` is tested prior to each execution of `body`"
`(loop while ,condition
do
(progn
,@body)))
;;; However, with a modern compiler, this is not required; the LOOP form
;;; compiles equally well and is easier to read.
;;; Note that ``` is used, as well as `,` and `@`. ``` is a quote-type operator
;;; known as quasiquote; it allows the use of `,` . `,` allows "unquoting"
;;; variables. @ interpolates lists.
;;; GENSYM creates a unique symbol guaranteed to not exist elsewhere in
;;; the system. This is because macros are expanded at compile time and
;;; variables declared in the macro can collide with variables used in
;;; regular code.
;;; See Practical Common Lisp and On Lisp for more information on macros.
Coq:
(*** Comments ***)
(* Comments are enclosed in (* and *). It's fine to nest comments. *)
(* There are no single-line comments. *)
(*** Variables and functions ***)
(* The Coq proof assistant can be controlled and queried by a command
language called the vernacular. Vernacular keywords are capitalized and
the commands end with a period. Variable and function declarations are
formed with the Definition vernacular. *)
Definition x := 10.
(* Coq can sometimes infer the types of arguments, but it is common practice
to annotate with types. *)
Definition inc_nat (x : nat) : nat := x + 1.
(* There exists a large number of vernacular commands for querying
information. These can be very useful. *)
Compute (1 + 1). (* 2 : nat *) (* Compute a result. *)
Check tt. (* tt : unit *) (* Check the type of an expressions *)
About plus. (* Prints information about an object *)
(* Print information including the definition *)
Print true. (* Inductive bool : Set := true : Bool | false : Bool *)
Search nat. (* Returns a large list of nat related values *)
Search "_ + _". (* You can also search on patterns *)
Search (?a -> ?a -> bool). (* Patterns can have named parameters *)
Search (?a * ?a).
(* Locate tells you where notation is coming from. Very helpful when you
encounter new notation. *)
Locate "+".
(* Calling a function with insufficient number of arguments does not cause
an error, it produces a new function. *)
Definition make_inc x y := x + y. (* make_inc is nat -> nat -> nat *)
Definition inc_2 := make_inc 2. (* inc_2 is nat -> nat *)
Compute inc_2 3. (* Evaluates to 5 *)
(* Definitions can be chained with "let ... in" construct. This is roughly
the same to assigning values to multiple variables before using them in
expressions in imperative languages. *)
Definition add_xy : nat := let x := 10 in
let y := 20 in
x + y.
(* Pattern matching is somewhat similar to switch statement in imperative
languages, but offers a lot more expressive power. *)
Definition is_zero (x : nat) :=
match x with
| 0 => true
| _ => false (* The "_" pattern means "anything else". *)
end.
(* You can define recursive function definition using the Fixpoint
vernacular.*)
Fixpoint factorial n := match n with
| 0 => 1
| (S n') => n * factorial n'
end.
(* Function application usually doesn't need parentheses around arguments *)
Compute factorial 5. (* 120 : nat *)
(* ...unless the argument is an expression. *)
Compute factorial (5-1). (* 24 : nat *)
(* You can define mutually recursive functions using "with" *)
Fixpoint is_even (n : nat) : bool := match n with
| 0 => true
| (S n) => is_odd n
end with
is_odd n := match n with
| 0 => false
| (S n) => is_even n
end.
(* As Coq is a total programming language, it will only accept programs when
it can understand they terminate. It can be most easily seen when the
recursive call is on a pattern matched out subpiece of the input, as then
the input is always decreasing in size. Getting Coq to understand that
functions terminate is not always easy. See the references at the end of
the article for more on this topic. *)
(* Anonymous functions use the following syntax: *)
Definition my_square : nat -> nat := fun x => x * x.
Definition my_id (A : Type) (x : A) : A := x.
Definition my_id2 : forall A : Type, A -> A := fun A x => x.
Compute my_id nat 3. (* 3 : nat *)
(* You can ask Coq to infer terms with an underscore *)
Compute my_id _ 3.
(* An implicit argument of a function is an argument which can be inferred
from contextual knowledge. Parameters enclosed in {} are implicit by
default *)
Definition my_id3 {A : Type} (x : A) : A := x.
Compute my_id3 3. (* 3 : nat *)
(* Sometimes it may be necessary to turn this off. You can make all
arguments explicit again with @ *)
Compute @my_id3 nat 3.
(* Or give arguments by name *)
Compute my_id3 (A:=nat) 3.
(* Coq has the ability to extract code to OCaml, Haskell, and Scheme *)
Require Extraction.
Extraction Language OCaml.
Extraction "factorial.ml" factorial.
(* The above produces a file factorial.ml and factorial.mli that holds:
type nat =
| O
| S of nat
(** val add : nat -> nat -> nat **)
let rec add n m =
match n with
| O -> m
| S p -> S (add p m)
(** val mul : nat -> nat -> nat **)
let rec mul n m =
match n with
| O -> O
| S p -> add m (mul p m)
(** val factorial : nat -> nat **)
let rec factorial n = match n with
| O -> S O
| S n' -> mul n (factorial n')
*)
(*** Notation ***)
(* Coq has a very powerful Notation system that can be used to write
expressions in more natural forms. *)
Compute Nat.add 3 4. (* 7 : nat *)
Compute 3 + 4. (* 7 : nat *)
(* Notation is a syntactic transformation applied to the text of the program
before being evaluated. Notation is organized into notation scopes. Using
different notation scopes allows for a weak notion of overloading. *)
(* Imports the Zarith module holding definitions related to the integers Z *)
Require Import ZArith.
(* Notation scopes can be opened *)
Open Scope Z_scope.
(* Now numerals and addition are defined on the integers. *)
Compute 1 + 7. (* 8 : Z *)
(* Integer equality checking *)
Compute 1 =? 2. (* false : bool *)
(* Locate is useful for finding the origin and definition of notations *)
Locate "_ =? _". (* Z.eqb x y : Z_scope *)
Close Scope Z_scope.
(* We're back to nat being the default interpretation of "+" *)
Compute 1 + 7. (* 8 : nat *)
(* Scopes can also be opened inline with the shorthand % *)
Compute (3 * -7)%Z. (* -21%Z : Z *)
(* Coq declares by default the following interpretation scopes: core_scope,
type_scope, function_scope, nat_scope, bool_scope, list_scope, int_scope,
uint_scope. You may also want the numerical scopes Z_scope (integers) and
Q_scope (fractions) held in the ZArith and QArith module respectively. *)
(* You can print the contents of scopes *)
Print Scope nat_scope.
(*
Scope nat_scope
Delimiting key is nat
Bound to classes nat Nat.t
"x 'mod' y" := Nat.modulo x y
"x ^ y" := Nat.pow x y
"x ?= y" := Nat.compare x y
"x >= y" := ge x y
"x > y" := gt x y
"x =? y" := Nat.eqb x y
"x <? y" := Nat.ltb x y
"x <=? y" := Nat.leb x y
"x <= y <= z" := and (le x y) (le y z)
"x <= y < z" := and (le x y) (lt y z)
"n <= m" := le n m
"x < y <= z" := and (lt x y) (le y z)
"x < y < z" := and (lt x y) (lt y z)
"x < y" := lt x y
"x / y" := Nat.div x y
"x - y" := Init.Nat.sub x y
"x + y" := Init.Nat.add x y
"x * y" := Init.Nat.mul x y
*)
(* Coq has exact fractions available as the type Q in the QArith module.
Floating point numbers and real numbers are also available but are a more
advanced topic, as proving properties about them is rather tricky. *)
Require Import QArith.
Open Scope Q_scope.
Compute 1. (* 1 : Q *)
(* Only 1 and 0 are interpreted as fractions by Q_scope *)
Compute 2. (* 2 : nat *)
Compute (2 # 3). (* The fraction 2/3 *)
Compute (1 # 3) ?= (2 # 6). (* Eq : comparison *)
Close Scope Q_scope.
Compute ( (2 # 3) / (1 # 5) )%Q. (* 10 # 3 : Q *)
(*** Common data structures ***)
(* Many common data types are included in the standard library *)
(* The unit type has exactly one value, tt *)
Check tt. (* tt : unit *)
(* The option type is useful for expressing computations that might fail *)
Compute None. (* None : option ?A *)
Check Some 3. (* Some 3 : option nat *)
(* The type sum A B allows for values of either type A or type B *)
Print sum.
Check inl 3. (* inl 3 : nat + ?B *)
Check inr true. (* inr true : ?A + bool *)
Check sum bool nat. (* (bool + nat)%type : Set *)
Check (bool + nat)%type. (* Notation for sum *)
(* Tuples are (optionally) enclosed in parentheses, items are separated
by commas. *)
Check (1, true). (* (1, true) : nat * bool *)
Compute prod nat bool. (* (nat * bool)%type : Set *)
Definition my_fst {A B : Type} (x : A * B) : A := match x with
| (a,b) => a
end.
(* A destructuring let is available if a pattern match is irrefutable *)
Definition my_fst2 {A B : Type} (x : A * B) : A := let (a,b) := x in
a.
(*** Lists ***)
(* Lists are built by using cons and nil or by using notation available in
list_scope. *)
Compute cons 1 (cons 2 (cons 3 nil)). (* (1 :: 2 :: 3 :: nil)%list : list nat *)
Compute (1 :: 2 :: 3 :: nil)%list.
(* There is also list notation available in the ListNotations modules *)
Require Import List.
Import ListNotations.
Compute [1 ; 2 ; 3]. (* [1; 2; 3] : list nat *)
(* There is a large number of list manipulation functions available,
including:
• length
• head : first element (with default)
• tail : all but first element
• app : appending
• rev : reverse
• nth : accessing n-th element (with default)
• map : applying a function
• flat_map : applying a function returning lists
• fold_left : iterator (from head to tail)
• fold_right : iterator (from tail to head)
*)
Definition my_list : list nat := [47; 18; 34].
Compute List.length my_list. (* 3 : nat *)
(* All functions in coq must be total, so indexing requires a default value *)
Compute List.nth 1 my_list 0. (* 18 : nat *)
Compute List.map (fun x => x * 2) my_list. (* [94; 36; 68] : list nat *)
Compute List.filter (fun x => Nat.eqb (Nat.modulo x 2) 0) my_list.
(* [18; 34] : list nat *)
Compute (my_list ++ my_list)%list. (* [47; 18; 34; 47; 18; 34] : list nat *)
(*** Strings ***)
Require Import Strings.String.
(* Use double quotes for string literals. *)
Compute "hi"%string.
Open Scope string_scope.
(* Strings can be concatenated with the "++" operator. *)
Compute String.append "Hello " "World". (* "Hello World" : string *)
Compute "Hello " ++ "World". (* "Hello World" : string *)
(* Strings can be compared for equality *)
Compute String.eqb "Coq is fun!" "Coq is fun!". (* true : bool *)
Compute "no" =? "way". (* false : bool *)
Close Scope string_scope.
(*** Other Modules ***)
(* Other Modules in the standard library that may be of interest:
• Logic : Classical logic and dependent equality
• Arith : Basic Peano arithmetic
• PArith : Basic positive integer arithmetic
• NArith : Basic binary natural number arithmetic
• ZArith : Basic relative integer arithmetic
• Numbers : Various approaches to natural, integer and cyclic numbers
(currently axiomatically and on top of 2^31 binary words)
• Bool : Booleans (basic functions and results)
• Lists : Monomorphic and polymorphic lists (basic functions and results),
Streams (infinite sequences defined with co-inductive types)
• Sets : Sets (classical, constructive, finite, infinite, power set, etc.)
• FSets : Specification and implementations of finite sets and finite maps
(by lists and by AVL trees)
• Reals : Axiomatization of real numbers (classical, basic functions,
integer part, fractional part, limit, derivative, Cauchy series,
power series and results,...)
• Relations : Relations (definitions and basic results)
• Sorting : Sorted list (basic definitions and heapsort correctness)
• Strings : 8-bit characters and strings
• Wellfounded : Well-founded relations (basic results)
*)
(*** User-defined data types ***)
(* Because Coq is dependently typed, defining type aliases is no different
than defining an alias for a value. *)
Definition my_three : nat := 3.
Definition my_nat : Type := nat.
(* More interesting types can be defined using the Inductive vernacular.
Simple enumeration can be defined like so *)
Inductive ml := OCaml | StandardML | Coq.
Definition lang := Coq. (* Has type "ml". *)
(* For more complicated types, you will need to specify the types of the
constructors. *)
(* Type constructors don't need to be empty. *)
Inductive my_number := plus_infinity
| nat_value : nat -> my_number.
Compute nat_value 3. (* nat_value 3 : my_number *)
(* Record syntax is sugar for tuple-like types. It defines named accessor
functions for the components. Record types are defined with the notation
{...} *)
Record Point2d (A : Set) := mkPoint2d { x2 : A ; y2 : A }.
(* Record values are constructed with the notation {|...|} *)
Definition mypoint : Point2d nat := {| x2 := 2 ; y2 := 3 |}.
Compute x2 nat mypoint. (* 2 : nat *)
Compute mypoint.(x2 nat). (* 2 : nat *)
(* Types can be parameterized, like in this type for "list of lists of
anything". 'a can be substituted with any type. *)
Definition list_of_lists a := list (list a).
Definition list_list_nat := list_of_lists nat.
(* Types can also be recursive. Like in this type analogous to
built-in list of naturals. *)
Inductive my_nat_list :=
EmptyList | NatList : nat -> my_nat_list -> my_nat_list.
Compute NatList 1 EmptyList. (* NatList 1 EmptyList : my_nat_list *)
(** Matching type constructors **)
Inductive animal := Dog : string -> animal | Cat : string -> animal.
Definition say x :=
match x with
| Dog x => (x ++ " says woof")%string
| Cat x => (x ++ " says meow")%string
end.
Compute say (Cat "Fluffy"). (* "Fluffy says meow". *)
(** Traversing data structures with pattern matching **)
(* Recursive types can be traversed with pattern matching easily.
Let's see how we can traverse a data structure of the built-in list type.
Even though the built-in cons ("::") looks like an infix operator,
it's actually a type constructor and can be matched like any other. *)
Fixpoint sum_list l :=
match l with
| [] => 0
| head :: tail => head + (sum_list tail)
end.
Compute sum_list [1; 2; 3]. (* Evaluates to 6 *)
(*** A Taste of Proving ***)
(* Explaining the proof language is out of scope for this tutorial, but here
is a taste to whet your appetite. Check the resources below for more. *)
(* A fascinating feature of dependently type based theorem provers is that
the same primitive constructs underly the proof language as the
programming features. For example, we can write and prove the
proposition A and B implies A in raw Gallina *)
Definition my_theorem : forall A B, A /\ B -> A :=
fun A B ab => match ab with
| (conj a b) => a
end.
(* Or we can prove it using tactics. Tactics are a macro language to help
build proof terms in a more natural style and automate away some
drudgery. *)
Theorem my_theorem2 : forall A B, A /\ B -> A.
Proof.
intros A B ab. destruct ab as [ a b ]. apply a.
Qed.
(* We can easily prove simple polynomial equalities using the
automated tactic ring. *)
Require Import Ring.
Require Import Arith.
Theorem simple_poly : forall (x : nat), (x + 1) * (x + 2) = x * x + 3 * x + 2.
Proof. intros. ring. Qed.
(* Here we prove the closed form for the sum of all numbers 1 to n using
induction *)
Fixpoint sumn (n : nat) : nat :=
match n with
| 0 => 0
| (S n') => n + (sumn n')
end.
Theorem sum_formula : forall n, 2 * (sumn n) = (n + 1) * n.
Proof. intros n. induction n.
- reflexivity. (* 0 = 0 base case *)
- simpl. ring [IHn]. (* induction step *)
Qed.
Crystal:
# This is a comment
# Everything is an object
nil.class #=> Nil
100.class #=> Int32
true.class #=> Bool
# Falsey values are: nil, false and null pointers
!nil #=> true : Bool
!false #=> true : Bool
!0 #=> false : Bool
# Integers
1.class #=> Int32
# Five signed integer types
1_i8.class #=> Int8
1_i16.class #=> Int16
1_i32.class #=> Int32
1_i64.class #=> Int64
1_i128.class #=> Int128
# Five unsigned integer types
1_u8.class #=> UInt8
1_u16.class #=> UInt16
1_u32.class #=> UInt32
1_u64.class #=> UInt64
1_u128.class #=> UInt128
2147483648.class #=> Int64
9223372036854775808.class #=> UInt64
# Binary numbers
0b1101 #=> 13 : Int32
# Octal numbers
0o123 #=> 83 : Int32
# Hexadecimal numbers
0xFE012D #=> 16646445 : Int32
0xfe012d #=> 16646445 : Int32
# Floats
1.0.class #=> Float64
# There are two floating point types
1.0_f32.class #=> Float32
1_f32.class #=> Float32
1e10.class #=> Float64
1.5e10.class #=> Float64
1.5e-7.class #=> Float64
# Chars use 'a' pair of single quotes
'a'.class #=> Char
# Chars are 32-bit unicode
'あ' #=> 'あ' : Char
# Unicode codepoint
'\u0041' #=> 'A' : Char
# Strings use a "pair" of double quotes
"s".class #=> String
# Strings are immutable
s = "hello, " #=> "hello, " : String
s.object_id #=> 134667712 : UInt64
s += "Crystal"
s #=> "hello, Crystal" : String
s.object_id #=> 142528472 : UInt64
# Supports interpolation
"sum = #{1 + 2}" #=> "sum = 3" : String
# Multiline string
"This is
multiline string" #=> "This is\n multiline string"
# String with double quotes
%(hello "world") #=> "hello \"world\""
# Symbols
# Immutable, reusable constants represented internally as Int32 integer value.
# They're often used instead of strings to efficiently convey specific,
# meaningful values
:symbol.class #=> Symbol
sentence = :question? # :"question?" : Symbol
sentence == :question? #=> true : Bool
sentence == :exclamation! #=> false : Bool
sentence == "question?" #=> false : Bool
# Arrays
[1, 2, 3].class #=> Array(Int32)
[1, "hello", 'x'].class #=> Array(Char | Int32 | String)
# Empty arrays should specify a type
[] # Syntax error: for empty arrays use '[] of ElementType'
[] of Int32 #=> [] : Array(Int32)
Array(Int32).new #=> [] : Array(Int32)
# Arrays can be indexed
array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] : Array(Int32)
array[0] #=> 1 : Int32
array[10] # raises IndexError
array[-6] # raises IndexError
array[10]? #=> nil : (Int32 | Nil)
array[-6]? #=> nil : (Int32 | Nil)
# From the end
array[-1] #=> 5
# With a start index and size
array[2, 3] #=> [3, 4, 5]
# Or with range
array[1..3] #=> [2, 3, 4]
# Add to an array
array << 6 #=> [1, 2, 3, 4, 5, 6]
# Remove from the end of the array
array.pop #=> 6
array #=> [1, 2, 3, 4, 5]
# Remove from the beginning of the array
array.shift #=> 1
array #=> [2, 3, 4, 5]
# Check if an item exists in an array
array.includes? 3 #=> true
# Special syntax for an array of string and an array of symbols
%w(one two three) #=> ["one", "two", "three"] : Array(String)
%i(one two three) #=> [:one, :two, :three] : Array(Symbol)
# There is a special array syntax with other types too, as long as
# they define a .new and a #<< method
set = Set{1, 2, 3} #=> Set{1, 2, 3}
set.class #=> Set(Int32)
# The above is equivalent to
set = Set(typeof(1, 2, 3)).new #=> Set{} : Set(Int32)
set << 1 #=> Set{1} : Set(Int32)
set << 2 #=> Set{1, 2} : Set(Int32)
set << 3 #=> Set{1, 2, 3} : Set(Int32)
# Hashes
{1 => 2, 3 => 4}.class #=> Hash(Int32, Int32)
{1 => 2, 'a' => 3}.class #=> Hash(Char| Int32, Int32)
# Empty hashes must specify a type
{} # Syntax Error: for empty hashes use '{} of KeyType => ValueType'
{} of Int32 => Int32 # {} : Hash(Int32, Int32)
Hash(Int32, Int32).new # {} : Hash(Int32, Int32)
# Hashes can be quickly looked up by key
hash = {"color" => "green", "number" => 5}
hash["color"] #=> "green"
hash["no_such_key"] #=> Missing hash key: "no_such_key" (KeyError)
hash["no_such_key"]? #=> nil
# The type of the returned value is based on all key types
hash["number"] #=> 5 : (Int32 | String)
# Check existence of keys hash
hash.has_key? "color" #=> true
# Special notation for symbol and string keys
{key1: 'a', key2: 'b'} # {:key1 => 'a', :key2 => 'b'}
{"key1": 'a', "key2": 'b'} # {"key1" => 'a', "key2" => 'b'}
# Special hash literal syntax with other types too, as long as
# they define a .new and a #[]= methods
class MyType
def []=(key, value)
puts "do stuff"
end
end
MyType{"foo" => "bar"}
# The above is equivalent to
tmp = MyType.new
tmp["foo"] = "bar"
tmp
# Ranges
1..10 #=> Range(Int32, Int32)
Range.new(1, 10).class #=> Range(Int32, Int32)
# Can be inclusive or exclusive
(3..5).to_a #=> [3, 4, 5]
(3...5).to_a #=> [3, 4]
# Check whether range includes the given value or not
(1..8).includes? 2 #=> true
# Tuples are a fixed-size, immutable, stack-allocated sequence of values of
# possibly different types.
{1, "hello", 'x'}.class #=> Tuple(Int32, String, Char)
# Access tuple's value by its index
tuple = {:key1, :key2}
tuple[1] #=> :key2
tuple[2] #=> Error: index out of bounds for Tuple(Symbol, Symbol) (2 not in -2..1)
# Can be expanded into multiple variables
a, b, c = {:a, 'b', "c"}
a #=> :a
b #=> 'b'
c #=> "c"
# Procs represent a function pointer with an optional context (the closure data)
# It is typically created with a proc literal
proc = ->(x : Int32) { x.to_s }
proc.class # Proc(Int32, String)
# Or using the new method
Proc(Int32, String).new { |x| x.to_s }
# Invoke proc with call method
proc.call 10 #=> "10"
# Control statements
if true
"if statement"
elsif false
"else-if, optional"
else
"else, also optional"
end
puts "if as a suffix" if true
# If as an expression
a = if 2 > 1
3
else
4
end
a #=> 3
# Ternary if
a = 1 > 2 ? 3 : 4 #=> 4
# Case statement
cmd = "move"
action = case cmd
when "create"
"Creating..."
when "copy"
"Copying..."
when "move"
"Moving..."
when "delete"
"Deleting..."
end
action #=> "Moving..."
# Loops
index = 0
while index <= 3
puts "Index: #{index}"
index += 1
end
# Index: 0
# Index: 1
# Index: 2
# Index: 3
index = 0
until index > 3
puts "Index: #{index}"
index += 1
end
# Index: 0
# Index: 1
# Index: 2
# Index: 3
# But the preferable way is to use each
(1..3).each do |index|
puts "Index: #{index}"
end
# Index: 1
# Index: 2
# Index: 3
# Variable's type depends on the type of the expression
# in control statements
if a < 3
a = "hello"
else
a = true
end
typeof(a) #=> (Bool | String)
if a && b
# here both a and b are guaranteed not to be Nil
end
if a.is_a? String
a.class #=> String
end
# Functions
def double(x)
x * 2
end
# Functions (and all blocks) implicitly return the value of the last statement
double(2) #=> 4
# Parentheses are optional where the call is unambiguous
double 3 #=> 6
double double 3 #=> 12
def sum(x, y)
x + y
end
# Method arguments are separated by a comma
sum 3, 4 #=> 7
sum sum(3, 4), 5 #=> 12
# yield
# All methods have an implicit, optional block parameter
# it can be called with the 'yield' keyword
def surround
puts '{'
yield
puts '}'
end
surround { puts "hello world" }
# {
# hello world
# }
# You can pass a block to a function
# "&" marks a reference to a passed block
def guests(&block)
block.call "some_argument"
end
# You can pass a list of arguments, which will be converted into an array
# That's what splat operator ("*") is for
def guests(*array)
array.each { |guest| puts guest }
end
# If a method returns an array, you can use destructuring assignment
def foods
["pancake", "sandwich", "quesadilla"]
end
breakfast, lunch, dinner = foods
breakfast #=> "pancake"
dinner #=> "quesadilla"
# By convention, all methods that return booleans end with a question mark
5.even? # false
5.odd? # true
# Also by convention, if a method ends with an exclamation mark, it does
# something destructive like mutate the receiver.
# Some methods have a ! version to make a change, and
# a non-! version to just return a new changed version
fruits = ["grapes", "apples", "bananas"]
fruits.sort #=> ["apples", "bananas", "grapes"]
fruits #=> ["grapes", "apples", "bananas"]
fruits.sort! #=> ["apples", "bananas", "grapes"]
fruits #=> ["apples", "bananas", "grapes"]
# However, some mutating methods do not end in !
fruits.shift #=> "apples"
fruits #=> ["bananas", "grapes"]
# Define a class with the class keyword
class Human
# A class variable. It is shared by all instances of this class.
@@species = "H. sapiens"
# An instance variable. Type of name is String
@name : String
# Basic initializer
# Assign the argument to the "name" instance variable for the instance
# If no age given, we will fall back to the default in the arguments list.
def initialize(@name, @age = 0)
end
# Basic setter method
def name=(name)
@name = name
end
# Basic getter method
def name
@name
end
# The above functionality can be encapsulated using the propery method as follows
property :name
# Getter/setter methods can also be created individually like this
getter :name
setter :name
# A class method uses self to distinguish from instance methods.
# It can only be called on the class, not an instance.
def self.say(msg)
puts msg
end
def species
@@species
end
end
# Instantiate a class
jim = Human.new("Jim Halpert")
dwight = Human.new("Dwight K. Schrute")
# Let's call a couple of methods
jim.species #=> "H. sapiens"
jim.name #=> "Jim Halpert"
jim.name = "Jim Halpert II" #=> "Jim Halpert II"
jim.name #=> "Jim Halpert II"
dwight.species #=> "H. sapiens"
dwight.name #=> "Dwight K. Schrute"
# Call the class method
Human.say("Hi") #=> print Hi and returns nil
# Variables that start with @ have instance scope
class TestClass
@var = "I'm an instance var"
end
# Variables that start with @@ have class scope
class TestClass
@@var = "I'm a class var"
end
# Variables that start with a capital letter are constants
Var = "I'm a constant"
Var = "can't be updated" # Error: already initialized constant Var
# Class is also an object in Crystal. So a class can have instance variables.
# Class variable is shared among the class and all of its descendants.
# base class
class Human
@@foo = 0
def self.foo
@@foo
end
def self.foo=(value)
@@foo = value
end
end
# derived class
class Worker < Human
end
Human.foo #=> 0
Worker.foo #=> 0
Human.foo = 2 #=> 2
Worker.foo #=> 0
Worker.foo = 3 #=> 3
Human.foo #=> 2
Worker.foo #=> 3
module ModuleExample
def foo
"foo"
end
end
# Including modules binds their methods to the class instances
# Extending modules binds their methods to the class itself
class Person
include ModuleExample
end
class Book
extend ModuleExample
end
Person.foo # => undefined method 'foo' for Person:Class
Person.new.foo # => 'foo'
Book.foo # => 'foo'
Book.new.foo # => undefined method 'foo' for Book
# Exception handling
# Define new exception
class MyException < Exception
end
# Define another exception
class MyAnotherException < Exception; end
ex = begin
raise MyException.new
rescue ex1 : IndexError
"ex1"
rescue ex2 : MyException | MyAnotherException
"ex2"
rescue ex3 : Exception
"ex3"
rescue ex4 # catch any kind of exception
"ex4"
end
ex #=> "ex2"
Dart:
import "dart:collection";
import "dart:math" as math;
/// Welcome to Learn Dart in 15 minutes. http://dart.dev/
/// This is an executable tutorial. You can run it with Dart or on
/// the Try Dart! site if you copy/paste it there. http://dartpad.dev/
/// You can also run Flutter in DartPad by click the `< > New Pad ` and choose Flutter
/// In Dart, Everything is an Object.
/// Every declaration of an object is an instance of Null and
/// Null is also an object.
/// 3 Types of comments in dart
// Single line comment
/**
* Multi-line comment
* Can comment several lines
*/
/// Code doc comment
/// It uses markdown syntax to generate code docs when making an API.
/// Code doc comment is the recommended choice when documenting your APIs, classes and methods.
/// 4 types of variable declaration.
/// Constants are variables that are immutable cannot be change or altered.
/// `const` in dart should practice SCREAMING_SNAKE_CASE name declaration.
const CONSTANT_VALUE = "I CANNOT CHANGE";
CONSTANT_VALUE = "DID I?"; //Error
/// Final is another variable declaration that cannot be change once it has been instantiated. Commonly used in classes and functions
/// `final` can be declared in pascalCase.
final finalValue = "value cannot be changed once instantiated";
finalValue = "Seems not"; //Error
/// `var` is another variable declaration that is mutable and can change its value. Dart will infer types and will not change its data type
var mutableValue = "Variable string";
mutableValue = "this is valid";
mutableValue = false; // Error.
/// `dynamic` is another variable declaration in which the type is not evaluated by the dart static type checking.
/// It can change its value and data type.
/// Some dartisans uses dynamic cautiously as it cannot keep track of its data type. so use it at your own risk
dynamic dynamicValue = "I'm a string";
dynamicValue = false; // false
/// Functions can be declared in a global space
/// Function declaration and method declaration look the same. Function
/// declarations can be nested. The declaration takes the form of
/// name() {} or name() => singleLineExpression;
/// The fat arrow function declaration can be an implicit or
/// explicit return for the result of the expression.
/// Dart will execute a function called `main()` anywhere in the dart project.
///
example1() {
nested1() {
nested2() => print("Example1 nested 1 nested 2");
nested2();
}
nested1();
}
/// Anonymous functions don't include a name
example2() {
//// Explicit return type.
nested1(void Function() fn) {
fn();
}
nested1(() => print("Example2 nested 1"));
}
/// When a function parameter is declared, the declaration can include the
/// number of parameters the function takes by explicitly specifying the names of the
/// parameters it takes.
example3() {
planA(fn(String informSomething)) {
fn("Example3 plan A");
}
planB(fn) {
// Or don't declare number of parameters.
fn("Example3 plan B");
}
planA((s) => print(s));
planB((s) => print(s));
}
/// Functions have closure access to outer variables.
/// Dart will infer types when the variable has a value of something.
/// In this example dart knows that this variable is a String.
var example4Something = "Example4 nested 1";
example4() {
nested1(fn(informSomething)) {
fn(example4Something);
}
nested1((s) => print(s));
}
/// Class declaration with a sayIt method, which also has closure access
/// to the outer variable as though it were a function as seen before.
var example5method = "Example5 sayIt";
class Example5Class {
sayIt() {
print(example5method);
}
}
example5() {
/// Create an anonymous instance of the Example5Class and call the sayIt
/// method on it.
/// the `new` keyword is optional in Dart.
new Example5Class().sayIt();
}
/// Class declaration takes the form of class name { [classBody] }.
/// Where classBody can include instance methods and variables, but also
/// class methods and variables.
class Example6Class {
var instanceVariable = "Example6 instance variable";
sayIt() {
print(instanceVariable);
}
}
example6() {
Example6Class().sayIt();
}
/// Class methods and variables are declared with "static" terms.
class Example7Class {
static var classVariable = "Example7 class variable";
static sayItFromClass() {
print(classVariable);
}
sayItFromInstance() {
print(classVariable);
}
}
example7() {
Example7Class.sayItFromClass();
new Example7Class().sayItFromInstance();
}
/// Dart supports Generics.
/// Generics refers to the technique of writing the code for a class
/// without specifying the data type(s) that the class works on.
/// Source: https://stackoverflow.com/questions/4560890/what-are-generics-in-c
/// Type `T` refers to any type that has been instantiated
/// you can call whatever you want
/// Programmers uses the convention in the following
/// T - Type(used for class and primitype types)
/// E - Element(used for List, Set, or Iterable)
/// K,V - Key Value(used for Map)
class GenericExample<T>{
void printType(){
print("$T")
}
// methods can also have generics
genericMethod<M>(){
print("class:$T, method: $M");
}
}
/// List are similar to arrays but list is a child of Iterable<E>
/// Therefore Maps, List, LinkedList are all child of Iterable<E> to be able to loop using the keyword `for`
/// Important things to remember:
/// () - Iterable<E>
/// [] - List<E>
/// {} - Map<K,V>
/// List are great, but there's a restriction for what List can be
/// outside of function/method bodies. List on the outer scope of class
/// or outside of class have to be constant. Strings and numbers are constant
/// by default. But arrays and maps are not. They can be made constant by
/// declaring them "const". Kind of similar to JavaScript's Object.freeze()
const example8List = ["Example8 const array"];
const example8Map = {"someKey": "Example8 const map"};
/// Declare List or Maps as Objects.
List<String> explicitList = new List<String>();
Map<String,dynamic> explicitMaps = new Map<String,dynamic>();
explicitList.add("SomeArray");
example8() {
print(example8Map["someKey"]);
print(explicitList[0]);
}
/// Assigning a list from one variable to another will not be the same result.
/// Because dart is pass-reference-by-value.
/// So when you assign an existing list to a new variable.
/// Instead of List, it becomes an Iterable
var iterableExplicitList = explicitList;
print(iterableExplicitList) // ("SomeArray"); "[]" becomes "()"
var newExplicitLists = explicitList.toList() // Converts Iterable<E> to List<E>
/// Loops in Dart take the form of standard for () {} or while () {} loops,
/// slightly more modern for (.. in ..) {}, or functional callbacks with many
/// supported features, starting with forEach,map and where.
var example9Array = const ["a", "b"];
example9() {
for (int i = 0; i < example9Array.length; i++) {
print("Example9 for loop '${example9Array[i]}'");
}
var i = 0;
while (i < example9Array.length) {
print("Example9 while loop '${example9Array[i]}'");
i++;
}
for (final e in example9Array) {
print("Example9 for-in loop '${e}'");
}
example9Array.forEach((e) => print("Example9 forEach loop '${e}'"));
}
/// To loop over the characters of a string or to extract a substring.
var example10String = "ab";
example10() {
for (var i = 0; i < example10String.length; i++) {
print("Example10 String character loop '${example10String[i]}'");
}
for (var i = 0; i < example10String.length; i++) {
print("Example10 substring loop '${example10String.substring(i, i + 1)}'");
}
}
/// `int`, `double` and `num` are the three supported number formats.
/// `num` can be either `int` or `double`.
/// `int` and `double` are children of type `num`
example11() {
var i = 1 + 320, d = 3.2 + 0.01;
final num myFinalNumDouble = 2.2;
final num myFinalNumInt = 2;
final int myFinalInt = 1;
final double myFinalDouble = 0.1;
num myNumDouble = 2.2;
num myNumInt = 2;
int myInt = 1;
double myDouble = 0; // Dart will add decimal prefix, becomes 0.0;
myNumDouble = myFinalInt; // valid
myNumDouble = myFinalDouble; // valid
myNumDouble = myFinalNumInt; // valid
myNumInt = myFinalInt; // valid
myNumInt = myFinalDouble; // valid
myNumInt = myFinalNumDouble; // valid
myInt = myNumDouble; // error
myInt = myFinalDouble; // error
myInt = myFinalNumInt; // valid
myDouble = myFinalInt; // error
myDouble = myFinalNumInt; // error
myDouble = myFinalNumDouble; // valid
print("Example11 int ${i}");
print("Example11 double ${d}");
}
/// DateTime provides date/time arithmetic.
example12() {
var now = new DateTime.now();
print("Example12 now '${now}'");
now = now.add(new Duration(days: 1));
print("Example12 tomorrow '${now}'");
}
/// Regular expressions are supported.
example13() {
var s1 = "some string", s2 = "some", re = new RegExp("^s.+?g\$");
match(s) {
if (re.hasMatch(s)) {
print("Example13 regexp matches '${s}'");
} else {
print("Example13 regexp doesn't match '${s}'");
}
}
match(s1);
match(s2);
}
/// Boolean expressions support implicit conversions and dynamic type
example14() {
var a = true;
if (a) {
print("true, a is $a");
}
a = false;
if (a) {
print("true, a is $a");
} else {
print("false, a is $a"); /// runs here
}
/// dynamic typed null can not be convert to bool
var b; /// b is dynamic type
b = "abc";
try {
if (b) {
print("true, b is $b");
} else {
print("false, b is $b");
}
} catch (e) {
print("error, b is $b"); /// this could be run but got error
}
b = null;
if (b) { /// Failed assertion: boolean expression must not be null)
print("true, b is $b");
} else {
print("false, b is $b");
}
/// statically typed null can not be convert to bool
var c = "abc";
c = null;
/// compilation failed
/// if (c) {
/// print("true, c is $c");
/// } else {
/// print("false, c is $c");
/// }
}
/// try/catch/finally and throw are used for exception handling.
/// throw takes any object as parameter;
example15() {
try {
try {
throw "Some unexpected error.";
} catch (e) {
print("Example15 an exception: '${e}'");
throw e; /// Re-throw
}
} catch (e) {
print("Example15 catch exception being re-thrown: '${e}'");
} finally {
print("Example15 Still run finally");
}
}
/// To be efficient when creating a long string dynamically, use
/// StringBuffer. Or you could join a string array.
example16() {
var sb = new StringBuffer(), a = ["a", "b", "c", "d"], e;
for (e in a) {
sb.write(e);
}
print("Example16 dynamic string created with "
"StringBuffer '${sb.toString()}'");
print("Example16 join string array '${a.join()}'");
}
/// Strings can be concatenated by just having string List next to
/// one another with no further operator needed.
example17() {
print("Example17 "
"concatenate "
"strings "
"just like that");
}
/// Strings have single-quote or double-quote for delimiters with no
/// actual difference between the two. The given flexibility can be good
/// to avoid the need to escape content that matches the delimiter being
/// used. For example, double-quotes of HTML attributes if the string
/// contains HTML content.
example18() {
print('Example18 <a href="etc">'
"Don't can't I'm Etc"
'</a>');
}
/// Strings with triple single-quotes or triple double-quotes span
/// multiple lines and include line delimiters.
example19() {
print('''Example19 <a href="etc">
Example19 Don't can't I'm Etc
Example19 </a>''');
}
/// Strings have the nice interpolation feature with the $ character.
/// With $ { [expression] }, the return of the expression is interpolated.
/// $ followed by a variable name interpolates the content of that variable.
/// $ can be escaped like so \$ to just add it to the string instead.
example20() {
var s1 = "'\${s}'", s2 = "'\$s'";
print("Example20 \$ interpolation ${s1} or $s2 works.");
}
/// Optional types allow for the annotation of APIs and come to the aid of
/// IDEs so the IDEs can better refactor, auto-complete and check for
/// errors. So far we haven't declared any types and the programs have
/// worked just fine. In fact, types are disregarded during runtime.
/// Types can even be wrong and the program will still be given the
/// benefit of the doubt and be run as though the types didn't matter.
/// There's a runtime parameter that checks for type errors which is
/// the checked mode, which is said to be useful during development time,
/// but which is also slower because of the extra checking and is thus
/// avoided during deployment runtime.
class Example21 {
List<String> _names;
Example21() {
_names = ["a", "b"];
}
List<String> get names => _names;
set names(List<String> list) {
_names = list;
}
int get length => _names.length;
void add(String name) {
_names.add(name);
}
}
void example21() {
Example21 o = new Example21();
o.add("c");
print("Example21 names '${o.names}' and length '${o.length}'");
o.names = ["d", "e"];
print("Example21 names '${o.names}' and length '${o.length}'");
}
/// Class inheritance takes the form of class name extends AnotherClassName {}.
class Example22A {
var _name = "Some Name!";
get name => _name;
}
class Example22B extends Example22A {}
example22() {
var o = new Example22B();
print("Example22 class inheritance '${o.name}'");
}
/// Class mixin is also available, and takes the form of
/// class name extends SomeClass with AnotherClassName {}.
/// It's necessary to extend some class to be able to mixin another one.
/// The template class of mixin cannot at the moment have a constructor.
/// Mixin is mostly used to share methods with distant classes, so the
/// single inheritance doesn't get in the way of reusable code.
/// Mixins follow the "with" statement during the class declaration.
class Example23A {}
class Example23Utils {
addTwo(n1, n2) {
return n1 + n2;
}
}
class Example23B extends Example23A with Example23Utils {
addThree(n1, n2, n3) {
return addTwo(n1, n2) + n3;
}
}
example23() {
var o = new Example23B(), r1 = o.addThree(1, 2, 3), r2 = o.addTwo(1, 2);
print("Example23 addThree(1, 2, 3) results in '${r1}'");
print("Example23 addTwo(1, 2) results in '${r2}'");
}
/// The Class constructor method uses the same name of the class and
/// takes the form of SomeClass() : super() {}, where the ": super()"
/// part is optional and it's used to delegate constant parameters to the
/// super-parent's constructor.
class Example24A {
var _value;
Example24A({value: "someValue"}) {
_value = value;
}
get value => _value;
}
class Example24B extends Example24A {
Example24B({value: "someOtherValue"}) : super(value: value);
}
example24() {
var o1 = new Example24B(), o2 = new Example24B(value: "evenMore");
print("Example24 calling super during constructor '${o1.value}'");
print("Example24 calling super during constructor '${o2.value}'");
}
/// There's a shortcut to set constructor parameters in case of simpler classes.
/// Just use the this.parameterName prefix and it will set the parameter on
/// an instance variable of same name.
class Example25 {
var value, anotherValue;
Example25({this.value, this.anotherValue});
}
example25() {
var o = new Example25(value: "a", anotherValue: "b");
print("Example25 shortcut for constructor '${o.value}' and "
"'${o.anotherValue}'");
}
/// Named parameters are available when declared between {}.
/// Parameter order can be optional when declared between {}.
/// Parameters can be made optional when declared between [].
example26() {
var _name, _surname, _email;
setConfig1({name, surname}) {
_name = name;
_surname = surname;
}
setConfig2(name, [surname, email]) {
_name = name;
_surname = surname;
_email = email;
}
setConfig1(surname: "Doe", name: "John");
print("Example26 name '${_name}', surname '${_surname}', "
"email '${_email}'");
setConfig2("Mary", "Jane");
print("Example26 name '${_name}', surname '${_surname}', "
"email '${_email}'");
}
/// Variables declared with final can only be set once.
/// In case of classes, final instance variables can be set via constant
/// constructor parameter.
class Example27 {
final color1, color2;
/// A little flexibility to set final instance variables with syntax
/// that follows the :
Example27({this.color1, color2}) : color2 = color2;
}
example27() {
final color = "orange", o = new Example27(color1: "lilac", color2: "white");
print("Example27 color is '${color}'");
print("Example27 color is '${o.color1}' and '${o.color2}'");
}
/// To import a library, use import "libraryPath" or if it's a core library,
/// import "dart:libraryName". There's also the "pub" package management with
/// its own convention of import "package:packageName".
/// See import "dart:collection"; at the top. Imports must come before
/// other code declarations. IterableBase comes from dart:collection.
class Example28 extends IterableBase {
var names;
Example28() {
names = ["a", "b"];
}
get iterator => names.iterator;
}
example28() {
var o = new Example28();
o.forEach((name) => print("Example28 '${name}'"));
}
/// For control flow we have:
/// * standard switch with must break statements
/// * if-else if-else and ternary ..?..:.. operator
/// * closures and anonymous functions
/// * break, continue and return statements
example29() {
var v = true ? 30 : 60;
switch (v) {
case 30:
print("Example29 switch statement");
break;
}
if (v < 30) {
} else if (v > 30) {
} else {
print("Example29 if-else statement");
}
callItForMe(fn()) {
return fn();
}
rand() {
v = new DM.Random().nextInt(50);
return v;
}
while (true) {
print("Example29 callItForMe(rand) '${callItForMe(rand)}'");
if (v != 30) {
break;
} else {
continue;
}
/// Never gets here.
}
}
/// Parse int, convert double to int, or just keep int when dividing numbers
/// by using the ~/ operation. Let's play a guess game too.
example30() {
var gn,
tooHigh = false,
n,
n2 = (2.0).toInt(),
top = int.parse("123") ~/ n2,
bottom = 0;
top = top ~/ 6;
gn = new DM.Random().nextInt(top + 1); /// +1 because nextInt top is exclusive
print("Example30 Guess a number between 0 and ${top}");
guessNumber(i) {
if (n == gn) {
print("Example30 Guessed right! The number is ${gn}");
} else {
tooHigh = n > gn;
print("Example30 Number ${n} is too "
"${tooHigh ? 'high' : 'low'}. Try again");
}
return n == gn;
}
n = (top - bottom) ~/ 2;
while (!guessNumber(n)) {
if (tooHigh) {
top = n - 1;
} else {
bottom = n + 1;
}
n = bottom + ((top - bottom) ~/ 2);
}
}
/// Optional Positional Parameter:
/// parameter will be disclosed with square bracket [ ] & square bracketed parameter are optional.
example31() {
findVolume31(int length, int breath, [int height]) {
print('length = $length, breath = $breath, height = $height');
}
findVolume31(10,20,30); //valid
findVolume31(10,20); //also valid
}
/// Optional Named Parameter:
/// parameter will be disclosed with curly bracket { }
/// curly bracketed parameter are optional.
/// have to use parameter name to assign a value which separated with colan :
/// in curly bracketed parameter order does not matter
/// these type parameter help us to avoid confusion while passing value for a function which has many parameter.
example32() {
findVolume32(int length, int breath, {int height}) {
print('length = $length, breath = $breath, height = $height');
}
findVolume32(10,20,height:30);//valid & we can see the parameter name is mentioned here.
findVolume32(10,20);//also valid
}
/// Optional Default Parameter:
/// same like optional named parameter in addition we can assign default value for this parameter.
/// which means no value is passed this default value will be taken.
example33() {
findVolume33(int length, int breath, {int height=10}) {
print('length = $length, breath = $breath, height = $height');
}
findVolume33(10,20,height:30);//valid
findVolume33(10,20);//valid
}
/// Dart has also added feature such as Null aware operators
var isBool = true;
var hasString = isBool ?? "default String";
/// Programs have only one entry point in the main function.
/// Nothing is expected to be executed on the outer scope before a program
/// starts running with what's in its main function.
/// This helps with faster loading and even lazily loading of just what
/// the program needs to startup with.
main() {
print("Learn Dart in 15 minutes!");
[
example1, example2, example3, example4, example5,
example6, example7, example8, example9, example10,
example11, example12, example13, example14, example15,
example16, example17, example18, example19, example20,
example21, example22, example23, example24, example25,
example26, example27, example28, example29,
example30 // Adding this comment stops the dart formatter from putting all items on a new line
].forEach((ef) => ef());
}
Dhall:
-- Single-line comment
{- Multi-line comment
Unicode is fine 🙂
This file is a valid Dhall expression that evaluates to a large record
collecting the results of each step.
You can view the results by interpreting the file:
$ dhall --file learndhall.dhall
{- Comments can be nested -}
-}
let greeting = "Hello, world!"
let fruits = "🍋🍓🍍🍉🍌"
let interpolation = "Enjoy some delicious fruit: ${fruits}"
let multilineText {- Inline comments work, too -} =
''
Leading whitespace is stripped from multi-line text literals.
That means you can freely indent or dedent a text literal without
changing the result.
Relative indentation within the literal is still preserved.
Other than that, the text literal is preserved verbatim, similar to a
"literal" YAML multiline string.
''
let bool = True
-- Type annotations on bindings are optional, but helpful, so we'll use them
let annotation : Bool = True
let renderedBool : Text = if bool then "True" else "False"
-- Natural numbers are non-negative and are unsigned
let naturalNumber : Natural = 42
-- Integers may be negative, but require an explicit sign, even if positive
let positiveInteger : Integer = +1
let negativeInteger : Integer = -12
let pi : Double = 3.14159265359
{- You can use a wider character range for identifiers (such as quotation
marks and whitespace) if you quote them using backticks
-}
let `Avogadro's Number` : Double = 6.0221409e+23
let origin : { x : Double, y : Double } = { x = 0.0, y = 0.0 }
let somePrimes : List Natural = [ 2, 3, 5, 7, 11 ]
{- A schema is the same thing as a type
Types begin with an uppercase letter by convention, but this convention is
not enforced
-}
let Profile : Type
= { person :
{ name : Text
, age : Natural
}
, address :
{ country : Text
, state : Text
, city : Text
}
}
let john : Profile =
{ person =
{ name = "John Doe"
, age = 67
}
, address =
{ country = "United States"
, state = "Pennsylvania"
, city = "Philadelphia"
}
}
let philadelphia : Text = john.address.city
{- Enum alternatives also begin with an uppercase letter by convention. This
convention is not enforced
-}
let DNA : Type = < Adenine | Cytosine | Guanine | Thymine >
let dnaSequence : List DNA = [ DNA.Thymine, DNA.Guanine, DNA.Guanine ]
let compactDNASequence : List DNA =
let a = DNA.Adenine
let c = DNA.Cytosine
let g = DNA.Guanine
let t = DNA.Thymine
in [ c, t, t, a, t, c, g, g, c ]
-- You can transform enums by providing a record with one field per alternative
let theLetterG : Text =
merge
{ Adenine = "A"
, Cytosine = "C"
, Guanine = "G"
, Thymine = "T"
}
DNA.Guanine
let presentOptionalValue : Optional Natural = Some 1
let absentOptionalValue : Optional Natural = None Natural
let points : List { x : Double, y : Double } =
[ { x = 1.1, y = -4.2 }
, { x = 4.4, y = -3.0 }
, { x = 8.2, y = -5.5 }
]
{- `Natural -> List Natural` is the type of a function whose input type is a
`Natural` and whose output type is a `List Natural`
All functions in Dhall are anonymous functions (a.k.a. "lambdas"),
which you can optionally give a name
For example, the following function is equivalent to this Python code:
lambda n : [ n, n + 1 ]
... and this JavaScript code:
function (n) { return [ n, n + 1 ]; }
-}
let exampleFunction : Natural -> List Natural =
\(n : Natural) -> [ n, n + 1 ]
-- Dhall also supports Unicode syntax, but this tutorial will stick to ASCII
let unicodeFunction : Natural → List Natural =
λ(n : Natural) → [ n, n + 1 ]
-- You don't need to parenthesize function arguments
let exampleFunctionApplication : List Natural =
exampleFunction 2
let functionOfMultipleArguments : Natural -> Natural -> List Natural =
\(x : Natural) -> \(y : Natural) -> [ x, y ]
let functionAppliedToMultipleArguments : List Natural =
functionOfMultipleArguments 2 3
{- Same as `exampleFunction` except we gave the function's input type a
name: "n"
-}
let namedArgumentType : forall (n : Natural) -> List Natural =
\(n : Natural) -> [ n, n + 1 ]
{- If you name a function's input type, you can use that name later within the
same type
This lets you write a function that works for more than one type of input
(a.k.a. a "polymorphic" function)
-}
let duplicate : forall (a : Type) -> a -> List a =
\(a : Type) -> \(x : a) -> [ x, x ]
let duplicatedNumber : List Natural =
duplicate Natural 2
let duplicatedBool : List Bool =
duplicate Bool False
{- The language also has some built-in polymorphic functions, such as:
List/head : forall (a : Type) -> List a -> Optional a
-}
let firstPrime : Optional Natural = List/head Natural somePrimes
let functionOfARecord : { x : Natural, y : Natural } -> List Natural =
\(args : { x : Natural, y : Natural }) -> [ args.x, args.y ]
let functionAppliedToARecord : List Natural =
functionOfARecord { x = 2, y = 5 }
{- All type conversions are explicit
`Natural/show` is a built-in function of the following type:
Natural/show : Natural -> Text
... that converts `Natural` numbers to their `Text` representation
-}
let typeConversion : Natural -> Text =
\(age : Natural) -> "I am ${Natural/show age} years old!"
-- A "template" is the same thing as a function whose output type is `Text`
let mitLicense : { year : Natural, copyrightHolder : Text } -> Text =
\(args : { year : Natural, copyrightHolder : Text }) ->
''
Copyright ${Natural/show args.year} ${args.copyrightHolder}
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
''
-- Template instantiation is the same thing as function application
let templatedLicense : Text =
mitLicense { year = 2019, copyrightHolder = "Jane Smith" }
{- You can import expressions by URL
Also, like Bash, you can import code from your local filesystem (not shown)
Security-conscious users can pin remotely-imported expressions by adding a
semantic integrity check. The interpreter rejects any attempt to tamper with
an expression pinned in this way. However, behavior-preserving refactors
of imported content will not perturb the hash.
Imported expressions pinned in this way are also locally cached in a
content-addressable store (typically underneath `~/.cache/dhall`)
-}
let Natural/sum : List Natural -> Natural =
https://prelude.dhall-lang.org/Natural/sum
sha256:33f7f4c3aff62e5ecf4848f964363133452d420dcde045784518fb59fa970037
let twentyEight : Natural = Natural/sum somePrimes
-- A "package" is the same thing as a (possibly nested) record that you can import
let Prelude = https://prelude.dhall-lang.org/package.dhall
let false : Bool = Prelude.Bool.not True
-- You can import the raw contents of a file by adding `as Text` to an import
let sourceCode : Text = https://prelude.dhall-lang.org/Bool/not as Text
-- You can import environment variables, too:
let presentWorkingDirectory = env:PWD as Text
-- You can provide a fallback expression if an import fails
let home : Optional Text = Some env:HOME ? None Text
-- Fallback expressions can contain alternative imports of their own
let possiblyCustomPrelude =
env:DHALL_PRELUDE
? https://prelude.dhall-lang.org/package.dhall
{- Tie everything together by auto-generating configurations for 10 build users
using the `generate` function:
Prelude.List.generate
: Natural -> forall (a : Type) -> (Natural -> a) -> List a
-}
let buildUsers =
let makeUser = \(user : Text) ->
let home = "/home/${user}"
let privateKey = "${home}/.ssh/id_ed25519"
let publicKey = "${privateKey}.pub"
in { home = home
, privateKey = privateKey
, publicKey = publicKey
}
let buildUser =
\(index : Natural) -> makeUser "build${Natural/show index}"
let Config =
{ home : Text
, privateKey : Text
, publicKey : Text
}
in Prelude.List.generate 10 Config buildUser
-- Present all of the results in a final record
in { greeting = greeting
, fruits = fruits
, interpolation = interpolation
, multilineText = multilineText
, bool = bool
, annotation = annotation
, renderedBool = renderedBool
, naturalNumber = naturalNumber
, positiveInteger = positiveInteger
, negativeInteger = negativeInteger
, pi = pi
, `Avogadro's Number` = `Avogadro's Number`
, origin = origin
, somePrimes = somePrimes
, john = john
, philadelphia = philadelphia
, dnaSequence = dnaSequence
, compactDNASequence = compactDNASequence
, theLetterG = theLetterG
, presentOptionalValue = presentOptionalValue
, absentOptionalValue = absentOptionalValue
, points = points
, exampleFunction = exampleFunction
, unicodeFunction = unicodeFunction
, exampleFunctionApplication = exampleFunctionApplication
, functionOfMultipleArguments = functionOfMultipleArguments
, functionAppliedToMultipleArguments = functionAppliedToMultipleArguments
, namedArgumentType = namedArgumentType
, duplicate = duplicate
, duplicatedNumber = duplicatedNumber
, duplicatedBool = duplicatedBool
, firstPrime = firstPrime
, functionOfARecord = functionOfARecord
, functionAppliedToARecord = functionAppliedToARecord
, typeConversion = typeConversion
, mitLicense = mitLicense
, templatedLicense = templatedLicense
, twentyEight = twentyEight
, false = false
, sourceCode = sourceCode
, presentWorkingDirectory = presentWorkingDirectory
, home = home
, buildUsers = buildUsers
}
EasyLang:
print "Hello world"
#
# number variable (64 bit floating point)
#
h = 3.14
print h
#
# string variable
#
str$ = "monkey"
# strings can grow
str$ &= " circus"
print str$
#
# blocks end with 'end' or a dot, a newline has no
# other meaning than a space
#
for i = 1 to 5
sum += i * i
.
print sum
#
# functions have value and reference
# parameters, no return values
#
func gcd a b . res .
# a and b are value parameters
# res is a reference parameter
while b <> 0
# h is a local variable, because
# it is first used in the function
h = b
b = a mod b
a = h
.
res = a
.
call gcd 120 35 r
print r
#
# strings can be concatenated and numbers are
# automatically converted to strings
#
print "1 + 2 = " & 1 + 2
#
# array of numbers
#
a[] = [ 2.1 3.14 3 ]
#
# arrays can grow
a[] &= 4
print a[]
#
# arrays, strings and numbers are copied by value
#
b[] = a[]
a[] &= 4
print a[] ; print b[]
#
# array swapping ist fast
#
swap a[] b[]
print a[] ; print b[]
#
# array of strings
#
fruits$[] = [ "apple" "banana" "orange" ]
#
# for-in iterates over the elements of an array
#
for fruit$ in fruits$[]
print fruit$
.
#
# strings are also used for single characters
#
letters$[] = str_chars "ping"
print letters$[]
letters$[1] = "o"
print str_join letters$[]
#
# 2-dimensional arrays are arrays of arrays
# this defines 3 arrays with length 4
#
len a[][] 3
for i range len a[][]
len a[i][] 4
.
a[1][2] = 99
print a[][]
#
# builtin functions
if sin 90 = 1
print "angles are in degree"
.
print pow 2 8
# seconds since 1970
print floor sys_time
# random numbers
print randomf
print random 6 + 1
#
# hour and minutes
print substr time_str sys_time 11 5
#
print str_ord "A"
print str_chr 65
#
# set number format
numfmt 0 4
print sqrt 2
print pi
print logn 10
#
a$[] = str_split "10,15,22" ","
print a$[]
print 2 * number a$[0]
print len a$[]
print len "Hello"
#
# With 'break n' you can leave nested loops and a function
#
names$[] = [ ]
func name2id name$ . id .
for id range len names$[]
if names$[id] = name$
# leave loop and function
break 2
.
.
names$[] &= name$
.
call name2id "alice" id ; print id
call name2id "bob" id ; print id
call name2id "alice" id ; print i
#
# with 'repeat' you can make loops, which you can leave
# in the loop body using 'until'
#
sum = 0
repeat
s$ = input
until s$ = ""
sum += number s$
.
print "sum: " & sum
#
# "input" reads a string from the "input_data" section,
# if it exists, otherwise via a prompt.
#
input_data
10
-2
6
EDN:
; Comments start with a semicolon.
; Anything after the semicolon is ignored.
;;;;;;;;;;;;;;;;;;;
;;; Basic Types ;;;
;;;;;;;;;;;;;;;;;;;
nil ; also known in other languages as null
; Booleans
true
false
; Strings are enclosed in double quotes
"hungarian breakfast"
"farmer's cheesy omelette"
; Characters are preceded by backslashes
\g \r \a \c \e
; Keywords start with a colon. They behave like enums. Kind of
; like symbols in Ruby.
:eggs
:cheese
:olives
; Symbols are used to represent identifiers.
; You can namespace symbols by using /. Whatever precedes / is
; the namespace of the symbol.
spoon
kitchen/spoon ; not the same as spoon
kitchen/fork
github/fork ; you can't eat with this
; Integers and floats
42
3.14159
; Lists are sequences of values
(:bun :beef-patty 9 "yum!")
; Vectors allow random access
[:gelato 1 2 -2]
; Maps are associative data structures that associate the key with its value
{:eggs 2
:lemon-juice 3.5
:butter 1}
; You're not restricted to using keywords as keys
{[1 2 3 4] "tell the people what she wore",
[5 6 7 8] "the more you see the more you hate"}
; You may use commas for readability. They are treated as whitespace.
; Sets are collections that contain unique elements.
#{:a :b 88 "huat"}
;;;;;;;;;;;;;;;;;;;;;;;
;;; Tagged Elements ;;;
;;;;;;;;;;;;;;;;;;;;;;;
; EDN can be extended by tagging elements with # symbols.
#MyYelpClone/MenuItem {:name "eggs-benedict" :rating 10}
; Let me explain this with a Clojure example. Suppose I want to transform that
; piece of EDN into a MenuItem record.
(defrecord MenuItem [name rating])
; defrecord defined, among other things, map->MenuItem which will take a map
; of field names (as keywords) to values and generate a user.MenuItem record
; To transform EDN to Clojure values, I will need to use the built-in EDN
; reader, clojure.edn/read-string
(clojure.edn/read-string "{:eggs 2 :butter 1 :flour 5}")
; -> {:eggs 2 :butter 1 :flour 5}
; To transform tagged elements, pass to clojure.edn/read-string an option map
; with a :readers map that maps tag symbols to data-reader functions, like so
(clojure.edn/read-string
{:readers {'MyYelpClone/MenuItem map->MenuItem}}
"#MyYelpClone/MenuItem {:name \"eggs-benedict\" :rating 10}")
; -> #user.MenuItem{:name "eggs-benedict", :rating 10}
Elixir:
# Single line comments start with a number symbol.
# There's no multi-line comment,
# but you can stack multiple comments.
# To use the Elixir shell use the `iex` command.
# Compile your modules with the `elixirc` command.
# Both should be in your path if you installed Elixir correctly.
## ---------------------------
## -- Basic types
## ---------------------------
# There are numbers
3 # integer
0x1F # integer
3.0 # float
# Atoms are constants whose values are their own name. They start with `:`.
:hello # atom
# Tuples that are stored contiguously in memory.
{1,2,3} # tuple
# We can access a tuple element with the `elem` function:
elem({1, 2, 3}, 0) #=> 1
# Lists that are implemented as linked lists.
[1,2,3] # list
# We can access the head and tail of a list as follows:
[head | tail] = [1,2,3]
head #=> 1
tail #=> [2,3]
# In Elixir, just like in Erlang, the `=` denotes pattern matching and
# not an assignment.
#
# This means that the left-hand side (pattern) is matched against a
# right-hand side.
#
# This is how the above example of accessing the head and tail of a list works.
# A pattern match will error when the sides don't match, in this example
# the tuples have different sizes.
# {a, b, c} = {1, 2} #=> ** (MatchError) no match of right hand side value: {1,2}
# There are also binaries
<<1,2,3>> # binary
# Strings and char lists
"hello" # string
'hello' # char list
# Multi-line strings
"""
I'm a multi-line
string.
"""
#=> "I'm a multi-line\nstring.\n"
# Strings are all encoded in UTF-8:
"héllò" #=> "héllò"
# Strings are really just binaries, and char lists are just lists.
<<?a, ?b, ?c>> #=> "abc"
[?a, ?b, ?c] #=> 'abc'
# `?a` in Elixir returns the ASCII integer for the letter `a`
?a #=> 97
# To concatenate lists use `++`, for binaries use `<>`
[1,2,3] ++ [4,5] #=> [1,2,3,4,5]
'hello ' ++ 'world' #=> 'hello world'
<<1,2,3>> <> <<4,5>> #=> <<1,2,3,4,5>>
"hello " <> "world" #=> "hello world"
# Ranges are represented as `start..end` (both inclusive)
1..10 #=> 1..10
lower..upper = 1..10 # Can use pattern matching on ranges as well
[lower, upper] #=> [1, 10]
# Maps are key-value pairs
genders = %{"david" => "male", "gillian" => "female"}
genders["david"] #=> "male"
# Maps with atom keys can be used like this
genders = %{david: "male", gillian: "female"}
genders.gillian #=> "female"
## ---------------------------
## -- Operators
## ---------------------------
# Some math
1 + 1 #=> 2
10 - 5 #=> 5
5 * 2 #=> 10
10 / 2 #=> 5.0
# In Elixir the operator `/` always returns a float.
# To do integer division use `div`
div(10, 2) #=> 5
# To get the division remainder use `rem`
rem(10, 3) #=> 1
# There are also boolean operators: `or`, `and` and `not`.
# These operators expect a boolean as their first argument.
true and true #=> true
false or true #=> true
# 1 and true
#=> ** (BadBooleanError) expected a boolean on left-side of "and", got: 1
# Elixir also provides `||`, `&&` and `!` which accept arguments of any type.
# All values except `false` and `nil` will evaluate to true.
1 || true #=> 1
false && 1 #=> false
nil && 20 #=> nil
!true #=> false
# For comparisons we have: `==`, `!=`, `===`, `!==`, `<=`, `>=`, `<` and `>`
1 == 1 #=> true
1 != 1 #=> false
1 < 2 #=> true
# `===` and `!==` are more strict when comparing integers and floats:
1 == 1.0 #=> true
1 === 1.0 #=> false
# Elixir operators are strict in their arguments, with the exception
# of comparison operators that work across different data types:
1 < :hello #=> true
# This enables building collections of mixed types:
["string", 123, :atom]
# While there is an overall order of all data types,
# to quote Joe Armstrong on this: "The actual order is not important,
# but that a total ordering is well defined is important."
## ---------------------------
## -- Control Flow
## ---------------------------
# `if` expression
if false do
"This will never be seen"
else
"This will"
end
# There's also `unless`
unless true do
"This will never be seen"
else
"This will"
end
# Remember pattern matching? Many control-flow structures in Elixir rely on it.
# `case` allows us to compare a value against many patterns:
case {:one, :two} do
{:four, :five} ->
"This won't match"
{:one, x} ->
"This will match and bind `x` to `:two` in this clause"
_ ->
"This will match any value"
end
# It's common to bind the value to `_` if we don't need it.
# For example, if only the head of a list matters to us:
[head | _] = [1,2,3]
head #=> 1
# For better readability we can do the following:
[head | _tail] = [:a, :b, :c]
head #=> :a
# `cond` lets us check for many conditions at the same time.
# Use `cond` instead of nesting many `if` expressions.
cond do
1 + 1 == 3 ->
"I will never be seen"
2 * 5 == 12 ->
"Me neither"
1 + 2 == 3 ->
"But I will"
end
# It is common to set the last condition equal to `true`, which will always match.
cond do
1 + 1 == 3 ->
"I will never be seen"
2 * 5 == 12 ->
"Me neither"
true ->
"But I will (this is essentially an else)"
end
# `try/catch` is used to catch values that are thrown, it also supports an
# `after` clause that is invoked whether or not a value is caught.
try do
throw(:hello)
catch
message -> "Got #{message}."
after
IO.puts("I'm the after clause.")
end
#=> I'm the after clause
# "Got :hello"
## ---------------------------
## -- Modules and Functions
## ---------------------------
# Anonymous functions (notice the dot)
square = fn(x) -> x * x end
square.(5) #=> 25
# They also accept many clauses and guards.
# Guards let you fine tune pattern matching,
# they are indicated by the `when` keyword:
f = fn
x, y when x > 0 -> x + y
x, y -> x * y
end
f.(1, 3) #=> 4
f.(-1, 3) #=> -3
# Elixir also provides many built-in functions.
# These are available in the current scope.
is_number(10) #=> true
is_list("hello") #=> false
elem({1,2,3}, 0) #=> 1
# You can group several functions into a module. Inside a module use `def`
# to define your functions.
defmodule Math do
def sum(a, b) do
a + b
end
def square(x) do
x * x
end
end
Math.sum(1, 2) #=> 3
Math.square(3) #=> 9
# To compile our simple Math module save it as `math.ex` and use `elixirc`
# in your terminal: elixirc math.ex
# Inside a module we can define functions with `def` and private functions with `defp`.
# A function defined with `def` is available to be invoked from other modules,
# a private function can only be invoked locally.
defmodule PrivateMath do
def sum(a, b) do
do_sum(a, b)
end
defp do_sum(a, b) do
a + b
end
end
PrivateMath.sum(1, 2) #=> 3
# PrivateMath.do_sum(1, 2) #=> ** (UndefinedFunctionError)
# Function declarations also support guards and multiple clauses.
# When a function with multiple clauses is called, the first function
# that satisfies the clause will be invoked.
# Example: invoking area({:circle, 3}) will call the second area
# function defined below, not the first:
defmodule Geometry do
def area({:rectangle, w, h}) do
w * h
end
def area({:circle, r}) when is_number(r) do
3.14 * r * r
end
end
Geometry.area({:rectangle, 2, 3}) #=> 6
Geometry.area({:circle, 3}) #=> 28.25999999999999801048
# Geometry.area({:circle, "not_a_number"})
#=> ** (FunctionClauseError) no function clause matching in Geometry.area/1
# Due to immutability, recursion is a big part of Elixir
defmodule Recursion do
def sum_list([head | tail], acc) do
sum_list(tail, acc + head)
end
def sum_list([], acc) do
acc
end
end
Recursion.sum_list([1,2,3], 0) #=> 6
# Elixir modules support attributes, there are built-in attributes and you
# may also add custom ones.
defmodule MyMod do
@moduledoc """
This is a built-in attribute on an example module.
"""
@my_data 100 # This is a custom attribute.
IO.inspect(@my_data) #=> 100
end
# The pipe operator |> allows you to pass the output of an expression
# as the first parameter into a function.
Range.new(1,10)
|> Enum.map(fn x -> x * x end)
|> Enum.filter(fn x -> rem(x, 2) == 0 end)
#=> [4, 16, 36, 64, 100]
## ---------------------------
## -- Structs and Exceptions
## ---------------------------
# Structs are extensions on top of maps that bring default values,
# compile-time guarantees and polymorphism into Elixir.
defmodule Person do
defstruct name: nil, age: 0, height: 0
end
joe_info = %Person{ name: "Joe", age: 30, height: 180 }
#=> %Person{age: 30, height: 180, name: "Joe"}
# Access the value of name
joe_info.name #=> "Joe"
# Update the value of age
older_joe_info = %{ joe_info | age: 31 }
#=> %Person{age: 31, height: 180, name: "Joe"}
# The `try` block with the `rescue` keyword is used to handle exceptions
try do
raise "some error"
rescue
RuntimeError -> "rescued a runtime error"
_error -> "this will rescue any error"
end
#=> "rescued a runtime error"
# All exceptions have a message
try do
raise "some error"
rescue
x in [RuntimeError] ->
x.message
end
#=> "some error"
## ---------------------------
## -- Concurrency
## ---------------------------
# Elixir relies on the actor model for concurrency. All we need to write
# concurrent programs in Elixir are three primitives: spawning processes,
# sending messages and receiving messages.
# To start a new process we use the `spawn` function, which takes a function
# as argument.
f = fn -> 2 * 2 end #=> #Function<erl_eval.20.80484245>
spawn(f) #=> #PID<0.40.0>
# `spawn` returns a pid (process identifier), you can use this pid to send
# messages to the process. To do message passing we use the `send` operator.
# For all of this to be useful we need to be able to receive messages. This is
# achieved with the `receive` mechanism:
# The `receive do` block is used to listen for messages and process
# them when they are received. A `receive do` block will only
# process one received message. In order to process multiple
# messages, a function with a `receive do` block must recursively
# call itself to get into the `receive do` block again.
defmodule Geometry do
def area_loop do
receive do
{:rectangle, w, h} ->
IO.puts("Area = #{w * h}")
area_loop()
{:circle, r} ->
IO.puts("Area = #{3.14 * r * r}")
area_loop()
end
end
end
# Compile the module and create a process that evaluates `area_loop` in the shell
pid = spawn(fn -> Geometry.area_loop() end) #=> #PID<0.40.0>
# Alternatively
pid = spawn(Geometry, :area_loop, [])
# Send a message to `pid` that will match a pattern in the receive statement
send pid, {:rectangle, 2, 3}
#=> Area = 6
# {:rectangle,2,3}
send pid, {:circle, 2}
#=> Area = 12.56000000000000049738
# {:circle,2}
# The shell is also a process, you can use `self` to get the current pid
self() #=> #PID<0.27.0>
## ---------------------------
## -- Agents
## ---------------------------
# An agent is a process that keeps track of some changing value
# Create an agent with `Agent.start_link`, passing in a function
# The initial state of the agent will be whatever that function returns
{:ok, my_agent} = Agent.start_link(fn -> ["red", "green"] end)
# `Agent.get` takes an agent name and a `fn` that gets passed the current state
# Whatever that `fn` returns is what you'll get back
Agent.get(my_agent, fn colors -> colors end) #=> ["red", "green"]
# Update the agent's state the same way
Agent.update(my_agent, fn colors -> ["blue" | colors] end)
Stop updates.

