Skip to main content

Upgrading

7 min read Last updated
On this page

This page lists every breaking change from 0.54 back to 0.37, with the fix for each. Newest first. Read each section between your current version and the target. The full changelog for each version is in Releases.

How to upgrade

  1. Fix deprecations on your current version first. They show only when you ask for them:

    vendor/bin/phel test --warn-deprecations      # or PHEL_WARN_DEPRECATIONS=1
  2. Bump the version and clear the compiled cache:

    composer require phel-lang/phel-lang:^0.54
    vendor/bin/phel cache:clear
  3. Run your tests again, and rebuild any downstream project.

Never skip the cache clear. Compiled PHP from an older install can reference renamed core types and fail to load.

Going to 1.0 from 0.49 or later? Follow the 1.0 upgrade guide. What a version number promises is on Stability Policy. Releases older than 0.37 are in the changelog.

0.54

OldNew
{:a 1 :a 2} read as {:a 2}PHEL203 error. Remove the repeated key
(defn sq [n] ...) then (sq 3 4) ranPHEL002 error. Pass one argument per parameter
{:my/keys [a]} read :aReads :my/a. Write {:keys [a]} to read :a
A :refer of a name the namespace does not define failed at the call, or neverPHEL013 error at the ns form. Drop the name or define it
transduce with a reducer that has only a 2-arityFails when it completes. Wrap the reducer in completing
Implementing CompilerFacadeInterfaceAdd withoutDeprecations(), rejectSupersededForms() and findSimilarNames()

A :refer of a private name fails the same way. ^:dynamic and ^:redef fns skip the argument count check. The last row affects PHP code that implements Phel's PHP API only. 0.54 release notes.

0.53

OldNew
Older PHPPHP 8.5 is the minimum
"\400" (octal escape above \377) compiled to NULCompile error. Use a valid escape
Phel\Filesystem\FilesystemFacadeInterfacePhel\Shared\Facade\FilesystemFacadeInterface
Phel\Fiber\FiberFacadeInterfaceType-hint Phel\Fiber\FiberFacade
MetaInterface::withMeta() mutatingReturns a copy. Keep the returned value
Lint rule code constantsMoved to Phel\Shared\LintRuleCodes, same strings

The last four affect PHP code that calls Phel's PHP API only. 0.53 release notes.

0.52

OldNew
(php/new \Foo arg)(new \Foo arg) or (\Foo. arg)
(php/-> obj (method arg))(.method obj arg), fields with (.-field obj)
(php/:: \Foo (method arg))(\Foo/method arg), constants with \Foo/CONST
(set-var v x)(alter-var-root (var v) (constantly x))
CommandFacadeInterface::getRuntimeErrorReport() resultReturns a string
ErrorCode::INVALID_QUOTE, INVALID_UNQUOTE, INVALID_CHARACTERRemoved

