enso/docs/polyglot/polyglot-bindings.md
2023-08-30 06:10:18 +02:00

6.9 KiB

layout title category tags order
developer-doc Polyglot Bindings polyglot
polyglot
bindings
interop
1

Polyglot Bindings

This document deals with the specification and design for the polyglot interop system provided in the Enso runtime. This system allows users to connect Enso to other supported programming languages, to both provide access to a wealth of libraries, and to integrate Enso into existing systems.

The polyglot support in Enso is best-in class, and it supports this through two main mechanisms:

  1. Polyglot FFI: The low-level polyglot support provides a fairly low-level syntax sugar system for working with values from foreign languages.
  2. Embedded Syntax: This system allows users to write code from other languages directly in their .enso files, and to seamlessly share values between Enso and that foreign code.

Impedance Mismatch

Polyglot interoperation in Enso has a significant impedance mismatch. In essence, this means that there is a mismatch between Enso's language semantics and the semantics of the foreign languages that are being worked with.

While some of this mismatch can be worked around by manually wrapping the foreign constructs in Enso, there are still concepts that can't easily be represented by Enso.

The actionables for this section are:

  • Expand on the impedance mismatch and how it leads to the defined semantics.

The Polyglot FFI

The low-level polyglot FFI mechanism refers to a way to use polyglot objects directly in Enso code. This can be used to underlie a library implementaion in Enso, or to interoperate with code running in other languages.

The mechanism provides users with the facilities to import bindings from other languages and call them via a generic mechanism.

Importing Polyglot Bindings

When importing a polyglot binding into scope in an Enso file, this introduces a polyglot object into scope. This object will have appropriate fields and/or methods defined on it, as described by the foreign language implementation.

The actionables for this section are:

  • Expand greatly on the detail of this as the semantics of the imports become clearer.

Using Polyglot Bindings

With a polyglot object in scope, the user is free to call methods on it directly. These polyglot objects are inherently dynamically typed, meaning that any operation may fail at runtime.

Enso implements a generic variadic syntax for calling polyglot functions using vectors of arguments. In essence, this is necessary due to the significant impedance mismatch between Enso's runtime semantics (let alone the type system) and the runtime semantics of many of the polyglot languages.

We went the way of the variadic call for multiple reasons:

  • It allows us to match up with a wide range of language semantics (such as subtyping and overloading).
  • It is flexible and easy to expand in the future.
  • We can easily build a more Enso-feeling interface on top of it.

By way of illustrative example, Java supports method overloading and subtyping, two things which have no real equivalent in the Enso type system.

The actionables for this section are:

  • Expand greatly on the runtime semantics of working with polyglot bindings.
  • Determine how to make the inherent 'failability' of polyglot objects safer.

Importing Polyglot Bindings (Syntax)

Polyglot bindings can be imported using a polyglot import directive. This is constructed as follows:

  • The polyglot keyword
  • A language identifier (e.g. java).
  • The keyword import.
  • Optionally (where the language supports it), an identifier for the type of language entity being imported (e.g. struct for c).
  • A path that uniquely identifies the polyglot object to import.
  • Optionally, the keyword as, followed by a new name.

For example:

polyglot java import org.example.MyClass as MyClassJava
polyglot c import struct NetworkPacket as NetworkPacketC

Using Polyglot Bindings (Syntax)

A polyglot binding is a polyglot object that has methods and/or fields defined on it. Due to an impedance mismatch between languages, Enso implements a variadic syntax for calling these polyglot bindings using vectors.

In essence, we have a primitive function as follows:

Polyglot.method : Polyglot.Object -> [Any] -> Any

It works as follows:

  • It is a method called method defined on the Polyglot type. The name method is, however, a stand-in for the name of the method in question.
  • It takes an object instance of the polyglot object.
  • It takes a vector of arguments (and is hence variadic).
  • And it returns some value.

By way of example, the following is a valid usage of a polyglot binding:

polyglot java import com.example.MyClass as MyClassJava

main =
    x = MyClassJava.foo [1, 2, 3]    # a static method
    inst = MyClassJava.new [a, b, c] # a constructor
    bar = inst.metod [x, y]          # a static method

Finding Polyglot Bindings

Polyglot objects for various languages are found in the polyglot subdirectory of an Enso project. This folder is subdivided into directories based on the polyglot language. The name of each subdirectory must match the language identifier used in the source code.

Inside each directory is an implementation-defined structure, with the polyglot implementation for that particular language needing to specify it. Please see the language-specific documentation for details.

Embedded Syntax

The term "Embedded Syntax" is our terminology for the ability to use foreign language syntaxes directly from inside .enso files. This system builds upon the more generic mechanisms used by the polyglot FFI to provide a truly seamless user experience.

Embedded Syntax Usage (Syntax)

A polyglot block is introduced as follows:

  • The foreign keyword starts a block.
  • This must be followed by a language identifier (e.g. python).
  • After the language identifier, the remaining syntax behaves like it is an Enso function definition until the =.
  • After the =, the user may write their foreign code as a string.
foreign python concat a b = """
  def concat(a, b):
    str(a) + str(b)

In the above example, this defines a function concat that takes two arguments a and b, implemented in Python.

The actionables for this section are:

  • Greatly flesh out the syntax for the high-level polyglot functionality.