{ lib }:

with lib;
{
  recursiveMergeAttrs = a: b: let
    commonAttrs = intersectLists (attrNames a) (attrNames b);
    aAttrs = subtractLists (attrNames a) commonAttrs;
    bAttrs = subtractLists (attrNames b) commonAttrs;
    aSide = (filterAttrs (k: v: elem k aAttrs) a);
    bSide = (filterAttrs (k: v: elem k bAttrs) b);
    common = (foldr (a: b: a // b) {}
       (map (k: { ${k} = a.${k} // b.${k}; }) commonAttrs));
  in aSide // bSide // common;
}