BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Rethinking Programming: Language and Platform for Cloud-Era Application Developers

Rethinking Programming: Language and Platform for Cloud-Era Application Developers

Leia em Português

This item in japanese

Bookmarks

Key Takeaways

  • The modern programming world is split between highly trained professionals who work on low-level code and people who don’t have a deep background but focus on high-level app development that helps meet business requirements. 
  • Ballerina is an open source programming language, which focuses on improving the productivity of the latter group by providing necessary abstractions, tools, and platforms to create cloud native applications.  
  • Ballerina treats the network differently by making networking abstractions like client objects, services, resource functions, and listeners a part of the language’s type system. So developers can use the language-provided types to write network programs that just work.
  • Ballerina's network and data-friendly type system enables developers to model network communication patterns in an intuitive way that enables maximum productivity.
  • Built-in cloud support helps to generate corresponding deployment artifacts by reading defined annotation in source code. These artifacts can be Dockerfiles, Docker images, Kubernetes YAML files, or serverless functions.
  • Ballerina’s abstractions and syntax for concurrency and network interaction have been designed so that there is a close correspondence with sequence diagrams.

Earlier this year, Mike Loukides, the VP of content strategy at O’Reilly Media, published a great article on “Rethinking programming.” It explores how the programming world is splitting between highly trained professionals and people who don’t have a deep background but have a lot of experience building things that meet business requirements.

The former group is involved with bundling tools, frameworks, and platforms while the latter group focuses on creating applications for their business by integrating different systems together. 

Almost all general-purpose programming languages are designed with abstractions and constructs for highly trained professionals to solve some low-level programming problems.

 Ballerina, an open-source programming language, introduces new and fundamental abstractions to help professionals who want a very agile process and use cloud platforms to scale their application with respect to their business requirements.

The Network in the Language

For decades, programming languages have treated networks simply as I/O sources. Because of that, to expose simple APIs, developers have to implement these services by writing an explicit loop that waits for network requests until a signal is obtained. Ballerina treats the network differently by making networking abstractions like client objects, services, resource functions, and listeners a part of the language’s type system so you can use the language-provided types to write network programs that just work.

Using service type and a listener object in Ballerina, developers can expose their APIs by simply writing API-led business logic within the resource function. Depending on the protocol defined in the listener object, these services can be exposed as HTTP/HTTPSHTTP2gRPC, and WebSockets. Ballerina services come with built-in concurrency. Every request to a resource method is handled in a separate strand (Ballerina concurrent unit) and it gives implicit concurrent behavior to a service.

The following code block illustrates a syntax service type in Ballerina.

import ballerina/http;
service serviceName on new http:Listener(8080) {
   resource function newResource(http:Caller caller, http:Request request) {
       // API-led logic
   }
}

In the request-response paradigm, network communication is done by blocking calls, but blocking a thread to a network call is very expensive. Many language frameworks support async invocations, but developers have to implement async/await by using callback-based code techniques. The following Ballerina code snippet shows a simple HTTP GET operation which is seemingly a blocking operation for the developer. However, internally it does an asynchronous execution using non-blocking I/O, where the current execution thread is released to the operating system to be used by others. 

http:Client clientEndpoint = new("http://example.com");

public function main() {
   var response = clientEndpoint->get("/get?test=123");
   ----- 	
}

Due to unreliable network behavior, remote invocations of APIs are vulnerable to failures. Sometimes an automatic retry will help recover from such failures. In some cases, failover techniques will help to have uninterrupted service delivery or techniques like circuit breakers help to prevent catastrophic cascading failure across multiple programs. Shipping these techniques as part of Ballerina’s client object helps developers to write resilient and robust code with remote network invocations. 

The following code snippet shows how to configure a circuit breaker to handle network-related errors in the Ballerina HTTP client object.

http:Client backendClientEP = new("http://example.com", {
       circuitBreaker: {
           rollingWindow: {
               timeWindowInMillis: 10000,
               bucketSizeInMillis: 2000,
               requestVolumeThreshold: 0
           },
           failureThreshold: 0.2,
           resetTimeInMillis: 10000,
           statusCodes: [400, 404, 500]
       },
       timeoutInMillis: 2000
   });

Network services have to work with various user input data. In general, all user input can be dangerous if this isn’t properly checked. Taint analysis is designed to increase security by preventing any variable that can be modified by user input. Ballerina’s built-in taint analyzer helps to identify untrusted (tainted) data by observing how tainted data propagates through the program. If untrusted data is passed to a security-sensitive parameter, a compiler error is generated. Since the taint check happens at the compiler stage, the programmer can then redesign the program to erect a safe wall around the dangerous input. 

Network and Data-Friendly Type System

Ballerina supports structural type systems and works with an abstraction of a value called a shape. A shape basically ignores the storage identity of a value. This means it does not consider the name reference of a value when it is compared with other values. This is particularly useful when combining data from multiple, independently-designed systems. 

Ballerina also supports Union types, where sets of values are the union of the value spaces of its component types. For example, you can use a variable of a union type to store a string or an int, but there is only one type of value at any given time. 

