Experiments >

Programming language

Experiment #19118th May, 2021by Joshua Nussbaum

This is the programming language I wish existed.

The ideas are basically a mix or Elixir/Erlang, Ruby and JavaScript.

Aims

  • A language designed for building business applications
  • Expressive and minimal
  • Functional, imperative and declarative.
  • Actor model, no OOP.
  • Isomorphic
  • Cloud-native
  • Distributed VM

Multi-paradigm

It should support functional, imperative, and declarative programming models. Functional is the default.

# functional
f(x) -> x * 2

It supports imperative too, but it’s scoped. Imperative code is more for lower layers of an app or for libraries. There should be linting rules that block its usage in higher level business logic.

mutable f(x) ->
  x += 2 # mutation allowed here
  x

Declarative syntax is also supported. It makes it easy to define configuration systems or addons to the language.

interpreter belongs_to(context) ->
  grammar ...

Usage:

module Order
  belong_to :user
end

Isomorphic

The same syntax would run on the server and on the client (browser). That means all nodes can message each other without serializing/deserializing, regardless if they are a browser or a server.

To make that possible, multiple transport layers exist under the hood:

  • For comms between different machines: TCP
  • For comms between VMs on the same machine: Pipes
  • For comms between server and browser: WebSockets
  • For comms from external apps: HTTTP

All messages are serialized and transported using the correct layer, without developer intervention.

Cloud-native distributed-VM

Unlike erlang, it’s designed to be deployed to the cloud. It works more like a “Function as a Service” deploy. That means there is a cloud-based registry of actors.

> deploy
Deploying cart.api.domain.tld
Deploying checkout.api.domain.tld

> curl cart.api.domain.tld/status
UP

Queues

Each actor has a queue. When an actor sends another a a message, that means it contacts the registry to locate the actor and then places a message on that actor’s queue.

The actor can specifiy if it prefers a prioritized queue, FIFO queue or shared queue (multiple workers).

Reactive actor-model

It uses the Actor Model instead of OOP, because the Actor Model is more effective for distributed and parallelized workloads. Each actor has a queue and is addressable by name or id. The name can be public and accessible by browser, or private and only accessible by other actors in the cluster. Messages between actors are async by default, because they go on the queue first, but the sender can block waiting for a response. This flavor of Actor Model can be reactive too. That means the active can publish changes, and other actors can subscribe to those broadcasts.

This part is basically a mix of erlang gen_server’s and pub-sub. But unlike erlang, there is no difference between an actor and a process. The lowest level primitive is an Actor.

Example

actor Cart
  strategy :temporary, timeout: 24.hours

  def initialize
    @items = []
  end

  on add(items)
    @items = [...@items, ...items]

    broadcast {:added, items}
    broadcast {:updated, self}
  end

  on remove(item)
    @items = @items . without(item)

    broadcast {:removed, item}
    broadcast {:updated, self}
  end

  on clear
    @items = []

    broadcast :cleared
    broadcast {:updated, self}
  end
end

Each actor is addresable thru a “registry”, which is a “name service” the hosting system provides. Public actors can have DNS too, that makes them accsessible via http/websocket/tcp.

Like in erlang, actors can “monitor” or “link” with other actors. When an actor monitors another, they receive notifications about that actor. When an actor links with another, if either fails, the other is terminated too.

Supervision

Supervisors are a special type of actor that monitors other actors and deals with failures. ie restarting the actor, or using a fallback method.

Meta-programming

Works together with the “declarative” facet of the language. You define an interpreter/grammar, and it can generate code.

I’m not sure if Ruby’s meta-programming style is possible in a functional world. But I prefer that approach to macros - even if macros are faster - because they feel harder to write.

Notes

  • These ideas have tons of holes in them, just wanted to write something down, in case I ever come back to it.
  • I built some of this a few years back. https://github.com/joshnuss/topaz
view all experiments

Stay tuned in

Learn how to add more experimentation to your workflow