Ballerina is a statically-typed programming language for making development for the cloud an enjoyable experience.
1// Single-line comment
2
3// Import modules into the current source file
4import ballerina/io;
5import ballerina/time;
6import ballerina/http;
7import ballerinax/java.jdbc;
8import ballerina/lang.'int as ints;
9import ballerinax/awslambda;
10// Module alias "af" used in code in place of the full module name
11import ballerinax/azure.functions as af;
12
13http:Client clientEP = new ("https://freegeoip.app/");
14jdbc:Client accountsDB = new ({url: "jdbc:mysql://localhost:3306/AccountsDB",
15 username: "test", password: "test"});
16
17// A service is a first-class concept in Ballerina, and is one of the
18// entrypoints to a Ballerina program.
19// The Ballerina platform also provides support for easy deployment to
20// environments such as Kubernetes (https://ballerina.io/learn/deployment/kubernetes/).
21service geoservice on new http:Listener(8080) {
22
23 @http:ResourceConfig {
24 path: "/geoip/{ip}"
25 }
26 resource function geoip(http:Caller caller, http:Request request,
27 string ip) returns @tainted error? {
28 http:Response resp = check clientEP->get("/json/" + <@untainted>ip);
29 check caller->respond(<@untainted> check resp.getTextPayload());
30 }
31
32}
33
34// Serverless Function-as-a-Service support with AWS Lambda.
35// The Ballerina compiler automatically generates the final deployment
36// artifact to be deployed.
37@awslambda:Function
38public function echo(awslambda:Context ctx, json input) returns json {
39 return input;
40}
41
42@awslambda:Function
43public function notifyS3(awslambda:Context ctx,
44 awslambda:S3Event event) returns json {
45 return event.Records[0].s3.'object.key;
46}
47
48// Serverless Function-as-a-Service support with Azure Functions.
49// Similar to AWS Lambda, the compiler generates the deployment artifacts.
50@af:Function
51public function fromQueueToQueue(af:Context ctx,
52 @af:QueueTrigger { queueName: "queue1" } string inMsg,
53 @af:QueueOutput { queueName: "queue2" } af:StringOutputBinding outMsg) {
54 outMsg.value = inMsg;
55}
56
57// A custom record type
58public type Person record {
59 string id; // required field
60 string name;
61 int age?; // optional field
62 string country = "N/A"; // default value
63};
64
65@af:Function
66public function fromHttpTriggerCosmosDBInput(
67 @af:HTTPTrigger { route: "c1/{country}" } af:HTTPRequest httpReq,
68 @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection",
69 databaseName: "db1", collectionName: "c1",
70 sqlQuery: "select * from c1 where c1.country = {country}" }
71 Person[] dbReq)
72 returns @af:HTTPOutput string|error {
73 return dbReq.toString();
74}
75
76public function main() returns @tainted error? {
77 int a = 10; // 64-bit signed integer
78 float b = 1.56; // 64-bit IEEE 754-2008 binary floating point number
79 string c = "hello"; // a unicode string
80 boolean d = true; // true, false
81 decimal e = 15.335; // decimal floating point number
82
83 var f = 20; // type inference with 'var' - 'f' is an int
84
85 int[] intArray = [1, 2, 3, 4, 5, 6];
86 int x = intArray.shift(); // similar to a dequeue operation
87 x = intArray.pop(); // removes the last element
88 intArray.push(10); // add to the end
89
90 // Tuples - similar to a fixed length array with a distinct type for each slot
91 [string, int] p1 = ["Jack", 1990];
92 [string, int] p2 = ["Tom", 1986];
93 io:println("Name: ", p1[0], " Birth Year: ", p1[1]);
94
95 string name1;
96 int birthYear1;
97 [name1, birthYear1] = p1; // tuple destructuring
98
99 var [name2, birthYear2] = p2; // declare and assign values in the same statement
100
101 // If statements
102 int ix = 10;
103 if ix < 10 {
104 io:println("value is less than 10");
105 } else if ix == 10 {
106 io:println("value equals to 10");
107 } else {
108 io:println("value is greater than 10");
109 }
110
111 // Loops
112 int count = 10;
113 int i = 0;
114 while i < 10 {
115 io:println(i);
116 }
117 // Loop from 0 to count (inclusive)
118 foreach var j in 0...count {
119 io:println(j);
120 }
121 // Loop from 0 to count (non-inclusive)
122 foreach var j in 0..<count {
123 io:println(j);
124 }
125 // Loop a list
126 foreach var j in intArray {
127 io:println(j);
128 }
129
130 json j1 = { "name" : name1, "birthYear" : birthYear1, "zipcode" : 90210 };
131 io:println(j1.name, " - ", j1.zipcode);
132 // New fields are added to a JSON value through "mergeJson"
133 var j2 = j1.mergeJson({ "id" : "90400593053"});
134
135 // XML namespace declaration
136 xmlns "http://example.com/ns1" as ns1;
137 xmlns "http://example.com/default";
138
139 // XML variable from a literal value
140 xml x1 = xml `<ns1:entry><name>{{name1}}</name><birthYear>{{birthYear1}}</birthYear></ns1:entry>`;
141 io:println(x1);
142 // Access specific elements in the XML value
143 io:println(x1/<name>);
144 // List all child items in the XML value
145 io:println(x1/*);
146
147 // Function invocations
148 x = add(1, 2);
149 io:println(multiply(2, 4));
150 // Invocation providing value for the defaultable parameter
151 io:println(multiply(3, 4, true));
152 // Invocation with values to a rest parameter (multi-valued)
153 io:println(addAll(1, 2, 3));
154 io:println(addAll(1, 2, 3, 4, 5));
155
156 // Function pointers
157 (function (int, int) returns int) op1 = getOperation("add");
158 (function (int, int) returns int) op2 = getOperation("mod");
159 io:println(op1(5, 10));
160 io:println(op2(13, 10));
161
162 // Closures
163 (function (int x) returns int) add5 = getAdder(5);
164 (function (int x) returns int) add10 = getAdder(10);
165 io:println(add5(10));
166 io:println(add10(10));
167
168 int[] numbers = [1, 2, 3, 4, 5, 6, 7, 8];
169 // Functional iteration
170 int[] evenNumbers = numbers.filter(function (int x) returns boolean { return x % 2 == 0; });
171
172 // Union types - "input" is of type either string or byte[]
173 string|byte[] uval = "XXX";
174
175 // A type test expression ("uval is string") can be used to check the
176 // runtime type of a variable.
177 if uval is string {
178 // In the current scope, "uval" is a string value
179 string data = "data:" + uval;
180 } else {
181 // Since the expression in the "if" statement ruled out that it's not a string,
182 // the only type left is "byte[]"; so in the current scope, "uval" will always
183 // be a "byte[]".
184 int inputLength = uval.length();
185 }
186
187 // Error handling
188 string input = io:readln("Enter number: ");
189 int|error result = ints:fromString(input);
190 if result is int {
191 io:println("Number: ", result);
192 } else {
193 io:println("Invalid number: ", input);
194 }
195
196 // A check expression can be used to directly return the error from
197 // the current function if its subexpression evaluated to an error
198 // value in the runtime.
199 int number = check ints:fromString(input);
200
201 // Concurrent execution using workers in a function
202 doWorkers();
203
204 // Asynchronous execution with futures
205 future<int> f10 = start fib(10);
206 var webresult = clientEP->get("/");
207 int fresult = wait f10;
208 if webresult is http:Response {
209 io:println(webresult.getTextPayload());
210 io:println(fresult);
211 }
212
213 // Mapping types
214 map<int> ageMap = {};
215 ageMap["Peter"] = 25;
216 ageMap["John"] = 30;
217
218 int? agePeter = ageMap["Peter"]; // int? is the union type int|() - int or nill
219 if agePeter is int {
220 io:println("Peter's age is ", agePeter);
221 } else {
222 io:println("Peter's age is not found");
223 }
224
225 Person person1 = { id: "p1", name : "Anne", age: 28, country: "Sri Lanka" };
226 Scores score1 = { physics : 80, mathematics: 95 };
227 score1["chemistry"] = 75;
228 io:println(score1["chemistry"]);
229
230 Student student1 = { id: "s1", name: "Jack", age: 25, country: "Japan" };
231 student1.college = "Stanford";
232 string? jacksCollege = student1?.college; // optional field access
233 if jacksCollege is string {
234 io:println("Jack's college is ", jacksCollege);
235 }
236
237 // Due to the structural type system, "student1" can be assigned to "person2",
238 // since the student1's structure is compatible with person2's,
239 // where we can say, a "Student" is a "Person" as well.
240 Person person2 = student1;
241
242 map<int> grades = {"Jack": 95, "Anne": 90, "John": 80, "Bill": 55};
243 Person px1 = {id: "px1", name: "Jack", age: 30, country: "Canada"};
244 Person px2 = {id: "px2", name: "John", age: 25};
245 Person px3 = {id: "px3", name: "Anne", age: 17, country: "UK"};
246 Person px4 = {id: "px4", name: "Bill", age: 15, country: "USA"};
247 Person[] persons = [];
248 persons.push(px1);
249 persons.push(px2);
250 persons.push(px3);
251 persons.push(px4);
252
253 // Query expressions used to execute complex queries for list data
254 Result[] results = from var person in persons
255 let int lgrade = (grades[person.name] ?: 0)
256 where lgrade > 75
257 let string targetCollege = "Stanford"
258 select {
259 name: person.name,
260 college: targetCollege,
261 grade: lgrade
262 };
263
264 // Compile-time taint checking for handling untrusted data
265 string s1 = "abc";
266 mySecureFunction(s1);
267 // Explicitely make "s2" a tainted value. External input to a Ballerina
268 // program such as command-line arguments and network input are by-default
269 // marked as tainted data.
270 string s2 = <@tainted> s1;
271 // "s2x" is now a tainted value, since its value is derived using a
272 // tainted value (s1).
273 string s2x = s2 + "abc";
274 // The following line uncommented will result in a compilation error,
275 // since we are passing a tainted value (s2x) to a function which
276 // exepects an untainted value.
277 // mySecureFunction(s2x);
278
279 // Instantiating objects
280 Employee emp1 = new("E0001", "Jack Smith", "Sales", 2009);
281 io:println("The company service duration of ", emp1.name,
282 " is ", emp1.serviceDuration());
283
284 // Supported operations can be executed in a transaction by enclosing the actions
285 // in a "transaction" block.
286 transaction {
287 // Executes the below database operations in a single local transactions
288 var r1 = accountsDB->update("UPDATE Employee SET balance = balance + ? WHERE id = ?", 5500.0, "ID001");
289 var r2 = accountsDB->update("UPDATE Employee SET balance = balance + ? WHERE id = ?", 5500.0, "ID001");
290 }
291}
292
293// An object is a behavioural type, which encapsulates both data and functionality.
294type Employee object {
295
296 // Private fields are only visible within the object and its methods
297 private string empId;
298 // Public fields can be accessed by anyone
299 public string name;
300 public string department;
301 // The default qualifier is a "protected" field,
302 // which are accessible only within the module.
303 int yearJoined;
304
305 // The object initialization function; automatically called when an object is instantiated.
306 public function __init(string empId, string name, string department, int yearJoined) {
307 self.empId = empId;
308 self.name = name;
309 self.department = department;
310 self.yearJoined = yearJoined;
311 }
312
313 // An object method
314 public function serviceDuration() returns int {
315 time:Time ct = time:currentTime();
316 return time:getYear(ct) - self.yearJoined;
317 }
318
319};
320
321// Student is a subtype of Person
322type Student record {
323 string id;
324 string name;
325 int age;
326 string college?;
327 string country;
328};
329
330type Scores record {
331 int physics;
332 int mathematics;
333};
334
335type Result record {
336 string name;
337 string college;
338 int grade;
339};
340
341public function getOperation(string op) returns (function (int, int) returns int) {
342 if op == "add" {
343 return add;
344 } else if op == "mod" {
345 return function (int a, int b) returns int { // anonymous function
346 return a % b;
347 };
348 } else {
349 return (x, y) => 0; // single expression anonymous no-op function
350 }
351}
352
353// Two required parameters
354public function add(int a, int b) returns int {
355 return a + b;
356}
357
358// 'log' is a defaultable parameter
359public function multiply(int a, int b, boolean log = false) returns int {
360 if log {
361 io:println("Multiplying ", a, " with ", b);
362 }
363 return a * b;
364}
365
366// 'numbers' is a rest parameter - it can have multiple values,
367// similar to an array.
368public function addAll(int... numbers) returns int {
369 int result = 0;
370 foreach int number in numbers {
371 result += number;
372 }
373 return result;
374}
375
376public function getAdder(int n) returns (function (int x) returns int) {
377 return function (int x) returns int { // returns closure
378 return x + n;
379 };
380}
381
382function fib(int n) returns int {
383 if n <= 2 {
384 return 1;
385 } else {
386 return fib(n - 1) + fib(n - 2);
387 }
388}
389
390// The code in worker blocks "w1" and "w2" are executed concurrency
391// when this function is invoked. The "wait" expressions waits for
392// the given workers to finish to retrieve their results.
393public function doWorkers() {
394 worker w1 returns int {
395 int j = 10;
396 j -> w2;
397 int b;
398 b = <- w2;
399 return b * b;
400 }
401 worker w2 returns int {
402 int a;
403 a = <- w1;
404 a * 2 -> w1;
405 return a + 2;
406 }
407 record {int w1; int w2;} x = wait {w1, w2};
408 io:println(x);
409}
410
411// A function which takes in only an untainted string value.
412public function mySecureFunction(@untainted string input) {
413 io:println(input);
414}