Pkl 0.33 Release Notes
Pkl 0.33 was released on XXX.
The latest bugfix release is 0.33.0. (All Versions)
The next release (0.34) is scheduled for February 2027. To see what’s coming in the future, follow the Pkl Roadmap.
Please send feedback and questions to GitHub Discussions, or submit an issue on GitHub.
Pkl is hosted on GitHub. To get started, follow Installation.
Highlights π
New standard library module: pkl:net
This release introduces a new standard library module, pkl:net, which brings native URL support to Pkl (#1834).
Its centerpiece is net.Url, a URL as defined by RFC 3986.
A string is parsed with net.Url(), which accepts an absolute URL as well as a relative reference, and which throws if the string cannot be parsed:
import "pkl:net"
local url = net.Url("https://user:pw@example.com:8080/a/b c?q=1&q=2#f")
scheme = url.scheme // "https"
rawUserInfo = url.rawUserInfo // "user:pw"
authority = url.authority // "user:pw@example.com:8080"
rawHost = url.rawHost // "example.com"
rawPort = url.rawPort // "8080"
port = url.port // 8080
rawPath = url.rawPath // "/a/b%20c"
path = url.path // "/a/b c"
pathSegments = url.pathSegments // List("a", "b c")
rawQuery = url.rawQuery // "q=1&q=2"
queryParameters = url.queryParameters // Mapping { ["q"] = Listing { "1"; "2" } }
rawFragment = url.rawFragment // "f"
Parsing is lenient: whatever cannot appear literally in a component is percent-encoded as its UTF-8 bytes, and whatever is already encoded is kept as written.
Every component is held percent-encoded in its raw property, whether the input was encoded or not; the percent-decoded form of each is available as userInfo, host, path, query, and fragment.
The derived pathSegments and queryParameters are percent-decoded too.
A URL can also be built from its components, or derived from another URL by amending it. A component given this way has to be percent-encoded already:
built = new net.Url {
scheme = "https"
rawHost = "example.com"
rawPath = "/a%20b"
}.toString() // "https://example.com/a%20b"
local base = net.Url("https://example.com/a?q=1")
amended = (base) { rawQuery = null }.toString() // "https://example.com/a"
Each component is type-checked, so a URL built this way cannot be malformed.
For example, a rawPort can only hold digits, only a URL with a rawHost can have one, and a rawPath of "/a b" is rejected.
The decoded components are fixed, so they cannot be amended.
A relative reference can be resolved against an absolute URL, which also removes dot segments from the result’s path:
resolved = net.Url("https://example.com/a/b").resolve("../c").toString() // "https://example.com/c"
Comparing two URLs with == compares their components as written.
To instead compare them as the syntax-based normalization of RFC 3986, where scheme and host are case-insensitive and percent-encoding is normalized, use equals():
same = net.Url("HTTPS://Example.com/%7Euser").equals(net.Url("https://example.com/~user")) // true
The module also provides standalone helpers: net.encodeUrlComponent() and net.decodeUrlComponent() for percent-encoding, and net.buildQuery() and net.parseQuery() for application/x-www-form-urlencoded queries.
Alongside this module, pkl:base gains String.isValidUrl, which tells whether a string is a valid absolute URL.
It is stricter than net.Url(), which also parses a relative reference, and which percent-encodes whatever cannot appear literally in a URL component.
The Uri typealias is now defined in terms of it, which is a breaking change.
Noteworthy πΆ
CLI Changes
Default file locations
For new setups, the CLI no longer stores anything under ~/.pkl (#1809).
It uses XDG-style locations on Unix and Known Folder locations on Windows:
| Concern | Unix (Linux/macOS) | Windows | Legacy fallback |
|---|---|---|---|
Package cache |
|
|
none |
Settings file |
|
|
|
CA certificates |
|
|
|
REPL history |
|
|
none |
On every OS, these locations can be overridden with XDG-style env vars.
For example, setting XDG_CACHE_HOME will configure the cache directory.
Note that the existing ~/.pkl/cache directory is ignored, so Pkl will download packages to populate its cache if configured to do so.
Java Library Changes
pkl.net#Url maps to java.net.URI
The JVM bindings now understand the new pkl.net#Url class (#1834).
The Java and Kotlin code generators generate a java.net.URI for a property whose type is net.Url.
Two conversions are added to org.pkl.config.java.mapper.Conversions, both part of the preconfigured set:
-
pUrlToString— gives the same result asUrl.toString() -
pUrlToURI— throwsConversionExceptionif the URL cannot be represented as ajava.net.URI
Standard Library Changes
pkl:net
-
New module introduced.
pkl:base
-
New property:
String.isValidUrl -
Changed typealias:
Uriis nowString(isValidUrl)(breaking change)
Breaking Changes π
Removal of macOS amd64 (Intel) Binaries
Native pkl and pkldoc binaries for Intel Mac systems are no longer published with Pkl releases.
To continue running new Pkl releases on these systems, use an appropriate Java runtime and the jpkl and jpkldoc Java executables.
Type check changes for Class<T>
In prior versions of Pkl, type arguments to the Class type were erased.
Any Class value would typecheck against Class<T> for any value of T.
In Pkl 0.33, this erasure has been removed.
A Class value typechecked against Class<T> must be a subclass of T.
If T does not resolve to a class type (i.e. it is a union type, nullable type, string literal type, parameterized type, or nothing), the type check will always fail.
Standard library external module properties are now marked const
Previously, module properties in pkl:math and pkl:release could be overriden by amending the module.
Doing this makes little senseβthese value are constants and overriding them by amending their module would be odd.
This is a breaking change for code that looks like this:
bogusMath = (import("pkl:math")) {
minInt = -1
}
Changes to join method on List, Set, and Listing
The join method in List, Set, and Listing will now call the toString() method if overridden on an object (#1872).
This is a breaking change, but is also considered a bugfix.
The following snippet will produce different results:
class Bird {
name: String
function toString() = "{ My name is \(name) }"
}
hidden birds: Listing<Bird> = new {
new { name = "Osprey" }
new { name = "Eagle" }
}
res = birds.join("\n")
Currently, Pkl ignores the toString() override, and produces:
res = """
new Bird { name = "Osprey" }
new Bird { name = "Eagle" }
"""
In Pkl 0.33, the overridden toString() will be called, and produces:
res = """
{ My name is Osprey }
{ My name is Eagle }
"""
Uri validates its value
A value typed as Uri must be a valid absolute URL according to RFC 3986.
This means that a relative reference, or a URL containing a character that must be percent-encoded, no longer type-checks:
good: Uri = "https://example.com/some%20path"
bad1: Uri = "./some/path" // throws: not absolute
bad2: Uri = "https://example.com/some path" // throws: space must be percent-encoded
This affects every standard library member typed as Uri, among them Resource.uri, analyze.importGraph(), and the URI-valued properties of pkl:Project, pkl:EvaluatorSettings and pkl:DocPackageInfo.
As a consequence, an invalid URI is now reported as a type constraint violation at the point where it is set, rather than as an error raised later by whatever consumes it.
Bug Fixes π
-
XXX
Contributors π
We would like to thank the contributors to this release (in alphabetical order):
-
XXX