anchor.diff:      #
       #     bash$ nix-shell --run "cabal new-build all"
       #     bash$ nix-shell --run "python"
  -    shellFor = { packages, withHoogle ? false, ... } @ args:
  +    shellFor =
  +      { # Packages to create this development shell for.  These are usually
  +        # your local packages.
  +        packages
  +      , # Whether or not to generated a Hoogle database for all the
  +        # dependencies.
  +        withHoogle ? false
  +      , ...
  +      } @ args:
         let
  -        combinedPackageFor = packages:
  -          let
  -            selected = packages self;
  -
  -            pname = if pkgs.lib.length selected == 1
  -              then (pkgs.lib.head selected).name
  -              else "packages";
  -
  -            # If `packages = [ a b ]` and `a` depends on `b`, don't build `b`,
  -            # because cabal will end up ignoring that built version, assuming
  -            # new-style commands.
  -            combinedPackages = pkgs.lib.filter
  -              (input: pkgs.lib.all (p: input.outPath or null != p.outPath) selected);
  -
  -            # Returns an attrset containing a combined list packages' inputs for each
  -            # stage of the build process
  -            packageInputs = pkgs.lib.zipAttrsWith
  -              (_: pkgs.lib.concatMap combinedPackages)
  -              (map (p: p.getCabalDeps) selected);
  -
  -            genericBuilderArgs = {
  -              inherit pname;
  -              version = "0";
  -              license = null;
  -            } // packageInputs;
  -
  -          in self.mkDerivation genericBuilderArgs;
  +        # A list of the packages we want to build a development shell for.
  +        #
  +        # This is a list of Haskell package derivations.
  +        selected = packages self;
  +
  +        # This is a list of attribute sets, where the each attribute set
  +        # corresponds to the build inputs one of the packages input to shellFor.
  +        #
  +        # Each attribute has keys like buildDepends, executableHaskellDepends,
  +        # testPkgconfigDepends, etc.  The values for the keys of the attribute
  +        # set are lists of dependencies.
  +        #
  +        # Example:
  +        #   cabalDepsForSelected
  +        #   => [
  +        #        # This may be the attribute set corresponding to the `backend`
  +        #        # package in the example above.
  +        #        { buildDepends = [ gcc ... ];
  +        #          libraryHaskellDepends = [ lens conduit ... ];
  +        #          ...
  +        #        }
  +        #        # This may be the attribute set corresponding to the `common`
  +        #        # package in the example above.
  +        #        { testHaskellDepends = [ tasty hspec ... ];
  +        #          libraryHaskellDepends = [ lens aeson ];
  +        #          benchmarkHaskellDepends = [ criterion ... ];
  +        #          ...
  +        #        }
  +        #        ...
  +        #      ]
  +        cabalDepsForSelected = map (p: p.getCabalDeps) selected;
  +
  +        # A predicate that takes a derivation as input, and tests whether it is
  +        # the same as any of the `selected` packages.
  +        #
  +        # Returns true if the input derivation is not in the list of `selected`
  +        # packages.
  +        #
  +        # isNotSelected :: Derivation -> Bool
  +        #
  +        # Example:
  +        #
  +        #   isNotSelected common [ frontend backend common ]
  +        #   => false
  +        #
  +        #   isNotSelected lens [ frontend backend common ]
  +        #   => true
  +        isNotSelected = input: pkgs.lib.all (p: input.outPath or null != p.outPath) selected;
  +
  +        # A function that takes a list of list of derivations, filters out all
  +        # the `selected` packages from each list, and concats the results.
  +        #
  +        #   zipperCombinedPkgs :: [[Derivation]] -> [Derivation]
  +        #
  +        # Example:
  +        #   zipperCombinedPkgs [ [ lens conduit ] [ aeson frontend ] ]
  +        #   => [ lens conduit aeson ]
  +        #
  +        # Note: The reason this isn't just the function `pkgs.lib.concat` is
  +        # that we need to be careful to remove dependencies that are in the
  +        # `selected` packages.
  +        #
  +        # For instance, in the above example, if `common` is a dependency of
  +        # `backend`, then zipperCombinedPkgs needs to be careful to filter out
  +        # `common`, because cabal will end up ignoring that built version,
  +        # assuming new-style commands.
  +        zipperCombinedPkgs = vals:
  +          pkgs.lib.concatMap
  +            (drvList: pkgs.lib.filter isNotSelected drvList)
  +            vals;
  +
  +        # Zip `cabalDepsForSelected` into a single attribute list, combining
  +        # the derivations in all the individual attributes.
  +        #
  +        # Example:
  +        #   packageInputs
  +        #   => # Assuming the value of cabalDepsForSelected is the same as
  +        #      # the example in cabalDepsForSelected:
  +        #      { buildDepends = [ gcc ... ];
  +        #        libraryHaskellDepends = [ lens conduit aeson ... ];
  +        #        testHaskellDepends = [ tasty hspec ... ];
  +        #        benchmarkHaskellDepends = [ criterion ... ];
  +        #        ...
  +        #      }
  +        #
  +        # See the Note in `zipperCombinedPkgs` for what gets filtered out from
  +        # each of these dependency lists.
  +        packageInputs =
  +          pkgs.lib.zipAttrsWith (_name: zipperCombinedPkgs) cabalDepsForSelected;
  +
  +        # A attribute set to pass to `haskellPackages.mkDerivation`.
  +        #
  +        # The important thing to note here is that all the fields from
  +        # packageInputs are set correctly.
  +        genericBuilderArgs = {
  +          pname =
  +            if pkgs.lib.length selected == 1
  +            then (pkgs.lib.head selected).name
  +            else "packages";
  +          version = "0";
  +          license = null;
  +        }
  +        // packageInputs;
  +
  +        # This is a pseudo Haskell package derivation that contains all the
  +        # dependencies for the packages in `selected`.
  +        #
  +        # This is a derivation created with `haskellPackages.mkDerivation`.
  +        #
  +        # pkgWithCombinedDeps :: HaskellDerivation
  +        pkgWithCombinedDeps = self.mkDerivation genericBuilderArgs;
  +
  +        # The derivation returned from `envFunc` for `pkgWithCombinedDeps`.
  +        #
  +        # This is a derivation that can be run with `nix-shell`.  It provides a
  +        # GHC with a package database with all the dependencies of our
  +        # `selected` packages.
  +        #
  +        # This is a derivation created with `stdenv.mkDerivation` (not
  +        # `haskellPackages.mkDerivation`).
  +        #
  +        # pkgWithCombinedDepsDevDrv :: Derivation
  +        pkgWithCombinedDepsDevDrv = pkgWithCombinedDeps.envFunc { inherit withHoogle; };
   
           mkDerivationArgs = builtins.removeAttrs args [ "packages" "withHoogle" ];
  -      in ((combinedPackageFor packages).envFunc { inherit withHoogle; }).overrideAttrs (old: mkDerivationArgs // {
  +
  +      in pkgWithCombinedDepsDevDrv.overrideAttrs (old: mkDerivationArgs // {
           nativeBuildInputs = old.nativeBuildInputs ++ mkDerivationArgs.nativeBuildInputs or [];
           buildInputs = old.buildInputs ++ mkDerivationArgs.buildInputs or [];
         });
  
positive.code:     #     (import ./.).shellFor {
      #       packages = p: [p.frontend p.backend p.common];
      #       withHoogle = true;
      #       buildInputs = [ pkgs.python ];
      #     }
      #
      #     -- cabal.project
  
negative.code:     # packages themselves. Using nix-shell on this derivation will
      # give you an environment suitable for developing the listed
      # packages with an incremental tool like cabal-install.
      #
      # In addition to the "packages" arg and "withHoogle" arg, anything that
      # can be passed into stdenv.mkDerivation can be included in the input attrset
      #
  