Ballerina

Личный сайт Go-разработчика из Казани

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}

Further Reading