Language Binding Specification
Pkl can be embedded within any host application. The host application has access to low level controls. It is able to manage the lifecycle of evaluators, as well as provide custom modules and resources to Pkl.
Currently, Pkl must be embedded as a child process, by shelling out to the CLI using the pkl server
command. In the future, a C library will also be provided.
When embedded, communication between a host application and Pkl happens via message passing. The message passing specification can be found in Message Passing API.
Pkl’s Java and Kotlin libraries binds to Pkl directly, and do not use message passing. |
Features of a language binding
A language binding for Pkl should generally have the following components:
-
A client that spawns
pkl server
, and talks to it using message passing. -
A deserializer that turns pkl binary encoding into a structure in the host language.
-
A code generator that transforms Pkl schemas into schemas written in the host language. The code generator is mostly written in Pkl, with a lightweight executable that acts as the glue layer. For examples of code generators, consult pkl-go and pkl-swift.
Sample flow
Here is a sample flow for evaluating a module with the following contents:
module MyModule
theModules = import*("customfs:/*.pkl")
-
Client sends Create Evaluator Request, including
customfs
as a custom module reader.[ 0x20, { "requestId": 135, "allowedModules": ["pkl:", "repl:", "file:", "customfs:"], "clientModuleReaders": [ { "scheme": "customfs", "hasHierarchicalUris": true, "isGlobbable": true, "isLocal": true } ] } ]
-
Server sends Create Evaluator Response, with an evaluator id.
[ 0x21, { "requestId": 135, "evaluatorId": -135901 } ]
-
Client sends Evaluate Request, providing the module’s URI.
[ 0x23, { "requestId": 9805131, "evaluatorId": -13901, "moduleUri": "file:///path/to/myModule.pkl" } ]
-
During evaluation, server evaluates
import*("customfs:/*.pkl")
, and sends List Modules Request.[ 0x2c, { "requestId": -6478924, "evaluatorId": -13901, "uri": "customfs:/" } ]
-
Client responds with List Modules Response. In our pretend scenario, there is only one file;
foo.pkl
.[ 0x2d, { "requestId": -6478924, "evaluatorId": -13901, "pathElements": [ { "name": "foo.pkl", "isDirectory": false } ] } ]
-
Server sends Read Module Request to read
foo.pkl
.[ 0x28, { "requestId": 36408291, "evaluatorId": -13901, "uri": "customfs:/foo.pkl" } ]
-
Client responds with the module’s contents
[ 0x29, { "requestId": 36408291, "evaluatorId": -13901, "contents": "foo = 1" } ]
-
Server finishes evaluation, and responds with the Evaluate Response.
[ 0x24, { "requestId": 9805131, "evaluatorId": -13901, "result": <pkl binary value> } ]
-
Client sends Close Evaluator.
[ 0x22, { "evaluatorId": -13901 } ]
Debug logs
Set the env var PKL_DEBUG=1
to enable more verbose logging from Pkl.
It is recommended that clients also use this environment variable to enable debug logs of their own.