purescript v0.15.0 Release Notes

  • ๐Ÿ’ฅ Breaking changes:

    • Switch from Common JS to ES modules (#4232 by @sigma-andex)

    Previously, Purescript used Common JS for FFI declarations.

    Before, FFI was declared like this...

      const mymodule = require('mymodule')
    
      exports.myvar = mymodule.myvar
    

    ...and will be changed to this...

      import * as M from 'mymodule';
      export const myvar = M.myvar
    

    ...or using the short version...

      export { myvar } from 'mymodule';
    
    • FFI is annotated with /* #__PURE__ */ so that bundlers can perform DCE
    • The current LTS Node.js version 12 is now the required minimum version

    • ๐Ÿ‘Œ Improve apartness checking (#4149 by @rhendric)

    See details in https://github.com/purescript/documentation/blob/master/language/Type-Classes.md#instance-chains

    • Disable type class constraints in FFI (#4240 by @JordanMartinez)

    Previously, one could write FFI like the following:

      foreign import foo :: forall a. Show a => a -> String
    

    Type class dictionaries are "magically" handled by the compiler. By including them in the above FFI, one can depend on their representation. Since the representation can change without notice, this may silently break code.

    In v0.14.x, a warning was emitted if these were used. Now it will fail to compile. Rather, one should write something like the following where the members of the type class are passed explicitly to the FFI function as arguments:

      foo :: forall a. Show a => a -> String
      foo val = fooImpl show val
    
      foreign import fooImpl :: forall a. (a -> String) -> a -> String
    
    • โœ‚ Removes deprecated syntax for rows (i.e. #) and kinds (i.e. kind-keyword) (#4239 by @JordanMartinez)

    • Apply precedence rules to operator sections (#4033 by @rhendric)

    Previously, (_ * 4 + 1) would desugar to \x -> x * (4 + 1), even though * has higher precedence than +. Conversely, (3 * 2 + _) would not compile, even though * has higher precedence than +. These bugs have now been fixed; (_ * 4 + 1) is an error, and (3 * 2 + _) desugars to \x -> 3 * 2 + x.

    If you have code that relied on the old behavior, add an extra pair of parentheses around the expression in the section.

    • ๐Ÿ“œ If FFI parsing succeeds & CommonJS is detected, fail; otherwise, do not error or warn (#4250 by @sigma-andex)

    Previously, the compiler would emit an error if it failed to parse the FFI JavaScript file. Since the underlying JavaScript parser (i.e. language-javascript) fails to parse even valid JavaScript files, we cannot consider every failed parse to mean invalid JS files. Fixing the parser would require a lot of effort, so we are planning to remove it instead in v0.16.x.

    If the parse succeeds and a CommonJS module is detected, a compiler error is now emitted. If the parse fails, we no longer emit a compiler error. While we could emit a warning, such a warning will quickly become annoying for FFI files that trigger the buggy paths of language-javascript. Moreover, we presume that all will be migrating their code to ES modules now that CommonJS is being deprecated in the larger JavaScript ecosystem.

    • Warn on ad-hoc non-single-line case expression syntax (#4241 by @JordanMartinez)

    The following code will now produce a compiler warning. These were originally supported to ease the migration to the new CST parser.

      -- before: `arg` isn't indented "past" the `Foo arg` binder
      case foo of Foo arg ->
        arg
      -- after
      case foo of Foo arg ->
                    foo
    

    Dropping the above syntax make case expressions more similar to how let bindings work:

      let ok = 1
      let
        ok = 1
      let ok =
            1
      let notOk =
        1
    
    • โฌ‡๏ธ Drop support for browser backend for repl (i.e. purs repl --port 1234) (#4255 by @JordanMartinez)

    Running this command will print a link that directs users to use Try PureScript instead.

    • โœ‚ Remove purs bundle (#4255 by @JordanMartinez)

    Users of purs bundle should switch to a standalone bundler such as esbuild, webpack or parcel.

    • Lazy initialization for recursive bindings (#4283 by @rhendric)

    This is unlikely to break a working program, but the upshot for users is that it's now possible to get a run-time error when dereferencing an identifier in a recursive binding group before it has been initialized, instead of silently getting an undefined value and having that maybe or maybe not lead to an error somewhere else.

    This change can cause code that relies on tail-call optimization to no longer compile with that optimization. If you find that code that previously compiled to a TCO loop no longer does but does include $lazy initializers, please report the issue.

    Alternate backend maintainers: for you, this change represents a clarification of a responsibility shared by all backends. The identifiers bound in a recursive binding group need to behave as if those identifiers have call-by-need semantics during the initialization of the entire binding group. (Initializing the binding group entails ensuring every initializer has been executed, so after the binding group is initialized, these identifiers can be considered call-by-value again.)

    If an identifier is needed during its own call-by-need initialization, the backend must ensure that an explicit run-time error is raised appropriate for your target platform. This error may be raised at compile time instead if the backend can determine that such a cycle is inevitable. Returning your target language's equivalent of JavaScript's undefined, as purs did in earlier releases in some cases, is not permitted.

    If your target language natively has call-by-need semantics, you probably don't have to do anything. If your target language is call-by-value and you are using PureScript as a library, you can use the function Language.PureScript.CoreFn.Laziness.applyLazinessTransform to your CoreFn input to satisfy this responsibility; if you do, you will need to do the following:

    * Translate `InternalIdent RuntimeLazyFactory` and `InternalIdent (Lazy _)`
      identifiers to appropriate strings for your backend
    * Ensure that any output file that needs it has a reference to a function
      named `InternalIdent RuntimeLazyFactory`, with type `forall a. Fn3 String
      String (Unit -> a) (Int -> a)`, and with the same semantics as the
      following JavaScript (though you should customize the error raised to be
      appropriate for your target language):
    
      ```js
      function (name, moduleName, init) {
          var state = 0;
          var val;
          return function (lineNumber) {
              if (state === 2) return val;
              if (state === 1) throw new ReferenceError(name + " was needed before it finished initializing (module " + moduleName + ", line " + lineNumber + ")", moduleName, lineNumber);
              state = 1;
              val = init();
              state = 2;
              return val;
          };
      };
      ```
    

    If neither of the previous cases apply to you, you can meet this responsibility most easily simply by ensuring that all recursive bindings are lazy. You may instead choose to implement some light analysis to skip generating lazy bindings in some cases, such as if every initializer in the binding group is an Abs. You also may choose to reimplement applyLazinessTransform, or even develop a more sophisticated laziness transform for your backend. It is of course your responsibility to ensure that the result of whatever analysis you do is equivalent to the expected semantics.

    ๐Ÿ†• New features:

    • Implement the Reflectable type class (#4207 by @PureFunctor)

    The Reflectable type class is a common interface for reflecting type-level values down to the term-level. Its instances are automatically solved by the compiler, and it allows Symbol, Int, Boolean, and Ordering kinded types to be reflected to their term-level representations.

    • Implement native type-level integers (#4207 and #4267 by @PureFunctor and @JordanMartinez)

    Added support for type-level integers and compiler-solved operations such as Add, Mul, Compare, and ToString. Type-level integers use the Int type as their kind.

    • ๐Ÿ–จ Print compilation progress on the command line (#4258 by @PureFunctor)

    This feature makes it so purs compile and purs docs now show compilation progress on the command line. Example output:

      [ 1 of 59] Compiling Type.Proxy
      [ 2 of 59] Compiling Type.Data.RowList
      ...
      [58 of 59] Compiling Effect.Class.Console
      [59 of 59] Compiling Test.Main
    
    • โช Restore names of quantified variables during generalization (#4257 by @PureFunctor)

    This makes the compiler aware of the names of quantified variables instantiated into unification variables, such that when the latter is generalized, semantic information is restored. For example:

      addNumberSuffix :: forall a b c d. a -> b -> c -> d -> a
      addNumberSuffix a _ _ _ = a
    
      addNumberSuffix' = addNumberSuffix 0
    

    Previously, inferring top-level declarations without type signatures would use t suffixed with an integer for type variables.

      forall t6 t7 t8. t6 -> t7 -> t8 -> Int
    

    Now, the inferred type would refer back to their original names.

      forall b6 c7 d8. b6 -> c7 -> d8 -> Int
    
    • ๐Ÿ‘Œ Support Glibc versions >= 2.24 (#4228 by @sd-yip)

    Previously, purs required a Glibc version greater than or equal to 2.27. This requirement is relaxed to support a Glibc version down to 2.24.

    ๐Ÿ›  Bugfixes:

    • ๐Ÿ›  Fix warning suppression for wildcard types (#4269 by @rhendric)

    This bug was triggered by defining recursive partial functions or recursive bindings that contained wildcards in inner type annotations. Recursive partial function declarations now no longer cause spurious wildcard warnings to be emitted, and actual user-written wildcards now accurately emit warnings if and only if they don't appear within a binding (recursive or otherwise) with a complete (wildcard-free) type signature.

    • โœ‚ Remove compiler-generated identifiers from type search results (#4260 by @PureFunctor)

    Other improvements:

    • ๐Ÿ‘Œ Improve "Unknown value bind" and "Unknown value discard" errors (#4272 by @mhmdanas)

    The previous error implies that do-notation compiles down to only bind or to only discard (depending on whether the symbol not found was bind or discard respectively), which is somewhat misleading, especially in the latter case. Now, the error states correctly that do-notation compiles down to both functions.

    Internal:

    • Document the HSPEC_ACCEPT flag for generating golden files (#4243 by @JordanMartinez)

    • โœ… Fail test if PureScript file(s) don't have a Main module (#4243 by @JordanMartinez)

    • โšก๏ธ Update CI to use windows-2019 since windows-2016 is deprecated (#4248 by @JordanMartinez)

    • ๐Ÿšš Move lib/purescript-cst into src/ (#4290 by @JordanMartinez)

    • โšก๏ธ Update tests and their bower deps to 0.15.0-compatible versions (#4300 by @JordanMartinez)