Ballerina Swan Lake: 10 Compelling Language Characteristics for Cloud Native Programming

2022-06-25 03:58:39 By : Ms. Sisi Xu

Live Webinar and Q&A: How to Choose A Cloud Database (Live Webinar July 7th, 2022) Register Now

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Avdi Grimm describes the future of development, which is already here. Get a tour of a devcontainer, and contrast it with a deployment container.

Fran Mendez discusses event-driven or asynchronous APIs, comparing AsyncAPI with OpenAPI/Swagger, AMQP/MQTT/Kafka with HTTP, and publish/subscribe with request/response.

Lola Priego, Jose del Pozo discuss how they improved the user input accuracy, normalized lab data using a scoring algorithm, and how this work finishes with an AI to diagnose health.

In this podcast Shane Hastie spoke to Rachel Obstler of Heap Analytics about product-led growth, designing for customer self-service and how high growth products are constantly experimenting.

This article shares how Apache DolphinScheduler was updated to use a more modern, cloud-native architecture. This includes moving to Kubernetes and integrating with Argo CD and Prometheus. This improves substantially the user experience of deploying, operating, and monitoring DolphinScheduler.

Learn how to migrate an application to serverless and what are the common mistakes to avoid. Register Now.

Learn how cloud architectures help organizations take care of application and cloud security, observability, availability and elasticity. Register Now.

Understand the emerging software trends you should pay attention to. Attend in-person on Oct 24-28, 2022.

InfoQ Homepage Articles Ballerina Swan Lake: 10 Compelling Language Characteristics for Cloud Native Programming

Integration can be considered as a type of programming and very often has become a visual representation with different techniques that have tried to simplify and abstract the complexity. DSLs have become quite popular because they provide the right abstractions for programming but they come with limitations—most often than not, integration developers have to use regular code to address parts of the problem. Furthermore, integration programming practices have become isolated such that developers programming with the chosen integration tool must develop the rest of their applications with another tool or programming language. And visual representation remains important to be able to observe flows and interactions between endpoints. On top of all that, with cloud native engineering, integration systems are now running in containers, and applications are created using microservices that are distributed across a wide number of nodes. 

Wouldn’t it be extremely useful to have a language that provided the capabilities to integrate with code and also tools for visualization? So that we can address the limitations of DSL and also be able to preserve software engineering best practices? This thinking is what precisely led to the Ballerina project. The goal was to create a modern programming language that combines the best of programming languages, integration technology, and cloud native computing into a textual and graphical language optimized for integration and with mainstream potential.  

Let’s go through the list below to explore the key characteristics of the Ballerina Language that have been either introduced or enhanced with the Swan Lake Beta release and thereby understand why Ballerina is a great choice for creating network services and distributed application integration. 

Reliable, scalable, secure, and flexible IoT Messaging platform trusted by more than 130 companies across industries. Learn more.

You can think of languages being on a spectrum from scripting languages like Perl or Awk to system languages like Rust or C with application languages like Go or Java in the middle. Programs can be written with one-line scripts all the way up to writing millions of lines of code. Ballerina resides on the spectrum between scripting languages and application languages. 

Ballerina has unique features that make it particularly worthwhile for smaller programs. Most other scripting languages that are designed for smaller programs have significant differences from Ballerina in that they are dynamically typed and they don't have the unique scalability and robustness features that Ballerina has. Problems in the pre-cloud era that you could solve with other scripting languages are still relevant problems. Except now, network services are involved; robustness is now more important than ever. With standard scripting languages, a 50-line program tends to become an unmaintainable 1000-line program a few years later, and this doesn’t scale. Ballerina can be used to solve problems addressed with scripting language programs but it's much more scalable, more robust, and more suitable for the cloud. Scripting languages also typically don't have any visual components, but Ballerina does. 

In network interactions, the object-oriented approach bundles data with the code, which is not the most optimal way to send data across widely distributed networks of microservices and APIs. 

In the pre-cloud era, your APIs are function calls to libraries in the path, and you can pass objects in the call. But you can't do this when your APIs are in the cloud. You want to be able to send data over the network that's independent of code because you don't want to expose your code. Although JAVA RMI and CORBA tried to support distributed objects, it works when you have tight coupling and if both ends belong to the same party. Distributed objects don't work in the loosely coupled cloud. Ballerina emphasizes plain data that is independent of any code used to process the data. While Ballerina provides objects for internal interfaces, it is not an object-oriented language. 

