For the services that you want to invoke, you can create endpoint objects of different types. The objects during initialization use connectors to establish the communication. It's like doing a constructor call in an OOP. You can import different packages the way you would with any language and shared packages are available on central.
Once you have one of those endpoint objects, you can do type safe invocations against it. So you might create a Twitter connection creating a "tweeter" object. That connector then has functions which can be invoked, "tweeter -> tweet (params);". This returns a data structure that is strongly typed and mapped to the payload expected in return from the service.
It's also pretty simple to write your own packages which have your own connectors. The push / pull dynamics of packages with central are similar to Elk or DockerHub, but applied to code modules.
I'll read more on ballerina.io anyway later to see if I can answer this.
On the surface every networked call feels like a method call, so absolutely, within any other language it could be done with a nicely prepared API wrapper for a similar type of connector. And, ultimately, that is what happens within a lot of ESB products that standardize how a connector should be deployed, versioned, and consumed by client applications within that environment.
Beyond that, though, the approach to the language design and the underlying implementation of connectors offers simplicity benefits that should make consuming new connectors easier and more reliable. A few things: 1. All network-bound calls are required to use arrow `->` notations instead of dot `.` to make some important distinctions. One is that sequence diagrams make this distinction and we can auto-generate sequence diagrams from any code file as the code structure is designed to reflect how integrators model their services.
2. Treating an endpoint as a native keyword and how that endpoint object is initialized within the language makes it opinionated, which ultimately makes configuration of some complex connectors simpler than what would be seen in API wrappers. If you take a look at the circuit breaker example, the circuit breaker is added as a configuration parameter to an outgoing HTTP connection. Of course, other languages can be opinionated about this as well, but they almost always delegate this to an add-on framework, and there are always some minor quirks to how frameworks work with their language of choice. So we get to bypass that and ultimately the syntax gets minimized to the point where it's easier to embrace and learn.
3. The underlying concurrency model in the language's runtime is based upon workers for parallelization, which are how sequence diagrams are modeled. Languages like Java map threads to classes. Node maps threads to the event processor, and so forth. When an HTTP -> call is made, we can do some nice internal optimizations such that while the code that you write will see that network call as a blocking call, behind the scenes, the runtime treats the outbound request as one worker and receiving the response as a second worker. Each worker is mapped to a different thread and you get some nice throughput mechanics because the system's scheduler releases a thread when the request is made. Because we know that any connector's invocation is going over a network with this syntax, there are interesting thread scheduling algorithms we can implement. We are seeing about 5x the TPS with a simple routing service built in Ballerina vs. the same type of service that we would implement in an ESB like Camel or Apache.
There are other thing that the designers will probably comment on, too.