wiki:Backpack

Backpack

This is the launch page for Backpack, actively maintained by Edward (as of Nov 2017).

Backpack is a system for retrofitting Haskell with an applicative, mix-in module system. It has been implemented in GHC 8.2 and cabal-install 2.0, but it is not supported by Stack.

The documentation for how to use Backpack is a bit scattered about at this point, but here are useful, up-to-date (as of 2017-04-02, prior to GHC 8.2's release) references:

  • This pair of blog posts: Try Backpack, ghc --backpack and Cabal packages have up-to-date tutorials for using the main features of Backpack, with and without Cabal.
  • There is not yet a manual entry in Cabal for how Cabal works. This section is under development.
  • Edward Z. Yang's thesis contains detailed information about the specification and implementation of Backpack. We also have an older paper draft which was submitted to ICFP'16. History nuts can also read the original POPL paper but note that Backpack has changed dramatically since then.
  • Hackage does not yet support uploads of Backpack-using packages. next.hackage is a Hackage instances running a development branch of Hackage that can handle Backpack; for now, Backpack-enabled packages should be uploaded here.

You might find it useful to find some code using Backpack. Here are the biggest examples worth looking at:

  • backpack-str defines a signature and implementations for strings. It is quite comprehensive, and the packages are available on next.hackage.
  • coda parametrizes over a few core data types including notions of "delta". It takes advantage of zero-cost abstraction, which lets it split into multiple data types, while still ensuring they are UNPACKed in the end.
  • streamy defines a signature and implementations for "streaming" libraries (e.g., conduit, pipes and streaming).
  • haskell-opentracing defines a signature for the OpenTracing standard, a middleware built on top of this signature, and (at the moment) a single backend to Jaeger.
  • slay is a layouting engine parametrized over Double and Int coordinate types.
  • reflex-backpack is a kind of crazy experiment at Backpack'ing Reflex. Reflex uses a lot of advanced GHC features and it took some coaxing to get Backpack to handle it all, but handle it all it did!

Some more out-of-date documents:

  • Backpack specification. This was subsumed by my thesis but once Backpack stabilizes it will be worth distilling the thesis PDF back into a more web-friendly format.

Known gotchas

Can I use this with Stack? No, Backpack requires support from the package manager, and Stack integration has not been implemented yet.

Can I use this with Template Haskell? Yes, but you need GHC 8.2.2; GHC 8.2.1 has a critical bug which means that most real-world uses of TH will not work. See this issue and this issue for two examples of this occurring in the wild.

Can I use this with the C preprocessor? No, this is buggy (you'll get an error <command line>: unknown package: hole). See #14525 for the issue and a patch.

Make sure cabal-version is recent enough. (#4448) If you set the cabal-version of your package too low, you may get this error:

Error:
    Mix-in refers to non-existent package 'pkg'
    (did you forget to add the package to build-depends?)
    In the stanza 'executable myexe'
    In the inplace package 'pkg'

This is because internal libraries are feature-gated by the cabal-version of your package. Setting it to cabal-version: >= 2.0 is enough to resolve the problem.

You can't instantiate a dependency with a locally defined module. Consider the following package:

library
  other-modules: StrImpl
  build-depends: foo-indef
  mixins: foo-indef requires (Str as StrImpl)

This looks like it should work, but actually it will fail:

Error:
    Non-library component has unfilled requirements: StrImpl
    In the stanza 'library'
    In the inplace package 'mypkg-1.2'

The reason for this is Backpack does not (currently) support instantiating a package with a locally defined module: since the module can turn around and *import* the mixed in foo-indef, which would result in mutual recursion (not presently supported.)

To solve this problem, just create a new library to define the implementing module. This library can be in the same package using the convenience library mechanism:

library str-impl
  exposed-modules: StrImpl

library
  build-depends: str-impl, foo-indef
  mixins: foo-indef requires (Str as StrImpl)

How can I declare that a module implements a signature? In traditional Haskell style, you can write x :: Type to specify that a value x should have some type. In Backpack, specifying that a module implements a signature is done out-of-line; you must create a third component to link them together (e.g., a test suite):

test-suite implements
  type:                exitcode-stdio-1.0
  main-is:             Main.hs
  build-depends:
    base,
    foo-implementation,
    foo-sig
  default-language:    Haskell2010

A few notes about this encoding:

  • If you need to specify that a module implements multiple signatures, you include all of those signatures in the same implements test or create separate implements tests.
  • Being a test suite, this requires you to create a dummy Main.hs file (main = return () is sufficient) and add a base dependency. So why do we pick a test suite? A test suite will ensure that you have in fact filled all of the holes of a foo-sig, whereas a regular library will happily pass through any unfilled holes, making it easy for you to think that a check has occurred when it has not.
  • You might wonder if you can skip defining an extra test-suite by mixing in the signature package from the implementation package. Unfortunately, this runs afoul the "you can't instantiate a dependency with a local module" restriction. Additionally, this adds an extra spurious dependency to your package which is not actually needed.

Backpack-related tickets

Backpack-related tickets are marked with keyword 'backpack'. If the ticket is assigned to ezyang, it means he's planning on working on it.

Ticket Type Summary Priority Owner
#1409 feature request Allow recursively dependent modules transparently (without .hs-boot or anything) normal
#9351 feature request add ability to version symbols .c for packages with C code normal
#10749 bug Boot file instances should imply superclasses normal ezyang
#10827 feature request GHCi should support interpeting multiple packages/units with separate DynFlags normal
#12703 feature request Expand Backpack's signature matching relation beyond definitional equality normal
#13151 task Make all never-exported IfaceDecls implicit normal ezyang
#13266 bug Source locations from signature merging/matching are bad normal
#13469 feature request -fdefer-type-errors for Backpack normal
#14212 bug Give better error message with non-supported Backpack/TH use normal
#14478 feature request Abstract pattern synonyms (for hsig and hs-boot) normal
#10266 task Split base for Backpack low ezyang
#10681 feature request Teach GHC to interpret all hs files as two levels of hs-boot files (abstract types only/full types + values) low ezyang
#12680 feature request Permit type equality instances in signatures low
#13149 task Giving Backpack a Promotion low
#13262 feature request Allow type synonym family application in instance head if it has no free variables low
#13361 bug Better type synonym merging/subtyping for Backpack low
#13765 bug GHC cannot parse valid Haskell98 whose first identifier is named signature low
#14210 feature request bkp files cannot find TemplateHaskell symbols (even without Backpack features) low
#10871 feature request Implement "fat" interface files which can be directly compiled without source lowest ezyang
#12717 feature request Permit data types in signatures to be implemented with equivalent pattern synonyms (and vice versa) lowest

Last modified 3 weeks ago Last modified on Nov 25, 2017 5:16:36 AM