With more services in the cloud, a developer is given the added responsibility of working with networked resources in their code. The programming language itself must aid in this operation. That’s why Ballerina comes with a network-friendly type system with powerful features to handle data on the wire. 

JSON is the ‘lingua franca’ in Ballerina. The data types in Ballerina are very close to JSON, and a fundamental set of data types which are numbers, strings, maps, and arrays map one-to-one to JSON. Ballerina’s plain in-memory data values are pretty much in-memory JSON. This allows a JSON payload from the wire to come immediately into the language and be operated on without transformation or serialization.

The type system of a programming language allows you to describe how your bits fit together and has evolved beyond catching a class of errors—that's now only a small part of what a type system does for you. It’s also a big part of providing a good IDE experience.

Scripting languages have dynamic typing, and application languages have the traditional static types like in C++ or Java. As discussed earlier, Ballerina is a scripting language but it also comes with features from application languages, including a static type system. In a statically-typed language, type compatibility is checked at compile-time. Statically-typed languages are generally more robust to refactoring, easier to debug, and aids in creating better language tooling.

Even though Ballerina’s type system is static, it's much more flexible than the types you get in application languages. The type system of the Ballerina language is primarily structural with added support for nominal typing. This means that the type compatibility is identified by considering the structure of the value rather than just relying on the name of the type. This is different from languages like Java, C++, and C# that have nominal type systems, in which the type compatibility is bound by the name of the actual type. You can say as much or as little about the structure as you need to. And with that, we get simplicity and flexibility by accepting that some things won't be caught at compile time.  

To elaborate, you can say it’s similar to the way the structure is defined in an XML schema. If there's a change or deviation in the XML payload that the program receives,  it still processes what it can recognize. It’s not so strict as to cause a failure if something changes in the payload that isn't recognized.  Ballerina’s type system works both as a schema language to describe network data as well as a type system for the program working on values in memory. 

Read more about Ballerina’s flexible type system here. 

Ballerina also comes with a set of language features for working on data, out of which the integrated query feature stands out. This feature allows you to query the data using an SQL-like syntax as shown below. Query expressions contain a set of clauses similar to SQL to process the data. They must start with the from clause, and they can perform various operations such as filter, join, sort, limit, and projection.

There’s also a Table data type which makes it easy to work with relational and tabular data. The code below creates a table with Employee type members, where each member is uniquely identified using their name field. The main function retrieves the Employee with the key value John and performs a salary increase for each employee. 

Read more about writing integrated queries in Ballerina here. 

Moreover, Ballerina provides in-built XML support with functionality similar to XQuery with an XML navigation mechanism like XPath. This is especially useful for people who work heavily with XML but don't want to use an XML-specific language because they work with a variety of data formats these days. 

Among other capabilities to handle different types of data seamlessly, there’s also a decimal data type in Ballerina. These are floating point numbers designed for business needs, e.g., to indicate prices. Because values are represented in binary in ordinary languages, they cannot represent all real numbers accurately. When there are more digits than the format allows, the leftover ones are omitted—the number is rounded and thereby creates precision errors. The real world runs on decimal numbers and this is why we think it’s a powerful capability to have.

Concurrency is a fundamental requirement in the cloud because your network operations have high latency. Scripting languages generally don’t handle concurrency well. Scripting languages like Javascript typically use async functions, which are slightly better than callbacks (but not a lot). With Ballerina, you have a simpler programming model that provides the advantages of async functions but it’s a more straightforward and intuitive approach to concurrency than async functions. 

In Ballerina, the main concurrency concept is a strand, which is similar to a goroutine in Go.  A Ballerina program is executed on one or more threads. A thread may run on a separate core simultaneously with other threads, or may be pre-emptively multitasked with other threads onto a single core. Each thread can be divided into one or more strands, which are language-managed, logical threads of control. From the programmer’s perspective, a strand does look like an OS thread, but it’s not—it’s cheaper and more lightweight. Strands can be scheduled on separate OS threads. Go follows a similar approach but not many programming languages do. In fact, most dynamic scripting languages don’t support concurrency. For example, Python has a global lock, so it doesn't really support parallel execution. 

A function in Ballerina can have named "workers" that each run on a new strand concurrently with the function's default worker and other named workers as shown below:

Ballerina also allows strands to share mutable state. Typically, combining concurrency and shared mutable state can create data races and give incorrect results. This is one reason why dynamic languages don't usually expose threads. Ballerina, however, comes with a nice solution to this problem and ensures concurrency safety via cooperative multitasking. No two strands belonging to the same thread can run simultaneously. Instead, Ballerina cooperatively (not preemptively) multitasks all strands onto a single thread to avoid locking problems. This is similar to asynchronous functions where everything runs on a single thread but without a complicated programming model. A strand enables cooperative multitasking by yielding. When a strand yields at a specific “yield point”, the runtime scheduler can suspend execution of the strand, and switch its thread to executing another strand. 

We can also determine when we can run strands in parallel—an annotation can be used to make a strand run on a separate thread. This is because Ballerina’s unique type system makes it possible to determine when services have locked enough to be able to safely use multiple threads to handle incoming requests in parallel. While this may not seem to provide a massive amount of parallel executions, it’s enough to make effective use of common cloud instance types. 

Handling concurrency and network interactions are an inherent part of writing a cloud program. In Ballerina, every program is a sequence diagram that illustrates distributed and concurrent interactions automatically. A function in a Ballerina program has equivalent representations both in textual syntax and as a sequence diagram. You can switch between the two views seamlessly. Ballerina’s unique graphical view wasn’t an afterthought. In fact, it has been designed deeply into the language in order to provide real insight with respect to a function’s network interactions and its use of concurrency. A sequence diagram is the kind of diagram that works best for that.

To explain, Ballerina’s named workers (discussed in point 5) and other function-level concurrency features depict concurrency, and language abstractions for clients and services depict network interactions. The vertical lines, also known as lifelines, represent workers and remote endpoints. A remote endpoint is a client object, and it contains remote methods that represent outbound interactions with a remote system. There’s distinct syntax for remote method calls and most languages don't distinguish remote calls from regular function calls. The horizontal lines represent the messages sent from a function's worker to another worker or from a function's worker to a remote endpoint. Ballerina easily distinguishes these key aspects and shows the user a high level view of them without the user having to do anything. This is only possible because of the way the graphical elements have been designed into the language from the start. You don’t get this in any other language!

The Ballerina VSCode plugin can generate a sequence diagram dynamically from the source code. To start generating a sequence diagram from the above Ballerina code, download the VSCode plugin and launch the graphical viewer.

Along with a network-aware type system, Ballerina comes with fundamental syntax abstraction for working with network services. The language also includes built-in support to deploy Ballerina applications on the cloud using Docker and Kubernetes.

Service Objects to Produce Services

Ballerina accommodates the concept of a service, and a service can be written in just three or four lines of Ballerina code. Services in Ballerina are powered by three things working together: applications, listeners and libraries. The application defines service objects and attaches them to listeners. Listeners are provided by libraries; for example, there’s a listener for each protocol (HTTP/GraphQL etc.), which is provided by a library. The listener receives network input and then makes a call to the application to find service objects. Service objects support two interface styles: 

Because Ballerina’s service approach is coupled with its unique wire-oriented type system, you can generate an interface description from the Ballerina code. This can be an OpenAPI or a GraphQL specification. So you can actually write regular ballerina service objects and generate your client code.The combination of these features enables cloud integration to work smoothly.  

Client Objects to Consume Remote Services 

The outbound network interactions are represented by client objects. Clients have remote methods that represent outbound interactions with a remote system. The client object is one of the syntax elements that allows us to draw the sequence diagram. 

Code to Cloud  

Ballerina supports generating Docker and Kubernetes artifacts from code without any additional configuration. This simplifies the experience of developing and deploying Ballerina code in the cloud. Code to cloud builds the containers and required artifacts by deriving the required values from the code. See this example for more details. 

To deploy your code into different cloud platforms, such as AWS and Microsoft Azure, annotations on service objects are used to enable easy cloud deployment, as shown in the code snippet below. The Ballerina compiler can generate artifacts, such as Dockerfiles, Docker images, Kubernetes YAML files, and serverless functions.

For example, Ballerina functions can be deployed in Azure by annotating a Ballerina function with @azure_functions:Function.

The approach to dealing with errors has a pervasive impact on language design and usage. It affects everything about the language. When you're dealing with a network, errors are a normal part of doing business, especially when considering the eight fallacies of distributed computing. Many pre-cloud languages, such as Java, Javascript, and Typescript, used exceptions as a way of dealing with errors. But not every language follows that design. Languages such as Go and Rust don't even have exceptions at all.

