ted.neward@newardassociates.com | Blog: http://blogs.newardassociates.com | Github: tedneward | LinkedIn: tedneward
... specifically to consume HTTP APIs/"Web 2.0"?
... specifically to make it easy to produce said APIs?
... to parallelize well?
... but without trying to do too much?
general-purpose
concurrent
strongly-typed
with both textual and graphical syntaxes
heavily "service"-oriented
optimized for integration
open-source (https://github.com/ballerinalang
)
An Albums HTTP API (1 of 2)
configurable int port = 8080; type Album readonly & record {| string id; string title; string artist; decimal price; |}; table<Album> key(id) albums = table [ {id: "1", title: "Blue Train", artist: "John Coltrane", price: 56.99}, {id: "2", title: "Jeru", artist: "Gerry Mulligan", price: 17.99}, {id: "3", title: "Sarah Vaughan and Clifford Brown", artist: "Sarah Vaughan", price: 39.99} ];
An Albums HTTP API (2 of 2)
service / on new http:Listener(port) { resource function get albums() returns Album[] { return albums.toArray(); } resource function get albums/[string id]() returns Album|http:NotFound { Album? album = albums[id]; if album is () { return http:NOT_FOUND; } else { return album; } } resource function post albums(@http:Payload Album album) returns Album { albums.add(album); return album; } }
services are our principal distributed design choice
so make them first-class citizens
concurrency within a service should be easy/default
obervability should be plumbing
just like code resolution or memory management
functions and explicit state are sufficient
supports objects but not a full-featured O-O language
prefer structural typing over nominative
service
a collection of network-accessible entrypoints
must be bound to a network protocol for it to become exposed
resource
a single network-accessible entrypoint within a service
isolation and transactions
for managing (and checking) race conditions
strands
logical coroutines bound to a native thread
no two strands of the same thread will execute concurrently
Ballerina Playground
https://play.ballerina.io/
Ballerina Docker image
https://hub.docker.com/r/ballerina/ballerina
docker run -v .:/home/ballerina -it ballerina/ballerina:1.0.2 bal run demo.bal
Ballerina source
https://github.com/ballerina-platform/
or install it locally
Download: https://ballerina.io/downloads/
(currently) requires Java11
extract into directory of choice
add BALLERINA_HOME/bin
to your PATH
Homebrew/Linuxbrew: brew install ballerina
Ballerina installers (Windows msi, macOS pkg, Linux deb, rpm)
bal
tool can self-update after installation
Read: Ballerina-by-Example (BBE)
https://ballerina.io/learn/by-example/
Language spec
https://ballerina.io/learn/platform-specifications/
Generating a new package
$ bal new greeter
similar (not identical) to most C-family languages
curly braces for blocks of code
// comments inside functions
semicolon statement terminators
files end in .bal
suffix
no filename/packaging restrictions
Hello, Ballerina
import ballerina/io; public function main() { io:println("Hello, World!"); }
from https://github.com/tedneward/Demo-Ballerina/greeter
structurally typed
no implementation inheritance
heavy FP influence
"resource-oriented"
services as first-class constructs
concurrency analysis and verification
transactional support
runs on top of the Java Runtime (JRE)
mostly hidden behind the bal
tool
... but JAR artifacts easily obtained
Run
bal run greeter
Compile and run built JAR
bal build greeter
java -jar greeter/target/bin/greeter.jar
REST API for Greetings
import ballerina/http; listener http:Listener httpListener = new (8080); service / on httpListener { resource function get greeting() returns string { return "Hello, World!"; } resource function get greeting/[string name]() returns string { return "Hello " + name; } }
from https://github.com/tedneward/Demo-Ballerina/restgreeter
Running
$ bal run
Client access
$ curl localhost:8080/greeting Hello, World! $ curl localhost:8080/greeting/Ballerina Hello Ballerina
Add [build-options] cloud = "docker"
to Ballerina.toml
Add a Cloud.toml file to provide Docker settings
set buildImage=true
to generate image
set buildImage=false
to generate Dockerfile/arrange dependencies
bal build
generates the executable and the Docker image
Cloud.toml
[container.image] repository="tedneward" # ex - Docker hub name name="greeter" # container name tag="v0.1.0" [settings] buildImage=true # set this false to just gen the Dockerfile
from https://github.com/tedneward/Demo-Ballerina/restgreeter
Run the Docker container
$ docker run -d -p 8080:8080 tedneward/restgreeter:v0.1.0
https://github.com/tedneward/TicTacToeREST
intended as a "full example" of API + database + some business rules
"contract-first" (from OpenAPI YAML)
bal openapi -i ttt.yaml --mode service -o server
HTTP endpoints -> "business layer" -> "persistence layer" -> database
H2 embedded database
... probably not going to replace your current language/platform of choice
... but something to keep an eye on
... most certainly a hopeful influence for future languages/platforms
simple values: not constructed from other values
structured values: contain other values
sequence values: zero or more constituent items
behavioral values: not just data; code
"storage identity": reference vs value storage
"plain data": simple, sequence, or structured value with all-plain-data members
declared "C-style"
"Typename VariableName" with optional assignment
globals
declared at "file scope"
visible across entire module
visible everywhere if declared public
names can be any non-keyword identifier
or "escaped" using single backtick
multiple variables can be declared simultaneously
used to "destructure" tuples
var
used to declare type-inferenced variables
nil
: ()
boolean
: true
, false
int
: 64-bit signed integers
float
: 64-bit floating-point
decimal
: decimal floating-point
cast using angle-bracket syntax
no implicit conversions; language library functions help with this
array: ordered list of values of a single type
tuple: ordered list of values of multiple types
map
: mapping of string keys to values of (one) type
record
: mapping from strings to heterogeneous types
table
: ordered collection of uniquely-identified mappings
indicated by square brackets ([ ]
) after type
initializer uses comma-delimited [ a, b, c ]
empty array initializer is [ ]
arrays know their own length (.length()
)
use ballerina/lang.arrays
package for utility methods
accessed for read/write by []
comma-delimited list within square brackets
structurally-typed
accessed via indexed integers or...
... destructured using assignment syntax
value type defined using angle-bracketed syntax
use -string-indexed access for get/set
use ballerina/lang.maps
package for utility methods
named fields of types
structurally typed
record types can compose as part of definition
fields can have defaulted values
Composing record types
type Date record { int year; int month; int day; }; type TimeOfDay record { int hour; int minute; decimal seconds; }; type Time record { *Date; *TimeOfDay; };
"closed": records can only contain named fields
denoted by {|
|}
pairs instead of usual {}
"open": records can contain additional named fields
data just "comes along for the ride" in that instance
records are open by default
string
: sequence of Unicode values (string:Char
instances)
package ballerina/lang.string
contains usual string functions
iterate characters via iterator()
xml
: sequence of XML nodes (elements, processing instructions, etc)
package ballerina/lang.xml
for XML-related functions
iterate nodes via iterator()
hierarchy of types xmlelement, xmldocument, etc
sequence of zero or more XML {element, text, comment, or PI} instances
xml literal strings allowed with backtick-quoted strings
supports namespaces: (namspace-uri)element-name
format
package xmldata
converts between XML and JSON/Ballerina records
package xslt
does XML transformations
function
: function w/0+ specified parameter types and a return type
object
: combination of named fields and named methods
typedesc
: type descriptor
error
: representation of an error
future
: a value to be returned by a function execution
stream
: sequence of values that can be generated lazily
handle
: reference to externally managed storage
package ballerina/lang.error
for utility methods
cause
: the cause of the error
detail
: deeper details about the error
message
: error message describing the error
stackTrace
: stack trace of the error
two forms of construction:
"new-expression" using class
-defined template
"object-expression" using object literal syntax
object type inclusion: compose object declarations
not the same as implementation inheritance
Ballerina does not provide implementation inheritance
Object construction
type Hashable object { function hash() returns int; }; function h() returns Hashable { var obj = object { function hash() returns int { return 42; } }; return obj; }
Object type inclusion
type Cloneable object { function clone() returns Cloneable; }; type Shape object { *Cloneable; function draw(); }; class Circle { *Shape; function clone() returns Circle { return new; } function draw() { } }
provides nominal typing capabilities
use distinct
modifier
Distinct types
type Person distinct object { public string name; }; distinct class Employee { *Person; function init(string name) { self.name = name; } } distinct class Manager { *Person; function init(string name) { self.name = name; } }
Vertical pipe |
indicates type can be one of two+ types
meaning either type satisfies the type restriction
string|error
indicates string OR error
nullable types (string?
) are essentially a union type
error?
== error|()
json
type is essentially a large union type
string|int|array|map
services need to be highly concurrent
Ballerina wants to elevate this to compile-time validation
So Ballerina will try to compile-time enforce concurrency
isolated
functions/methods ideal for concurrency
they do not use anything outside of parameters passed in
compiler-enforced!
immutable values enforced via readonly
modifier
applicable to function parameters
immutable copies of mutable values can be generated via cloneReadonly()
method
while
foreach
if/else
match
while
boolean {
... }
terminated by:
boolean condition changing to false
break
return
early loop cessation via continue
parentheses optional around boolean expression
old-school range-based uses range operator
foreach int x in 0 ..< 10
or over any collection
foreach Student s in studentArray
if
boolean {
... }
if
boolean {
... } else {
... }
if
boolean {
... } else if
...
parentheses optional around boolean expression
constant pattern
compared against compile-time value
binding pattern var x
binds value into local-variable x
wildcard pattern _
"default" case
list pattern [1, 2, 3]
matches against values in source sequence
list values can actually be any of these patterns
rest match pattern ... var r
binds the remainder of the values into r
mapping pattern {command: "add", amount: var x}
matches against key/values in source map (or json)
value of key/values can be any of these patterns
error pattern error (...)
]
binds to particular error types/values
error types instead of exception handling
in conjunction with check
keyword
asynchronous function invocation and futures
start
, wait
, future types
function
name(
type name (, ...) ) returns
type {
... }
or function
name(...) =>
expression
single-return value (no value/void is ()
)
name is optional
assignable to variables (first-class value)
arguments can be passed either positionally or "by name"
declared parameters can have default values
defaulted parameters must come after non-defaulted
reference parameters by name instead of position
name =
value syntax
must come after all positional arguments
supports variable-number parameters
behaves as an array of the declared type
final parameter in parameter list
(behaves very similarly to JS rest/spread syntax)
use start
before call to make it async
each asynchronous call runs on a separate strand
results are now future
s
future values can be obtained via wait
keyword
ballerina/jballerina.java
module
external
keyword
handle
basic type
Annotations to match to name-based metadata
Connecting to JVM field
// Returns the `out` field in the `java.lang.System` class. // `java.lang.System#out` is of the `java.io.PrintStream` type. public function getStdOut() returns handle = @java:FieldGet { name: "out", 'class: "java/lang/System" } external;
Connecting to JVM method
// Invoke the `println` method on `java.lang.System#out`, // which accepts a `java.lang.String` parameter. public function printInternal(handle receiver, handle strValue) = @java:Method { name: "println", 'class: "java/io/PrintStream", paramTypes: ["java.lang.String"] } external;
Using both
public function main() { handle stdOut = getStdOut(); handle str = java:fromString("Hello World"); printInternal(stdOut, str); }
Architect, Engineering Manager/Leader, "force multiplier"
http://www.newardassociates.com
http://blogs.newardassociates.com
Sr Distinguished Engineer, Capital One
Educative (http://educative.io) Author
Performance Management for Engineering Managers
Books
Developer Relations Activity Patterns (w/Woodruff, et al; APress, forthcoming)
Professional F# 2.0 (w/Erickson, et al; Wrox, 2010)
Effective Enterprise Java (Addison-Wesley, 2004)
SSCLI Essentials (w/Stutz, et al; OReilly, 2003)
Server-Based Java Programming (Manning, 2000)