Ballerina has built-in network data type support such as JSON and XML. Ballerina json type is designed for processing data expression in JSON format. It is a built-in name for a union defined as follows:

type json = () | boolean | int | float | decimal | string | json[] | map<json>

By default, Ballerina has open records. Let’s take a look at a record type to represent the details of a person.

type Gender "male"|"female";

type Address record {|
   string city;
   string country;
|};

type Person record {
   string name;
   int birthYear; 
   Gender gender;
   Address address?;
};

Here, the type Person is an open record type, defined with an inclusive-record-type-descriptor by using the “{” and “}” delimiters. The Address type is a closed record type with an exclusive-record-type-descriptor by using the “{|” and “|}” delimiters in the definition. Also in the Person record address has the suffix “?”, making it an optional field where it can be skipped without setting a value.

Let’s create a new type Student.

type Student record {
   string name;
   int birthYear; 
   Gender gender;
   Address address?;
   string college;
};

The Student type is a subtype of the Person type. It has an extra field college of type string compared to the Person type. This is possible because the Person type is an open type and its shapes can have the string field called college as well. 

public function main() {

   Student s1 = { name: "John", birthYear: 1995, gender: "male",
                 	college: "US Berkeley" };
   Student s2 = { name: "Kamala", birthYear: 1994, gender: "female",
                 address: { city: "Milpitas", state: "CA" ,country: "USA"}, 
college: "Stanford" };
   Person p1 = s1;
   Person p2 = s2;

   io:println("P1's details:" ,p1);
   io:println("P2's details:" ,p2);

}

$ ballerina run person.bal
Compiling source
    person.bal
Running executables

P1's details:name=John birthYear=1995 gender=male college=US Berkeley
P2's details:name=Kamala birthYear=1994 gender=female address=city=Milpitas state=CA country=USA college=Stanford

Language-integrated query is a feature that allows you to use single syntax against multiple data sources. It will help to break down a complex problem into a series of short, comprehensible queries. 

Ballerina query expressions provide a language-integrated query feature using SQL-like syntax. Unlike SQL statements, query expressions help to identify mistakes during design time because of type safety.

import ballerina/io;

type Student record {
   string name;
   int age;
   string school;
};

type Result record {
   string name;
   string college;
   float gpa;
   string school;
};

public function main() {
   map<float> gpa = {"John": 4.1, "Bill": 3.9, "Sam": 3.3, "Jennifer": 3.1};
   Student s1 = {name: "John", age: 18, school: "Milpitas High School"};
   Student s2 = {name: "Bill", age: 17, school: "San Jose High School"};
   Student s3 = {name: "Sam", age: 18, school: "Clara Coutinho High School"};
   Student s4 = {name: "Jennifer", age: 18, school: "Fremont Union High School"};
   Student[] student = [];
   student.push(s1);
   student.push(s2);
   student.push(s3);
   student.push(s4);

   Result[] stanford = from var candidate in student
                       let float cgpa = (gpa[candidate.name] ?: 0),
                       string targetCollege = "Stanford"
                       where cgpa > 3.8
                           select {
                               name: candidate.name,
                               college: targetCollege,
                               gpa: cgpa,
                               school: candidate.school
                           };   

   io:println(stanford);
}

$ ballerina run query_expression.bal
Compiling source
    query_expression.bal
Running executables

name=John college=Stanford GPA=4.1 school=Milpitas High School name=Bill college=Stanford GPA=3.9 school=San Jose High School

The from clause works similarly to a foreach statement. It creates an iterator from an iterable value and then binds variables to each value returned by the iterator. The let clause binds variables. The where clause provides a way to perform a conditional execution which can refer to variables bound by the from clause. When the where condition evaluates to false, the iteration skips the following clauses. The select clause is evaluated for each iteration and the result of the query expression in this sample is a list whose members are the result of the select clause.

Built-in cloud technology integration 

Earlier, developers just wrote programs, built it, and ran it. But now they have various ways of running it. It can be in a bare-metal machine or a virtual machine. Or else programs can be packaged as a container and deployed into platforms like Kubernetes and service mesh, or run as serverless programs. However, these deployment options are not part of the programming experience for a developer. The developer has to write code in a certain way to work well in a given execution environment, and removing this from the programming problem isn’t good.

Docker helps to package applications and their dependencies in a binary image that can run in various locations, whether on-premises, in a public cloud, or in a private cloud. To create optimized images, developers have to follow a set of best practices, otherwise, the image that is built will be large in size, less secure, and have many other shortcomings. 

The Ballerina compiler is capable of creating optimized Docker images out of the application source code. Adding the @docker:Config {} to a service, generates the Dockerfile and a Docker image.

import ballerina/http;
import ballerina/docker;

@docker:Config {
   name: "hello",
   tag: "v1.0"
}
service Hello on new http:Listener(8080) {
   resource function hi(http:Caller caller, http:Request request) returns error? {
       check caller->respond("Hello World!");
   }
}

$ ballerina build hello.bal
Compiling source
    hello.bal

Generating executables
    hello.jar