With exceptions, the control flow is implicit and the code is harder to understand and maintain. When things go wrong, just conveniently throwing an exception makes everything go completely haywire. To make error handling work, you have to be able to look at the program and understand if there's an error where an error could take place and how the flow of control is going to change. So, there's a fairly strong trend now to eliminate exceptions and go back to a much simpler approach where errors are explicit and use the normal control flow on error. This approach can be found in Go, Rust, and Swift. Ballerina follows the same approach and allows the developer to use the error data type with explicit error control flow.  

Writing Ballerina programs that use transactions is quite straightforward because transactions are a language feature. What Ballerina offers isn't transactional memory but fundamentally language support for delimiting transactions. This way you’re always guaranteed that your transactions have begin, rollback, or commit options. 

A running instance of a Ballerina program includes a transaction manager. This may run in the same process as the Ballerina program or in a separate process (it should not be connected over an unreliable network). The transaction manager maintains a mapping from each strand to a stack of transactions (or, in a distributed context, transaction branches). When a strand's transaction stack is non-empty, we say it is in transaction mode; the topmost transaction on a strand's transaction stack is the current transaction for that strand. 

Transactions in Ballerina also compose with its network interaction features, i.e., clients and services, to support distributed transactions. A user can create transactional flows between a client and service by declaring resources/remote methods of a service and remote methods of a client object as transactional. 

The proliferation of languages does indicate that individuals are willing to learn new languages. But enterprises seem reluctant to adopt a new language because they worry that they won't be able to hire people who are familiar with that language. It’s important to highlight that while Ballerina provides better ways to do things, it also comes with a subset of features that are familiar to a programmer of a C-family language that are enough to get developers started in a couple of hours or less. 

Popular C-family languages (C, C++, Java, JavaScript, C#, TypeScript) have a lot in common, and Ballerina leverages this by doing a lot of things in the same way. If you are a programmer with some amount of programming experience with any of the C-family languages, coding with Ballerina is going to be rather straightforward. And in addition to the powerful language features, Ballerina is ‘batteries included’, which means that the language comes with a rich standard library (with libraries for network data, messaging and communication protocols), a package management system, structured documentation,  a testing framework, and extensions/plug-ins for popular IDEs (notably Visual Studio Code) among other tools to support the language. 

While Ballerina has all the general-purpose functionality of a modern programming language, its niche is that it uniquely provides language features that make it easier to use, combine and create network services for the cloud. Developers can now build resilient, secure, and highly performant services that address the fallacies of distributed computing and integrate them to create cloud native applications by simply using a programming language that is specialized to do just that. 

For a great, quick introduction to creating and consuming HTTP services in Ballerina, take a look at this screencast. And if you learn better from examples, a vast collection of code examples that highlight important Ballerina features and concepts can be found here. 

For an in-depth introductory explanation on the language features of Ballerina Swan Lake, watch this video series by James Clark, Ballerina’s Chief Language Designer. You can also check out his blog posts for more context on the design principles of Ballerina. 

Dakshitha Ratnayake is currently working at WSO2 as the Program Manager for Ballerina. With a background in Software Engineering, she has over 10 years of experience in the roles of Software Engineer, Solutions Architect, and Technology Evangelist at WSO2. Throughout, she has been a technology advocate for WSO2 in the areas of API Management, Enterprise Application Integration, Identity and Access Management, Microservices Architecture, Event-Driven Architecture, and Cloud Native Programming, while also maintaining technical relationships with different parties to translate business requirements and communicate technical strategy.

Becoming an editor for InfoQ was one of the best decisions of my career. It has challenged me and helped me grow in so many ways. We'd love to have more people join our team.

A round-up of last week’s content on InfoQ sent out every Tuesday. Join a community of over 250,000 senior developers. View an example

You need to Register an InfoQ account or Login 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

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

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

A round-up of last week’s content on InfoQ sent out every Tuesday. Join a community of over 250,000 senior developers. View an example

Real-world technical talks. No product pitches. Practical ideas to inspire you and your team. QCon San Francisco - Oct 24-28, In-person. QCon San Francisco brings together the world's most innovative senior software engineers across multiple domains to share their real-world implementation of emerging trends and practices. Uncover emerging software trends and practices to solve your complex engineering challenges, without the product pitches.Save your spot now

InfoQ.com and all content copyright © 2006-2022 C4Media Inc. InfoQ.com hosted at Contegix, the best ISP we've ever worked with. Privacy Notice, Terms And Conditions, Cookie Policy