Writing one of the first four forms in source is a PHEL012 error. The compiler still emits them, so macros that expand to them keep working. The rest of php/* stays. Runtime errors now carry a code: see the Error Reference. 0.52 release notes.

0.51

OldNew
Bare all-caps class as a value: (def driver-class PDO)Write \PDO, (:use PDO), or PDO/class. A bare name reads as a global constant
Constant holding a class name as a member targetUse php/NAME, or bind it with let first

The compiler warns at each all-caps site before it fails. See ADR 0016.

Deprecated (warns under --warn-deprecations):

OldNew
to-php-arrayto-array
Key-first map destructuring {:key local}Binding-first {local :key}

0.51 release notes.

0.50

Everything that printed a deprecation notice in 0.49 is gone. Run your tests with --warn-deprecations on 0.49 first. A clean run means the upgrade is a plain version bump.

OldNew
push, put, unsetconj, assoc, dissoc
put-in, unset-inassoc-in, dissoc-in
valuesvals
function?, hash-map?fn?, map?
ididentical?
str-contains?phel.string/contains?
set-meta!with-meta
#| |# and bare # comments; or ;;
|(...) short functions#(...) with %
, and ,@ unquote~ and ~@
foo$ auto-gensymfoo#
Lazy seqs print as @[1 2 3]Print as (1 2 3)
Wrong arity on a core fn was ignoredRaises an arity error. arity reports 0 for multi-arity fns
(max), (min) threw at runtimeCompile error PHEL002
Unresolved (:require ...) failed on first useFails at require time
Gacela 1, symfony/console 6gacela-project/gacela ^2.0, symfony/console ^7.3|^8.0. Rename DependencyProvider classes to Provider
phel index --out--output (-o)
phel config --json--format=json (-f json)
Warning

, and foo$ fail silently. , is now whitespace, so `(f ,x) still parses but quotes x instead of unquoting it. foo$ is now an ordinary symbol, so a macro that binds tmp$ compiles but loses its unique name. Search everything that generates Phel, not only .phel files:

grep -rnE ",[A-Za-z0-9_(\[{'\`~@:*+-]" --include='*.phel' src/ tests/

0.50 release notes.

0.49

No breaking changes. Behaviour changes:

  • partition and partition-all accept Clojure's [n step coll] arity, and partition also [n step pad coll].
  • Sorted maps and sets treat NaN as equal to itself and greater than every number. (count (sorted-set NAN NAN)) is 1.
  • pr and prn print a char literal like \A as "A".
  • Opt-in PhelConfig::withStripSymbolMeta() drops symbol metadata from build output. With it on, phel doc and (meta ...) over built defs return nil.
  • PhelConfig::withAppModulePaths() limits Gacela module discovery. The default is still the whole root.

0.49 release notes.

0.48

No breaking changes. Behaviour changes:

  • The compiled-code cache key hashes only the .phel source. Old cache entries are invalidated once on first run.
  • In read-only environments, caches degrade quietly and commands report a clear error instead of a fatal.

0.48 release notes.

0.47

No breaking changes. Behaviour changes:

  • phel test prints full structural diffs (+, -, ~) for differing collections.
  • phel compile prints folded values to stderr when a form emits no PHP.
  • phel init enables optimization level 2 in new configs. Existing configs are untouched.

0.47 release notes.

0.46

OldNew
PhelConfig setX() setterswithX() methods
useLayout(), useNestedLayout(), useFlatLayout()withLayout(ProjectLayout::...)
setX() on PhelBuildConfig, PhelExportConfigwithX() methods
phel build exited 0 when compilation abortedExits non-zero. CI may start failing as intended
Broken phel-config.php threw a stack traceClear error naming the file, exit code 1

The incremental build cache now recompiles namespaces that depend on a changed one. 0.46 release notes.

Older releases (0.45 to 0.37)

0.45

OldNew
argv*argv*
phel index --out, phel config --json--output, --format=json (old flags warned, removed in 0.50)
Overflowing constant int arithmetic folded to floatFolds to BigInt, like the runtime
Integer floats printed inconsistentlystr, print and the REPL all keep .0

0.45 release notes.

0.44

OldNew
gacela-project/gacela older than 1.15^1.15
phel test exited 0 when nothing ranNon-zero. Bad paths and selectors fail loudly
await-all and pmap returned in completion orderReturn in input order
composer test-docs, tests/doctest/Removed. Guides live on this site

Edits to phel-config.php take effect immediately again. 0.44 release notes.

0.43

OldNew
never, void or null :tag return on a value-returning fn was a load-time fatalCompile error. mixed, ?T and union tags still pass

0.43 release notes.

0.42

OldNew
Structs printed as (my\ns\point 1 2)Print as (my.ns.point 1 2). Update snapshot tests
str/index-of with "" threw ValueErrorReturns nil
Lexer columns counted in bytesCounted in code points
if-let, when-let, if-some, when-first could capture a user bindingHygienic

The typed interop added in 0.42 used php/new, php/-> and php/::. Those are PHEL012 errors since 0.52, see 0.52. 0.42 release notes.

0.41

OldNew
take with a non-int count, remove or select-keys on a non-seqable, int/long/float/double on non-numbers, get/assoc/update with bad keys leaked a PHP TypeErrorRaise a Phel error. Pass real values
map, filter, remove, concat, distinct, repeatedly realized their head eagerlyFully lazy. Force with doall or vec
map over nilReturns a lazy seq
LazySeq dropped nil valuesKeeps them

0.41 release notes.

0.40

OldNew
phel agent-install --with-docsDocs copied by default. Opt out with --no-docs
:keys, :strs, :syms with a non-vector value dropped the bindingShape error

0.40 release notes.

0.39

Core types renamed to match Clojure:

OldNew
VariableAtom
UuidUUID
BigIntegerBigInt
RationalRatio
PhelFutureFuture
ExInfoExceptionExceptionInfo
LazyConsCons
...Interface suffix, e.g. LazySeqInterfaceLazySeq

Common Phel\Lang\* types now resolve without (:use ...). Your own (:use ...) still wins. 0.39 release notes.

0.37

OldNew
PhelConfig setX()Immutable withX() chain (see Configuration)
PhelConfig::forProject() argumentsforProject(ProjectLayout $layout = Flat, string $mainNamespace = ''): layout first, Flat by default
Phel\PrinterPhel\Shared\Printer. In Phel: (:use Phel.Shared.Printer.Printer)
Cross-module exceptions, CodeSnippetPhel\Shared\Exceptions, Phel\Shared\Parser\ReadModel
Runtime state (cache, REPL history, error log) in several placesUnder .phel/. Override with withPhelDir('...') or PHEL_DIR

0.37 release notes.