Generating docker artifacts...
    @docker    	  - complete 2/2

    Run the following command to start a Docker container:
    docker run -d -p 8080:8080 hello:v1.0

Kubernetes is an open-source platform for automating deployment, and scaling and management of containerized applications. To deploy and run the program in Kubernetes requires developers to create a set of YAML files. To an average developer, creating these YAML files is not easy. The Ballerina compiler is capable of creating these YAML files while compiling the source code. 


import ballerina/http;
import ballerina/kubernetes;

@kubernetes:Service {
   serviceType: "NodePort"
}
@kubernetes:Deployment {
   name: "hello"
}
service Hello on new http:Listener(8080) {
   resource function hi(http:Caller caller, http:Request request) returns error? {
       check caller->respond("Hello World!");
   }
}

Adding the @kubernetes:Deployment{} annotation to the Ballerina service will generate the Kubernetes Deployment YAML that is required to deploy our hello application into Kubernetes. Adding the @kubernetes:Service{} annotation will generate the Kubernetes Service YAML. In this scenario, we have set serviceType as `NodePort` to access the hello service via the nodeIP:Port. 

$ ballerina build hello.bal
Compiling source
    hello.bal

Generating executables
    hello.jar

Generating artifacts...

    @kubernetes:Service    	- complete 1/1
    @kubernetes:Deployment    	- complete 1/1
    @kubernetes:Docker    		- complete 2/2
    @kubernetes:Helm    		- complete 1/1

    Run the following command to deploy the Kubernetes artifacts:
    kubectl apply -f hello/kubernetes

    Run the following command to install the application using Helm:
    helm install --name helloworld hello/kubernetes/helloworld

In the same way, developers can use Ballerina’s built-in annotations to deploy programs into platforms like OpenShiftIstio, and Knative, or as AWS Lambda functions

Sequence Diagram

A sequence diagram is the best way to visually describe how services interact. Ballerina’s

abstractions and syntax for concurrency and network interaction have been designed so that there is a close correspondence with sequence diagrams. In Ballerina, a remote method is invoked using a different syntax (->) from a non-remote method. It is depicted as a horizontal arrow from the worker’s lifeline to the client’s object lifeline in a sequence diagram. 

In general, developers will write code once and read it many times. In many cases, the code is read by another developer instead of the one who originally wrote it. Having these automated visual representations will help developers to understand program interactions. 

The Ballerina IDE plugin (for example, the VSCode plugin) can generate a sequence diagram dynamically from the source code. 

More Reading

In this article, we looked at how the unique features of Ballerina enable application developers to write cloud-native applications.

For more extensive language design principles, refer to the Ballerina language specification.

Various examples of using in-built data types, such as JSON/XML, and other network-based functionality can be found in the Ballerina by Example pages.

About the Author

Lakmal Warusawithana is the Senior Director/Developer Relations of WSO2. Lakmal has a long history of working in open source, cloud, and DevOps technologies, and has been Vice President of Apache Stratos PaaS Project. Lakmal is an architect for containerization and deployment orchestration of Ballerina, an open-source programming language for network distributed application. Lakmal has also presented at numerous events, including ApacheCon, CloudOpen, QCon, JaxLondon, Cloud Expo, Cloudstack Collaboration Conference, WSO2Con, KubeCon, ContainerCamp, DeveloperWeek, API Summit, and many tech meetups.

 

Rate this Article

Adoption
Style

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Community comments

  • Coming along nicely!

    by Cameron Purdy,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    It's looking very nice :-) ... Tyler originally pointed me to your work on this, and he was pretty proud of what you guys had managed to do! I had to look twice at the JSON example -- it looked very similar to the JSON solution we settled on, which has turned out quite nicely (but I had no idea anyone else had already used the same approach, so that makes me really happy to see!) I wish I could find some time to check Ballerina out in more detail.

    Keep it up! And hopefully, when this COVID thing slows down a bit, we can find a conference to meet up and talk.

  • Re: Coming along nicely!

    by Lakmal Warusawithana,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    I'm glad you liked it :) Yeah, I hope to meet you at a conference soon.

  • Very nice language

    by Miroslav Gula,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    I must confess. After I saw the first glimpse of Ballerina, I didn't like it very much. But after reading this article, which highlights some of the key features of the language very well, I'm starting to really adore it. I think it will become standard tool in my software engineering toolbox.

  • Java Interop

    by Kris K,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Ballerina is exciting! Great article. What is the interop picture with Java like? Can we use existing libraries in Java? As most cloud environments have Java based libraries for their services, it would make us easy to explore Ballerina if we can take advantage of the cloud resources (Eg: Kinesis, Azure ServiceBus, Google Pub/Sub etc) via these libraries.

  • Re: Java Interop

    by Lakmal Warusawithana,

    Your message is awaiting moderation. Thank you for participating in the discussion.

    Thanks for your feedback!

    Yes you can call the existing Java code from Ballerina and also provides a Java API to call Ballerina code from Java. The following guide discuss more details.

    ballerina.io/learn/how-to-call-java-code-from-b...

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

BT