purescript v0.14.0 Release Notes
-
Polykinds
Polymorphic kinds, based on the Kind Inference for Datatypes paper (#3779, #3831, #3929, #4007, @natefaubion, @kl0tl)
Just as types classify terms, kinds classify types. But while we have polymorphic types, kinds were previously monomorphic.
This meant that we were not able to abstract over kinds, leading for instance to a proliferation of proxy types:
data Proxy (a :: Type) = Proxy data SProxy (a :: Symbol) = SProxy data RProxy (row :: # Type) = RProxy data RLProxy (row :: RowList) = RLProxy
Now we can have a single proxy type, whose parameter has a polymorphic kind.
Type :: Type
The old
Kind
data type and namespace is gone. Kinds and types are the same and exist in the same namespace.Previously one could do:
foreign import kind Boolean foreign import data True :: Boolean foreign import data False :: Boolean
🚚 Where the kind
Boolean
and typeBoolean
were two different things. This is no longer the case. ThePrim
kindBoolean
is now removed, and you can just usePrim
typeBoolean
in the same way. This is a breaking change.🗄 The compiler still supports the old
foreign import kind
syntax but it warns that it's deprecated.foreign import kind Foo
🚀 > Foreign kind imports are deprecated and will be removed in a future release. Use empty 'data' instead.
It is treated internally as:
data Foo
Note that
foreign import data
declarations are not deprecated. They are still necessary to define types with kinds other thanType
since constructors are not lifted as in GHC with DataKinds.🗄 Likewise,
kind
imports and exports are deprecated and treated the same as a type import or export.🚀 > Kind imports are deprecated and will be removed in a future release. Omit the 'kind' keyword instead.
🗄 The special unary
#
syntax for row kinds is still supported, but deprecated and will warn. There is nowPrim.Row :: Type -> Type
which can be used like a normal type constructor.🚀 > Unary '#' syntax for row kinds is deprecated and will be removed in a future release. Use the 'Row' kind instead.
🗄 All of these deprecations have suggested fixes in the JSON output, so tools like
purescript-suggest
(or your IDE plugin) can automatically apply them.Kind Signatures
With PolyKinds, all type-level declarations are generalized.
data Proxy a = Proxy
0️⃣ Previously, this had the
Type
-defaulted kindType -> Type
. Now this will be generalized toforall k. k -> Type
. Such signature can be written with a kind signature declarations, similar to standalone kind signatures in GHC.data Proxy :: forall k. k -> Type data Proxy a = Proxy
In GHC, all signatures use the
type
prefix, but we reuse the same keyword as the subsequent declaration because we already haveforeign import data
(rather thanforeign import type
) and because it makes things align nicer. Signatures have the same rule as value-level signatures, so they must always be followed by the "real" declaration.👍 It's better to be explicit about polymorphism by writing signatures. Since we don't really quantify over free type variables, it's also necessary in the case that two poly-kinded arguments must have the same kind. The compiler will warn about missing kind signatures when polymorphic kinds are inferred.
Classes can have signatures too, but they must end with the new
Constraint
kind instead ofType
. For example, here's the new definition ofPrim.Row.Cons
:class Cons :: forall k. Symbol -> k -> Row k -> Row k -> Constraint class Cons label a tail row | label a tail -> row, label row -> a tail
Safe zero-cost coercions
Coercible constraints, based on the Safe Zero-cost Coercions for Haskell paper (#3351, #3810, #3896, #3873, #3860, #3905, #3893, #3909, #3931, #3906, #3881, #3878, #3937, #3930, #3955, #3927, #3999, #4000, @lunaris, @rhendric, @kl0tl, @hdgarrood)
Prim.Coerce.Coercible
is a new compiler-solved class, used to relate types with the same runtime representation. One can useSafe.Coerce.coerce
(from the newsafe-coerce
library) instead ofUnsafe.Coerce.unsafeCoerce
to safely turn aa
into ab
whenCoercible a b
holds.Roles
Types parameters now have roles, which depend on how they affect the runtime representation of their type. There are three roles, from most to least restrictive:
nominal parameters can only be coerced to themselves.
representational parameters can only be coerced to each other when a Coercible constraint holds.
phantom parameters can be coerced to anything.
Role annotations
The compiler infers nominal roles for foreign data types, which is safe but can be too constraining sometimes. For example this prevents the coercion of
Effect Age
toEffect Int
, even though they have the same runtime representation.✅ The roles of foreign data types can thus be loosened with explicit role annotations, similar to the RoleAnnotations GHC extension.
Here's the annotation we added to
Effect
:type role Effect representational
Conversely, we might want to strengthen the roles of parameters with invariants invisible to the type system. Maps are the canonical example of this: the shape of their underlying tree rely on the
Ord
instance of their keys, but theOrd
instance of a newtype may behave differently than the one of the wrapped type so it would be unsafe to allow coercions betweenMap k1 a
andMap k2 a
, even whenCoercible k1 k2
holds.In order to forbid such unsafe coercion we added a nominal annotation to the first parameter of
Map
:type role Map nominal representational
Annotated roles are compared against the roles inferred by the compiler so it is not possible to compromise safety by ascribing too permissive roles, except for foreign types.
Other changes
💥 Breaking
- ➕ Add compiler support for
Coercible
basedNewtype
(#3975, @fsoikin)
👀 We added a
Coercible
superclass toData.Newtype.Newtype
in order to implementunwrap
,wrap
and most newtype combinators withcoerce
(see https://github.com/purescript/purescript-newtype/pull/22). This is only a breaking change for non derived instances because theNewtype
class has no members anymore and can now only be implemented for representationally equal types (those satisfying the new superclass constraint).For example the instance for
newtype Additive a = Additive a
no longer implementsunwrap
andwrap
:+instance newtypeAdditive :: Newtype (Additive a) a -instance newtypeAdditive :: Newtype (Additive a) a where - wrap = Additive - unwrap (Additive a) = a
Derived instances don't require any modifications.
- Reform handling of quote characters in raw strings (#3961, @rhendric)
Quotes behaved rather unexpectedly in various edge cases inside raw strings. This clears things up by enforcing the following specification:
'"""' '"'{0,2} ([^"]+ '"'{1,2})* [^"]* '"""'
Meaning that raw strings can contain up to two successive quotes, any number of times, but three successive quotes are not allowed inside.
- 👍 Unsupport bare negative literals as equational binders (#3956, @rhendric)
It used to be possible to match on negative literals, such as
-1
, but this prevented parsing matches on constructors aliased to-
. The compiler will reject matches on bare negative literals, but they can still be matched by wrapping them in parentheses.- Forbid partial data constructors exports (#3872, @kl0tl)
Exporting only some of the constructors of a type meant that changes internal to a module, such as adding or removing an unexported constructor, could cause unexhaustive pattern match errors in downstream code. Partial explicit export lists will have to be completed with the missing constructors or replaced by implicit export lists.
- 🖨 Print compile errors to stdout, progress messages to stderr (#3839, @JordanMartinez)
⚠ Compiler errors and warnings arising from your code are now printed to stdout rather than stderr, and progress messages such as "Compiling Data.Array" are now printed to stderr rather than stdout. Warnings and errors arising from incorrect use of the CLI, such as specifying input files which don't exist or specifying globs which don't match any files, are still printed to stderr (as they were before). This change is useful when using the
--json-errors
flag, since you can now pipe JSON errors into other programs without having to perform awkward gymnastics such as2>&1
.🛠 Fixes
Only include direct dependencies in the output for
purs graph
instead of their transitive closure (#3993, @colinwahl)🛠 Fix the reversal of the qualifier of qualified operators (#3971, @rhendric)
Qualified operators, for instance
Data.Array.(!)
, were interpreted with a reversed qualifier, likeArray.Data.(!)
.- Check all recursive paths in data binding groups (#3936, @natefaubion)
The compiler was not catching recursive type synonyms when some recursive paths were guarded by data types or newtypes.
- Desugar type operator aliases inside parens (#3935, @natefaubion)
The compiler did not accept type operators inside parens in prefix position, except
(->)
.- 📌 Pin language-javascript to a specific version (#3904, @hdgarrood)
👍 Allowing the compiler to be built against various versions of
language-javascript
meant that multiple builds of the same version of the compiler could accept different syntaxes for JavaScript foreign modules, depending on how they were built.👌 Improvements
- 👌 Improves protocol errors from the IDE server (#3998, @kritzcreek)
📜 The IDE server now respond with more descriptive error messages when failing to parse a command. This should make it easier to contribute fixes to the various clients.
- Extend IDE ImportCompletion with declarationType (#3997, @i-am-the-slime)
👻 By exposing the declaration type (value, type, typeclass, etc.) downstream tooling can annotate imports with this info so users know what they are about to import. The info can also be mapped to a namespace filter to allow importing identifiers that appear more than once in a source file which throws an exception without such a filter.
- 👌 Improve error message when
negate
isn't imported (#3952, @mhmdanas)
This shows a specific message when using negative literals but
Data.Ring.negate
is out of scope, similar to the messages shown when using do notation ifControl.Bind.bind
andControl.Bind.discard
are out of scope.- ➕ Add source spans to
PartiallyAppliedSynonym
errors (#3951, @rhendric)
PartiallyAppliedSynonym
errors were usually rethrown with the appropriate source span, but not when deriving instances. This annotates those errors with the source span of the partially applied synonyms themselves, which is more robust and accurate than rethrowing the error with an approximate source span.- 👍 Allow type synonyms in instances heads and superclass constraints (#3539, #3966, #3965, @garyb, @kl0tl)
This allows declarations such as
type Env = { port :: Int } newtype App a = App (ReaderT Env Aff a) derive newtype instance monadAskApp :: MonadAsk Env App
or
class (Monad m, MonadAsk Env m) <= MonadAskEnv m
- 👌 Improve incremental rebuild times for modules with large dependencies (#3899, @milesfrain)
Other
- Warn against exported types with hidden constructors but
Generic
orNewtype
instances (#3907, @kl0tl)
Types with hidden constructors are supposed to be opaque outside of their module of definition but
Generic
andNewtype
instances allow to construct them withData.Generic.Rep.to
orData.Newtype.wrap
and examine their content withData.Generic.Rep.from
orData.Newtype.unwrap
, thus making void any invariant those types may witness.- Have module re-exports appear in generated code (#3883, @citizengabe)
👀 This is the first step towards smarter incremental rebuilds, which could skip rebuilding downstream modules when the interface of a module did not change (see #3724).
➕ Add a printer for CST modules (#3887, @kritzcreek)
🗄 Deprecate constraints in foreign imports (#3829, @kl0tl)
⚡️ Constrained foreign imports leak instance dictionaries, hindering the compiler ability to optimize their representation. Manipulating dictionaries in foreign code should be avoided and foreign imports should accept the class members they need as additional arguments instead of being constrained.
- 🗄 Deprecate primes (the
'
character) in identifiers exported from foreign modules (#3792, @kl0tl)
🚀 We are going to output ES modules instead of CommonJS in the next breaking release but named exports of ES modules, unlike CommonJS exports, have to be valid JavaScript identifiers and so cannot contain primes.
📄 Docs
🚀 Generate a changelog from the GitHub releases and add a pull request template (#3989, @JordanMartinez)
Detail license related error messages and fix incorrect SPDX sample licenses (#3970, @fsoikin)
✂ Remove a spurious doc comment on the CoreFn Module type (#3552, @jmackie)
➕ Add a link to the releases page (#3920, @milesfrain)
⚡️ Update CONTRIBUTING.md (#3924, @hdgarrood)
➕ Add troubleshooting steps for libtinfo and EACCES errors (#3903, @milesfrain)
⚡️ Update an outdated link to the book (#3916, @sumew)
Internal
Simplify the
Ord
instances of some AST types (#3902, @milesfrain)⚡️ Update the desugaring pipeline to work on individual modules (#3944, @kl0tl)
✂ Remove the unmaintained and ignored core libraries tests (#3861, @kl0tl)
👕 Configure Travis to run
hlint
(#3816, #3864, @joneshf, @hdgarrood)✂ Remove support for the legacy Bower resolutions format in
purs publish
(#3847, @kl0tl)➕ Add GitHub issue templates for bugs and proposals (#3853, @joneshf)
➕ Add support for Happy >=1.19.10 (#3837, @arrowd)
📦 Use the same default extensions in all packages (#3823, #3908, @natefaubion, @i-am-the-slime)
😌 Relax
purescript-ast
dependency onmicrolens-platform
tomicrolens
(#3817, @joneshf)📦 Extract the AST and CST types, and related functions, into their own
purescript-ast
andpurescript-cst
packages for ease of consumption by external tooling (#3793, #3821, #3826, @joneshf, @natefaubion)🛠 Fix various typos in documentation, comments and bindings names (#3795, @mhmdanas)
➕ Add golden tests for errors and warnings (#3774, #3811, #3808, #3846, @dariooddenino, @rhendric, @kl0tl)
More descriptive protocol errors from the ide server (@kritzcreek)