From 973636bcfe10c6f9539bb4d73014279212ad5e0d Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Thu, 15 Aug 2024 15:37:01 -0700 Subject: [PATCH 01/11] Use separate PPE for each of LCA/Alice/Bob when computing synhashes. --- unison-merge/src/Unison/Merge/Diff.hs | 37 +++++---------------- unison-merge/src/Unison/Merge/Mergeblob1.hs | 24 ++++++++++++- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/unison-merge/src/Unison/Merge/Diff.hs b/unison-merge/src/Unison/Merge/Diff.hs index 39be392c28..6e5786ea34 100644 --- a/unison-merge/src/Unison/Merge/Diff.hs +++ b/unison-merge/src/Unison/Merge/Diff.hs @@ -5,7 +5,6 @@ where import Data.Map.Strict qualified as Map import Data.Semialign (alignWith) -import Data.Set qualified as Set import Data.These (These (..)) import U.Codebase.Reference (TypeReference) import Unison.ConstructorReference (GConstructorReference (..)) @@ -14,20 +13,18 @@ import Unison.DataDeclaration qualified as DataDeclaration import Unison.DeclNameLookup (DeclNameLookup) import Unison.DeclNameLookup qualified as DeclNameLookup import Unison.Hash (Hash (Hash)) -import Unison.HashQualifiedPrime qualified as HQ' import Unison.Merge.DiffOp (DiffOp (..)) import Unison.Merge.PartialDeclNameLookup (PartialDeclNameLookup (..)) import Unison.Merge.Synhash qualified as Synhash import Unison.Merge.Synhashed (Synhashed (..)) import Unison.Merge.ThreeWay (ThreeWay (..)) -import Unison.Merge.ThreeWay qualified as ThreeWay import Unison.Merge.TwoWay (TwoWay (..)) import Unison.Merge.Updated (Updated (..)) import Unison.Name (Name) import Unison.Parser.Ann (Ann) import Unison.Prelude hiding (catMaybes) import Unison.PrettyPrintEnv (PrettyPrintEnv (..)) -import Unison.PrettyPrintEnv qualified as Ppe +import Unison.PrettyPrintEnvDecl qualified as PPED import Unison.Reference (Reference' (..), TermReference, TermReferenceId, TypeReferenceId) import Unison.Referent (Referent) import Unison.Referent qualified as Referent @@ -50,21 +47,16 @@ nameBasedNamespaceDiff :: (HasCallStack) => TwoWay DeclNameLookup -> PartialDeclNameLookup -> + ThreeWay PPED.PrettyPrintEnvDecl -> ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) -> Defns (Map TermReferenceId (Term Symbol Ann)) (Map TypeReferenceId (Decl Symbol Ann)) -> TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference) -nameBasedNamespaceDiff declNameLookups lcaDeclNameLookup defns hydratedDefns = - let lcaHashes = synhashLcaDefns ppe lcaDeclNameLookup defns.lca hydratedDefns - hashes = synhashDefns ppe hydratedDefns <$> declNameLookups <*> ThreeWay.forgetLca defns - in diffHashedNamespaceDefns lcaHashes <$> hashes - where - ppe :: PrettyPrintEnv - ppe = - -- The order between Alice and Bob isn't important here for syntactic hashing; not sure right now if it matters - -- that the LCA is added last - deepNamespaceDefinitionsToPpe defns.alice - `Ppe.addFallback` deepNamespaceDefinitionsToPpe defns.bob - `Ppe.addFallback` deepNamespaceDefinitionsToPpe defns.lca +nameBasedNamespaceDiff declNameLookups lcaDeclNameLookup ppeds defns hydratedDefns = + let ThreeWay {lca = lcaPPE, alice = alicePPE, bob = bobPPE} = PPED.unsuffixifiedPPE <$> ppeds + lcaHashes = synhashLcaDefns lcaPPE lcaDeclNameLookup defns.lca hydratedDefns + aliceHashes = synhashDefns alicePPE hydratedDefns declNameLookups.alice defns.alice + bobHashes = synhashDefns bobPPE hydratedDefns declNameLookups.bob defns.bob + in diffHashedNamespaceDefns lcaHashes <$> TwoWay {alice = aliceHashes, bob = bobHashes} diffHashedNamespaceDefns :: DefnsF2 (Map Name) Synhashed term typ -> @@ -183,19 +175,6 @@ synhashDefnsWith hashTerm hashType = do hashType1 name typ = Synhashed (hashType name typ) typ ------------------------------------------------------------------------------------------------------------------------- --- Pretty-print env helpers - -deepNamespaceDefinitionsToPpe :: Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name) -> PrettyPrintEnv -deepNamespaceDefinitionsToPpe Defns {terms, types} = - PrettyPrintEnv (arbitraryName terms) (arbitraryName types) - where - arbitraryName :: (Ord ref) => BiMultimap ref Name -> ref -> [(HQ'.HashQualified Name, HQ'.HashQualified Name)] - arbitraryName names ref = - BiMultimap.lookupDom ref names - & Set.lookupMin - & maybe [] \name -> [(HQ'.NameOnly name, HQ'.NameOnly name)] - ------------------------------------------------------------------------------------------------------------------------ -- Looking up terms and decls that we expect to be there diff --git a/unison-merge/src/Unison/Merge/Mergeblob1.hs b/unison-merge/src/Unison/Merge/Mergeblob1.hs index 83cfd58b16..c0daa655cb 100644 --- a/unison-merge/src/Unison/Merge/Mergeblob1.hs +++ b/unison-merge/src/Unison/Merge/Mergeblob1.hs @@ -1,14 +1,17 @@ module Unison.Merge.Mergeblob1 ( Mergeblob1 (..), + hydratedDefnDependencies, makeMergeblob1, ) where +import Control.Lens import Data.List qualified as List import Data.Map.Strict qualified as Map import Unison.DataDeclaration (Decl) import Unison.DataDeclaration qualified as DataDeclaration import Unison.DeclNameLookup (DeclNameLookup) +import Unison.LabeledDependency qualified as LD import Unison.Merge.CombineDiffs (CombinedDiffOp, combineDiffs) import Unison.Merge.DeclCoherencyCheck (IncoherentDeclReason, checkDeclCoherency, lenientCheckDeclCoherency) import Unison.Merge.Diff (nameBasedNamespaceDiff) @@ -27,11 +30,14 @@ import Unison.Name (Name) import Unison.NameSegment (NameSegment) import Unison.Parser.Ann (Ann) import Unison.Prelude +import Unison.PrettyPrintEnvDecl qualified as PPED import Unison.Reference (TermReference, TermReferenceId, TypeReference, TypeReferenceId) import Unison.Referent (Referent) import Unison.Symbol (Symbol) import Unison.Term (Term) +import Unison.Term qualified as Term import Unison.Type (Type) +import Unison.Type qualified as Type import Unison.Util.BiMultimap (BiMultimap) import Unison.Util.Defns (Defns (..), DefnsF, DefnsF2, DefnsF3) @@ -54,10 +60,25 @@ data Mergeblob1 libdep = Mergeblob1 unconflicts :: DefnsF Unconflicts Referent TypeReference } +hydratedDefnDependencies :: + ThreeWay + ( DefnsF + (Map Name) + (TermReferenceId, (Term Symbol Ann, Type Symbol Ann)) + (TypeReferenceId, Decl Symbol Ann) + ) -> + ThreeWay (Set LD.LabeledDependency) +hydratedDefnDependencies hydratedDefns = + hydratedDefns + <&> \Defns {terms, types} -> + (terms & foldOf (folded . _2 . beside (to Term.labeledDependencies) (to Type.labeledDependencies))) + <> (types & foldOf (folded . _2 . to DataDeclaration.labeledDeclTypeDependencies)) + makeMergeblob1 :: forall libdep. (Eq libdep) => Mergeblob0 libdep -> + ThreeWay PPED.PrettyPrintEnvDecl {- Pretty print env containing names for everything in 'hydratedDefnDependencies' -} -> ThreeWay ( DefnsF (Map Name) @@ -65,7 +86,7 @@ makeMergeblob1 :: (TypeReferenceId, Decl Symbol Ann) ) -> Either (EitherWay IncoherentDeclReason) (Mergeblob1 libdep) -makeMergeblob1 blob hydratedDefns = do +makeMergeblob1 blob ppeds hydratedDefns = do -- Make one big constructor count lookup for all type decls let numConstructors = Map.empty @@ -97,6 +118,7 @@ makeMergeblob1 blob hydratedDefns = do nameBasedNamespaceDiff declNameLookups lcaDeclNameLookup + ppeds blob.defns Defns { terms = From 9df178db255a845f6a6663ec089537761e751bb4 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Thu, 15 Aug 2024 16:02:01 -0700 Subject: [PATCH 02/11] Build and pass in the appropriate merge PPED for alice/bob/lca --- .../Unison/Codebase/Editor/HandleInput/Merge2.hs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs index d8166ae03a..13af6839fc 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs @@ -38,6 +38,7 @@ import Unison.Cli.MergeTypes (MergeSource (..), MergeSourceAndTarget (..), Merge import Unison.Cli.Monad (Cli) import Unison.Cli.Monad qualified as Cli import Unison.Cli.MonadUtils qualified as Cli +import Unison.Cli.PrettyPrintUtils qualified as Cli import Unison.Cli.ProjectUtils qualified as ProjectUtils import Unison.Cli.UpdateUtils ( getNamespaceDependentsOf3, @@ -72,8 +73,10 @@ import Unison.Merge.ThreeWay qualified as ThreeWay import Unison.Name (Name) import Unison.NameSegment (NameSegment) import Unison.NameSegment qualified as NameSegment +import Unison.Names (Names) import Unison.Parser.Ann (Ann) import Unison.Prelude +import Unison.PrettyPrintEnvDecl qualified as PPED import Unison.Project ( ProjectAndBranch (..), ProjectBranchName, @@ -228,6 +231,16 @@ doMerge info = do Cli.runTransactionWithRollback2 (\rollback -> Right <$> action (rollback . Left)) & onLeftM (done . Output.ConflictedDefn "merge") + names3 :: Merge.ThreeWay Names <- do + let causalHashes = Merge.TwoOrThreeWay {alice = info.alice.causalHash, bob = info.bob.causalHash, lca = info.lca.causalHash} + branches <- for causalHashes \ch -> do + liftIO (Codebase.getBranchForHash env.codebase ch) >>= \case + Nothing -> done (Output.CouldntLoadBranch ch) + Just b -> pure b + let names = fmap (Branch.toNames . Branch.head) branches + pure Merge.ThreeWay {alice = names.alice, bob = names.bob, lca = fromMaybe mempty names.lca} + ppeds3 :: Merge.ThreeWay PPED.PrettyPrintEnvDecl <- for names3 Cli.prettyPrintEnvDeclFromNames + libdeps3 <- Cli.runTransaction (loadLibdeps branches) let blob0 = Merge.makeMergeblob0 nametrees3 libdeps3 @@ -252,7 +265,7 @@ doMerge info = do ) blob1 <- - Merge.makeMergeblob1 blob0 hydratedDefns & onLeft \case + Merge.makeMergeblob1 blob0 ppeds3 hydratedDefns & onLeft \case Merge.Alice reason -> done (Output.IncoherentDeclDuringMerge mergeTarget reason) Merge.Bob reason -> done (Output.IncoherentDeclDuringMerge mergeSource reason) From be313bc5dd6f9fb15b90bbd953889be2d1f000f6 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Mon, 19 Aug 2024 12:32:51 -0700 Subject: [PATCH 03/11] Add eitherToThese to prelude --- lib/unison-prelude/src/Unison/Prelude.hs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/unison-prelude/src/Unison/Prelude.hs b/lib/unison-prelude/src/Unison/Prelude.hs index 374f4a1812..ef48bc2556 100644 --- a/lib/unison-prelude/src/Unison/Prelude.hs +++ b/lib/unison-prelude/src/Unison/Prelude.hs @@ -25,6 +25,7 @@ module Unison.Prelude whenJustM, eitherToMaybe, maybeToEither, + eitherToThese, altSum, altMap, hoistMaybe, @@ -82,6 +83,7 @@ import Data.Text as X (Text) import Data.Text qualified as Text import Data.Text.Encoding as X (decodeUtf8, encodeUtf8) import Data.Text.IO qualified as Text +import Data.These (These (..)) import Data.Traversable as X (for) import Data.Typeable as X (Typeable) import Data.Void as X (Void) @@ -205,6 +207,9 @@ throwEitherM = throwEitherMWith id throwEitherMWith :: forall e e' m a. (MonadIO m, Exception e') => (e -> e') -> m (Either e a) -> m a throwEitherMWith f action = throwExceptT . withExceptT f $ (ExceptT action) +eitherToThese :: Either a b -> These a b +eitherToThese = either This That + tShow :: (Show a) => a -> Text tShow = Text.pack . show From 0daa48971fbffc3fce07f408e935a968529237ca Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Mon, 19 Aug 2024 12:32:51 -0700 Subject: [PATCH 04/11] Split off propagated updates when computing diff --- .../Codebase/Editor/HandleInput/Merge2.hs | 5 +- unison-merge/src/Unison/Merge/Diff.hs | 60 +++++++++++++------ unison-merge/src/Unison/Merge/Mergeblob1.hs | 21 ++++--- unison-merge/src/Unison/Merge/Mergeblob2.hs | 2 +- 4 files changed, 57 insertions(+), 31 deletions(-) diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs index 13af6839fc..0c97e4b363 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs @@ -38,7 +38,6 @@ import Unison.Cli.MergeTypes (MergeSource (..), MergeSourceAndTarget (..), Merge import Unison.Cli.Monad (Cli) import Unison.Cli.Monad qualified as Cli import Unison.Cli.MonadUtils qualified as Cli -import Unison.Cli.PrettyPrintUtils qualified as Cli import Unison.Cli.ProjectUtils qualified as ProjectUtils import Unison.Cli.UpdateUtils ( getNamespaceDependentsOf3, @@ -76,7 +75,6 @@ import Unison.NameSegment qualified as NameSegment import Unison.Names (Names) import Unison.Parser.Ann (Ann) import Unison.Prelude -import Unison.PrettyPrintEnvDecl qualified as PPED import Unison.Project ( ProjectAndBranch (..), ProjectBranchName, @@ -239,7 +237,6 @@ doMerge info = do Just b -> pure b let names = fmap (Branch.toNames . Branch.head) branches pure Merge.ThreeWay {alice = names.alice, bob = names.bob, lca = fromMaybe mempty names.lca} - ppeds3 :: Merge.ThreeWay PPED.PrettyPrintEnvDecl <- for names3 Cli.prettyPrintEnvDeclFromNames libdeps3 <- Cli.runTransaction (loadLibdeps branches) @@ -265,7 +262,7 @@ doMerge info = do ) blob1 <- - Merge.makeMergeblob1 blob0 ppeds3 hydratedDefns & onLeft \case + Merge.makeMergeblob1 blob0 names3 hydratedDefns & onLeft \case Merge.Alice reason -> done (Output.IncoherentDeclDuringMerge mergeTarget reason) Merge.Bob reason -> done (Output.IncoherentDeclDuringMerge mergeSource reason) diff --git a/unison-merge/src/Unison/Merge/Diff.hs b/unison-merge/src/Unison/Merge/Diff.hs index 6e5786ea34..39a0254723 100644 --- a/unison-merge/src/Unison/Merge/Diff.hs +++ b/unison-merge/src/Unison/Merge/Diff.hs @@ -3,9 +3,11 @@ module Unison.Merge.Diff ) where +import Data.Either.Combinators (mapRight) import Data.Map.Strict qualified as Map -import Data.Semialign (alignWith) +import Data.Semialign (Unalign (..), alignWith) import Data.These (These (..)) +import Data.Zip qualified as Zip import U.Codebase.Reference (TypeReference) import Unison.ConstructorReference (GConstructorReference (..)) import Unison.DataDeclaration (Decl) @@ -14,16 +16,20 @@ import Unison.DeclNameLookup (DeclNameLookup) import Unison.DeclNameLookup qualified as DeclNameLookup import Unison.Hash (Hash (Hash)) import Unison.Merge.DiffOp (DiffOp (..)) +import Unison.Merge.HumanDiffOp (HumanDiffOp) import Unison.Merge.PartialDeclNameLookup (PartialDeclNameLookup (..)) import Unison.Merge.Synhash qualified as Synhash import Unison.Merge.Synhashed (Synhashed (..)) +import Unison.Merge.Synhashed qualified as Synhashed import Unison.Merge.ThreeWay (ThreeWay (..)) import Unison.Merge.TwoWay (TwoWay (..)) import Unison.Merge.Updated (Updated (..)) import Unison.Name (Name) +import Unison.Names (Names) import Unison.Parser.Ann (Ann) import Unison.Prelude hiding (catMaybes) import Unison.PrettyPrintEnv (PrettyPrintEnv (..)) +import Unison.PrettyPrintEnv qualified as PPE import Unison.PrettyPrintEnvDecl qualified as PPED import Unison.Reference (Reference' (..), TermReference, TermReferenceId, TypeReferenceId) import Unison.Referent (Referent) @@ -50,32 +56,50 @@ nameBasedNamespaceDiff :: ThreeWay PPED.PrettyPrintEnvDecl -> ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)) -> Defns (Map TermReferenceId (Term Symbol Ann)) (Map TypeReferenceId (Decl Symbol Ann)) -> - TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference) + ( -- Core diffs, i.e. adds, deletes, and updates which have different synhashes. + TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference), + -- Propagated updates, i.e. updates which have the same synhash but different Unison hashes. + TwoWay (DefnsF2 (Map Name) Updated Referent TypeReference) + ) nameBasedNamespaceDiff declNameLookups lcaDeclNameLookup ppeds defns hydratedDefns = - let ThreeWay {lca = lcaPPE, alice = alicePPE, bob = bobPPE} = PPED.unsuffixifiedPPE <$> ppeds - lcaHashes = synhashLcaDefns lcaPPE lcaDeclNameLookup defns.lca hydratedDefns - aliceHashes = synhashDefns alicePPE hydratedDefns declNameLookups.alice defns.alice - bobHashes = synhashDefns bobPPE hydratedDefns declNameLookups.bob defns.bob - in diffHashedNamespaceDefns lcaHashes <$> TwoWay {alice = aliceHashes, bob = bobHashes} + let lcaHashes = synhashLcaDefns synhashPPE lcaDeclNameLookup defns.lca hydratedDefns + aliceHashes = synhashDefns synhashPPE hydratedDefns declNameLookups.alice defns.alice + bobHashes = synhashDefns synhashPPE hydratedDefns declNameLookups.bob defns.bob + in (diffHashedNamespaceDefns lcaHashes <$> TwoWay {alice = aliceHashes, bob = bobHashes}) + & Zip.unzip + where + synhashPPE :: PPE.PrettyPrintEnv + synhashPPE = + let ThreeWay {lca = lcaPPE, alice = alicePPE, bob = bobPPE} = PPED.unsuffixifiedPPE <$> ppeds + in alicePPE `PPE.addFallback` bobPPE `PPE.addFallback` lcaPPE diffHashedNamespaceDefns :: DefnsF2 (Map Name) Synhashed term typ -> DefnsF2 (Map Name) Synhashed term typ -> - DefnsF3 (Map Name) DiffOp Synhashed term typ -diffHashedNamespaceDefns = - zipDefnsWith f f + ( -- Core diffs, i.e. adds, deletes, and updates which have different synhashes. + DefnsF3 (Map Name) DiffOp Synhashed term typ, + -- Propagated updates, i.e. updates which have the same synhash but different Unison hashes. + DefnsF2 (Map Name) Updated term typ + ) +diffHashedNamespaceDefns d1 d2 = + zipDefnsWith f f d1 d2 + & splitPropagated where - f :: Map Name (Synhashed ref) -> Map Name (Synhashed ref) -> Map Name (DiffOp (Synhashed ref)) - f old new = - Map.mapMaybe id (alignWith g old new) + f :: Map Name (Synhashed ref) -> Map Name (Synhashed ref) -> (Map Name (DiffOp (Synhashed ref)), Map Name (Updated ref)) + f old new = unalign (eitherToThese . mapRight (fmap Synhashed.value) <$> alignWith g old new) - g :: (Eq x) => These x x -> Maybe (DiffOp x) + g :: (Eq x) => These x x -> Either (DiffOp x) (Updated x) g = \case - This old -> Just (DiffOp'Delete old) - That new -> Just (DiffOp'Add new) + This old -> Left (DiffOp'Delete old) + That new -> Left (DiffOp'Add new) These old new - | old == new -> Nothing - | otherwise -> Just (DiffOp'Update Updated {old, new}) + | old == new -> Right (Updated {old, new}) + | otherwise -> Left (DiffOp'Update Updated {old, new}) + splitPropagated :: + Defns (Map Name (DiffOp (Synhashed term)), Map Name (Updated term)) (Map Name (DiffOp (Synhashed typ)), Map Name (Updated typ)) -> + (DefnsF3 (Map Name) DiffOp Synhashed term typ, DefnsF2 (Map Name) Updated term typ) + splitPropagated Defns {terms, types} = + (Defns {terms = fst terms, types = fst types}, Defns {terms = snd terms, types = snd types}) ------------------------------------------------------------------------------------------------------------------------ -- Syntactic hashing diff --git a/unison-merge/src/Unison/Merge/Mergeblob1.hs b/unison-merge/src/Unison/Merge/Mergeblob1.hs index c0daa655cb..5aeabc693d 100644 --- a/unison-merge/src/Unison/Merge/Mergeblob1.hs +++ b/unison-merge/src/Unison/Merge/Mergeblob1.hs @@ -28,9 +28,12 @@ import Unison.Merge.TwoWay (TwoWay (..)) import Unison.Merge.Unconflicts (Unconflicts) import Unison.Name (Name) import Unison.NameSegment (NameSegment) +import Unison.Names (Names) import Unison.Parser.Ann (Ann) import Unison.Prelude +import Unison.PrettyPrintEnv.Names qualified as PPE import Unison.PrettyPrintEnvDecl qualified as PPED +import Unison.PrettyPrintEnvDecl.Names qualified as PPED import Unison.Reference (TermReference, TermReferenceId, TypeReference, TypeReferenceId) import Unison.Referent (Referent) import Unison.Symbol (Symbol) @@ -46,7 +49,7 @@ data Mergeblob1 libdep = Mergeblob1 declNameLookups :: TwoWay DeclNameLookup, defns :: ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)), diff :: DefnsF2 (Map Name) CombinedDiffOp Referent TypeReference, - diffs :: TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference), + diffsFromLCA :: TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference), hydratedDefns :: ThreeWay ( DefnsF @@ -78,7 +81,7 @@ makeMergeblob1 :: forall libdep. (Eq libdep) => Mergeblob0 libdep -> - ThreeWay PPED.PrettyPrintEnvDecl {- Pretty print env containing names for everything in 'hydratedDefnDependencies' -} -> + ThreeWay Names {- Names for _at least_ every reference in 'hydratedDefnDependencies' -} -> ThreeWay ( DefnsF (Map Name) @@ -86,7 +89,9 @@ makeMergeblob1 :: (TypeReferenceId, Decl Symbol Ann) ) -> Either (EitherWay IncoherentDeclReason) (Mergeblob1 libdep) -makeMergeblob1 blob ppeds hydratedDefns = do +makeMergeblob1 blob names3 hydratedDefns = do + let ppeds3 :: ThreeWay PPED.PrettyPrintEnvDecl + ppeds3 = names3 <&> \names -> (PPED.makePPED (PPE.namer names) (PPE.suffixifyByHash names)) -- Make one big constructor count lookup for all type decls let numConstructors = Map.empty @@ -114,11 +119,11 @@ makeMergeblob1 blob ppeds hydratedDefns = do lenientCheckDeclCoherency blob.nametrees.lca numConstructors -- Diff LCA->Alice and LCA->Bob - let diffs = + let (diffsFromLCA, propagatedUpdates) = nameBasedNamespaceDiff declNameLookups lcaDeclNameLookup - ppeds + ppeds3 blob.defns Defns { terms = @@ -132,8 +137,8 @@ makeMergeblob1 blob ppeds hydratedDefns = do } -- Combine the LCA->Alice and LCA->Bob diffs together - let diff = - combineDiffs diffs + let diff = combineDiffs diffsFromLCA + -- Partition the combined diff into the conflicted things and the unconflicted things let (conflicts, unconflicts) = @@ -154,7 +159,7 @@ makeMergeblob1 blob ppeds hydratedDefns = do declNameLookups, defns = blob.defns, diff, - diffs, + diffsFromLCA, hydratedDefns, lcaDeclNameLookup, libdeps, diff --git a/unison-merge/src/Unison/Merge/Mergeblob2.hs b/unison-merge/src/Unison/Merge/Mergeblob2.hs index fc76660bbe..6c4d98090f 100644 --- a/unison-merge/src/Unison/Merge/Mergeblob2.hs +++ b/unison-merge/src/Unison/Merge/Mergeblob2.hs @@ -62,7 +62,7 @@ data Mergeblob2Error makeMergeblob2 :: Mergeblob1 libdep -> Either Mergeblob2Error (Mergeblob2 libdep) makeMergeblob2 blob = do -- Bail early if it looks like we can't proceed with the merge, because Alice or Bob has one or more conflicted alias - for_ ((,) <$> TwoWay Alice Bob <*> blob.diffs) \(who, diff) -> + for_ ((,) <$> TwoWay Alice Bob <*> blob.diffsFromLCA) \(who, diff) -> whenJust (findConflictedAlias blob.defns.lca diff) $ Left . Mergeblob2Error'ConflictedAlias . who From 01fff50b75e3c459926d0568408956b55396e408 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Mon, 19 Aug 2024 12:32:51 -0700 Subject: [PATCH 05/11] Add Human Diff machinery --- unison-merge/src/Unison/Merge/Diff.hs | 10 +++++++ unison-merge/src/Unison/Merge/HumanDiffOp.hs | 29 ++++++++++++++++++++ unison-merge/src/Unison/Merge/Mergeblob1.hs | 4 +++ unison-merge/unison-merge.cabal | 1 + 4 files changed, 44 insertions(+) create mode 100644 unison-merge/src/Unison/Merge/HumanDiffOp.hs diff --git a/unison-merge/src/Unison/Merge/Diff.hs b/unison-merge/src/Unison/Merge/Diff.hs index 39a0254723..d0ccc5e3cc 100644 --- a/unison-merge/src/Unison/Merge/Diff.hs +++ b/unison-merge/src/Unison/Merge/Diff.hs @@ -1,5 +1,6 @@ module Unison.Merge.Diff ( nameBasedNamespaceDiff, + humanizeDiffs, ) where @@ -101,6 +102,15 @@ diffHashedNamespaceDefns d1 d2 = splitPropagated Defns {terms, types} = (Defns {terms = fst terms, types = fst types}, Defns {terms = snd terms, types = snd types}) +-- | Post-process a diff to identify relationships humans might care about, +-- such as whether a given addition could be interpreted as an alias of an existing definition, +-- or whether an add and deletion could be a rename. +humanizeDiffs :: + ThreeWay Names -> + TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference) -> + TwoWay (DefnsF2 (Map Name) HumanDiffOp Referent TypeReference) +humanizeDiffs names3 diffs = _ + ------------------------------------------------------------------------------------------------------------------------ -- Syntactic hashing diff --git a/unison-merge/src/Unison/Merge/HumanDiffOp.hs b/unison-merge/src/Unison/Merge/HumanDiffOp.hs new file mode 100644 index 0000000000..a6518852bc --- /dev/null +++ b/unison-merge/src/Unison/Merge/HumanDiffOp.hs @@ -0,0 +1,29 @@ +module Unison.Merge.HumanDiffOp + ( HumanDiffOp (..), + ) +where + +import Data.Set (Set) +import Data.Set.NonEmpty (NESet) +import Unison.Merge.Updated (Updated) +import Unison.Name (Name) + +-- | A diff operation is one of: +-- +-- * An add (where nothing was) +-- * A delete (of the thing that was) +-- * An update (from old to new) +-- * A propagated update (from old to new) +-- * An alias of some definition(s) on the other side +-- * A rename from some definition(s) on the other side +data HumanDiffOp ref + = HumanDiffOp'Add !ref + | HumanDiffOp'Delete !ref + | HumanDiffOp'Update !(Updated ref) + | HumanDiffOp'PropagatedUpdate !(Updated ref) + | HumanDiffOp'AliasOf !ref !(NESet Name {- existing names -}) + | -- The definition at this location was renamed from the given set of names to the current place + HumanDiffOp'RenamedFrom !ref !(NESet Name) + | -- The definition at this location was renamed to the given set of names from the current place + HumanDiffOp'RenamedTo !ref !(NESet Name) + deriving stock (Show) diff --git a/unison-merge/src/Unison/Merge/Mergeblob1.hs b/unison-merge/src/Unison/Merge/Mergeblob1.hs index 5aeabc693d..4b69b79990 100644 --- a/unison-merge/src/Unison/Merge/Mergeblob1.hs +++ b/unison-merge/src/Unison/Merge/Mergeblob1.hs @@ -17,6 +17,7 @@ import Unison.Merge.DeclCoherencyCheck (IncoherentDeclReason, checkDeclCoherency import Unison.Merge.Diff (nameBasedNamespaceDiff) import Unison.Merge.DiffOp (DiffOp) import Unison.Merge.EitherWay (EitherWay (..)) +import Unison.Merge.HumanDiffOp (HumanDiffOp) import Unison.Merge.Libdeps (LibdepDiffOp, applyLibdepsDiff, diffLibdeps, getTwoFreshLibdepNames) import Unison.Merge.Mergeblob0 (Mergeblob0 (..)) import Unison.Merge.PartialDeclNameLookup (PartialDeclNameLookup) @@ -50,6 +51,7 @@ data Mergeblob1 libdep = Mergeblob1 defns :: ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)), diff :: DefnsF2 (Map Name) CombinedDiffOp Referent TypeReference, diffsFromLCA :: TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference), + humanDiffsFromLCA :: TwoWay (DefnsF2 (Map Name) HumanDiffOp Referent TypeReference), hydratedDefns :: ThreeWay ( DefnsF @@ -139,6 +141,7 @@ makeMergeblob1 blob names3 hydratedDefns = do -- Combine the LCA->Alice and LCA->Bob diffs together let diff = combineDiffs diffsFromLCA + let humanDiffsFromLCA = humanizeDiffs names3 diffsFromLCA propagatedUpdates -- Partition the combined diff into the conflicted things and the unconflicted things let (conflicts, unconflicts) = @@ -160,6 +163,7 @@ makeMergeblob1 blob names3 hydratedDefns = do defns = blob.defns, diff, diffsFromLCA, + humanDiffsFromLCA, hydratedDefns, lcaDeclNameLookup, libdeps, diff --git a/unison-merge/unison-merge.cabal b/unison-merge/unison-merge.cabal index 01f9170c4c..20fa1b3a4c 100644 --- a/unison-merge/unison-merge.cabal +++ b/unison-merge/unison-merge.cabal @@ -25,6 +25,7 @@ library Unison.Merge.EitherWay Unison.Merge.EitherWayI Unison.Merge.FindConflictedAlias + Unison.Merge.HumanDiffOp Unison.Merge.Libdeps Unison.Merge.Mergeblob0 Unison.Merge.Mergeblob1 From bba872243c1cfd24d204baf6f024eea682278c39 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Mon, 19 Aug 2024 13:25:55 -0700 Subject: [PATCH 06/11] Wire up a working humanized diff. --- .../Codebase/Editor/HandleInput/Merge2.hs | 2 +- unison-core/src/Unison/Util/Defns.hs | 12 +++ unison-merge/src/Unison/Merge/Diff.hs | 76 ++++++++++++++++++- unison-merge/src/Unison/Merge/HumanDiffOp.hs | 1 - unison-merge/src/Unison/Merge/Mergeblob1.hs | 2 +- 5 files changed, 87 insertions(+), 6 deletions(-) diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs index 0c97e4b363..1a533734e8 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs @@ -266,7 +266,7 @@ doMerge info = do Merge.Alice reason -> done (Output.IncoherentDeclDuringMerge mergeTarget reason) Merge.Bob reason -> done (Output.IncoherentDeclDuringMerge mergeSource reason) - liftIO (debugFunctions.debugDiffs blob1.diffs) + liftIO (debugFunctions.debugDiffs blob1.diffsFromLCA) liftIO (debugFunctions.debugCombinedDiff blob1.diff) diff --git a/unison-core/src/Unison/Util/Defns.hs b/unison-core/src/Unison/Util/Defns.hs index e61c5ba7bb..5f56166d01 100644 --- a/unison-core/src/Unison/Util/Defns.hs +++ b/unison-core/src/Unison/Util/Defns.hs @@ -13,6 +13,7 @@ module Unison.Util.Defns zipDefns, zipDefnsWith, zipDefnsWith3, + zipDefnsWith4, ) where @@ -99,3 +100,14 @@ zipDefnsWith3 :: Defns tm4 ty4 zipDefnsWith3 f g (Defns terms1 types1) (Defns terms2 types2) (Defns terms3 types3) = Defns (f terms1 terms2 terms3) (g types1 types2 types3) + +zipDefnsWith4 :: + (tm1 -> tm2 -> tm3 -> tm4 -> tm5) -> + (ty1 -> ty2 -> ty3 -> ty4 -> ty5) -> + Defns tm1 ty1 -> + Defns tm2 ty2 -> + Defns tm3 ty3 -> + Defns tm4 ty4 -> + Defns tm5 ty5 +zipDefnsWith4 f g (Defns terms1 types1) (Defns terms2 types2) (Defns terms3 types3) (Defns terms4 types4) = + Defns (f terms1 terms2 terms3 terms4) (g types1 types2 types3 types4) diff --git a/unison-merge/src/Unison/Merge/Diff.hs b/unison-merge/src/Unison/Merge/Diff.hs index d0ccc5e3cc..b4d6730d85 100644 --- a/unison-merge/src/Unison/Merge/Diff.hs +++ b/unison-merge/src/Unison/Merge/Diff.hs @@ -5,8 +5,12 @@ module Unison.Merge.Diff where import Data.Either.Combinators (mapRight) +import Data.List.NonEmpty qualified as NEL +import Data.List.NonEmpty qualified as NEList import Data.Map.Strict qualified as Map import Data.Semialign (Unalign (..), alignWith) +import Data.Set qualified as Set +import Data.Set.NonEmpty qualified as NESet import Data.These (These (..)) import Data.Zip qualified as Zip import U.Codebase.Reference (TypeReference) @@ -17,16 +21,18 @@ import Unison.DeclNameLookup (DeclNameLookup) import Unison.DeclNameLookup qualified as DeclNameLookup import Unison.Hash (Hash (Hash)) import Unison.Merge.DiffOp (DiffOp (..)) -import Unison.Merge.HumanDiffOp (HumanDiffOp) +import Unison.Merge.HumanDiffOp (HumanDiffOp (..)) import Unison.Merge.PartialDeclNameLookup (PartialDeclNameLookup (..)) import Unison.Merge.Synhash qualified as Synhash import Unison.Merge.Synhashed (Synhashed (..)) import Unison.Merge.Synhashed qualified as Synhashed import Unison.Merge.ThreeWay (ThreeWay (..)) +import Unison.Merge.ThreeWay qualified as ThreeWay import Unison.Merge.TwoWay (TwoWay (..)) import Unison.Merge.Updated (Updated (..)) import Unison.Name (Name) import Unison.Names (Names) +import Unison.Names qualified as Names import Unison.Parser.Ann (Ann) import Unison.Prelude hiding (catMaybes) import Unison.PrettyPrintEnv (PrettyPrintEnv (..)) @@ -40,7 +46,10 @@ import Unison.Syntax.Name qualified as Name import Unison.Term (Term) import Unison.Util.BiMultimap (BiMultimap) import Unison.Util.BiMultimap qualified as BiMultimap -import Unison.Util.Defns (Defns (..), DefnsF2, DefnsF3, zipDefnsWith) +import Unison.Util.Defns (Defns (..), DefnsF, DefnsF2, DefnsF3, zipDefnsWith) +import Unison.Util.Defns qualified as Defns +import Unison.Util.Relation (Relation) +import Unison.Util.Relation qualified as Rel -- | @nameBasedNamespaceDiff db declNameLookups defns@ returns Alice's and Bob's name-based namespace diffs, each in the -- form: @@ -108,8 +117,69 @@ diffHashedNamespaceDefns d1 d2 = humanizeDiffs :: ThreeWay Names -> TwoWay (DefnsF3 (Map Name) DiffOp Synhashed Referent TypeReference) -> + TwoWay (DefnsF2 (Map Name) Updated Referent TypeReference) -> TwoWay (DefnsF2 (Map Name) HumanDiffOp Referent TypeReference) -humanizeDiffs names3 diffs = _ +humanizeDiffs names3 diffs propagatedUpdates = + zipWithF3 + nameRelations + diffs + propagatedUpdates + \relation diffOps propagatedUpdates -> Defns.zipDefnsWith4 computeHumanDiffOp computeHumanDiffOp lcaRelation relation diffOps propagatedUpdates + where + zipWithF3 :: (Zip.Zip f) => f a -> f b -> f c -> (a -> b -> c -> d) -> f d + zipWithF3 a b c f = Zip.zipWith (\(x, y) z -> f x y z) (Zip.zip a b) c + namesToRelations :: Names -> (DefnsF (Relation Name) Referent TypeReference) + namesToRelations names = Defns {terms = Names.terms names, types = Names.types names} + lcaRelation :: DefnsF (Relation Name) Referent TypeReference + lcaRelation = namesToRelations names3.lca + nameRelations :: TwoWay (DefnsF (Relation Name) Referent TypeReference) + nameRelations = namesToRelations <$> ThreeWay.forgetLca names3 + + computeHumanDiffOp :: + forall ref. + (Show ref, Ord ref) => + Relation Name ref -> + Relation Name ref -> + Map Name (DiffOp (Synhashed ref)) -> + Map Name (Updated ref) -> + Map Name (HumanDiffOp ref) + computeHumanDiffOp oldRelation newRelation diffs propagatedUpdates = alignWith go diffs propagatedUpdates + where + go :: These (DiffOp (Synhashed ref)) (Updated ref) -> (HumanDiffOp ref) + go = \case + This diff -> humanizeDiffOp (Synhashed.value <$> diff) + That updated -> (HumanDiffOp'PropagatedUpdate updated) + These diff updated -> error (reportBug "E488729" ("The impossible happened, an update in merge was detected as both a propagated AND core update " ++ show diff ++ " and " ++ show updated)) + + humanizeDiffOp :: DiffOp ref -> HumanDiffOp ref + humanizeDiffOp = \case + DiffOp'Add ref -> + -- This name is newly added. We need to check if it's a new definition, an alias, or a rename. + case Set.toList (Rel.lookupRan ref oldRelation) of + -- No old names for this ref, so it's a new addition not an alias + [] -> HumanDiffOp'Add ref + -- There are old names for this ref, but not old refs for this name, so it's + -- either a new alias or a rename. + -- + -- If at least one old name for this ref no longer exists, we treat it like a + -- rename. + (n : ns) -> do + let existingNames = NESet.fromList (n NEList.:| ns) + case NESet.nonEmptySet (Rel.lookupRan ref newRelation) of + Nothing -> error (reportBug "E458329" ("Expected to find at least one name for ref in new namespace, since we found the ref by the name.")) + Just allNewNames -> + case NESet.nonEmptySet (NESet.difference existingNames allNewNames) of + -- If all the old names still exist in the new namespace, it's a new alias. + Nothing -> HumanDiffOp'AliasOf ref existingNames + -- Otherwise, treat it as a rename. + Just namesWhichDisappeared -> + HumanDiffOp'RenamedFrom ref namesWhichDisappeared + DiffOp'Delete ref -> + case NEL.nonEmpty $ Set.toList (Rel.lookupRan ref newRelation) of + -- No names for this ref, it was removed. + Nothing -> HumanDiffOp'Delete ref + Just newNames -> HumanDiffOp'RenamedTo ref (NESet.fromList newNames) + DiffOp'Update Updated {old, new} -> HumanDiffOp'Update Updated {old, new} ------------------------------------------------------------------------------------------------------------------------ -- Syntactic hashing diff --git a/unison-merge/src/Unison/Merge/HumanDiffOp.hs b/unison-merge/src/Unison/Merge/HumanDiffOp.hs index a6518852bc..1a4c5e4299 100644 --- a/unison-merge/src/Unison/Merge/HumanDiffOp.hs +++ b/unison-merge/src/Unison/Merge/HumanDiffOp.hs @@ -3,7 +3,6 @@ module Unison.Merge.HumanDiffOp ) where -import Data.Set (Set) import Data.Set.NonEmpty (NESet) import Unison.Merge.Updated (Updated) import Unison.Name (Name) diff --git a/unison-merge/src/Unison/Merge/Mergeblob1.hs b/unison-merge/src/Unison/Merge/Mergeblob1.hs index 4b69b79990..6e5477a84b 100644 --- a/unison-merge/src/Unison/Merge/Mergeblob1.hs +++ b/unison-merge/src/Unison/Merge/Mergeblob1.hs @@ -14,7 +14,7 @@ import Unison.DeclNameLookup (DeclNameLookup) import Unison.LabeledDependency qualified as LD import Unison.Merge.CombineDiffs (CombinedDiffOp, combineDiffs) import Unison.Merge.DeclCoherencyCheck (IncoherentDeclReason, checkDeclCoherency, lenientCheckDeclCoherency) -import Unison.Merge.Diff (nameBasedNamespaceDiff) +import Unison.Merge.Diff (humanizeDiffs, nameBasedNamespaceDiff) import Unison.Merge.DiffOp (DiffOp) import Unison.Merge.EitherWay (EitherWay (..)) import Unison.Merge.HumanDiffOp (HumanDiffOp) From de79fe82f6f020ab9ab39785cc75b29ddf76a258 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Mon, 19 Aug 2024 16:31:56 -0700 Subject: [PATCH 07/11] Fix up hydrated defns labeled dependencies --- unison-merge/src/Unison/Merge/Mergeblob1.hs | 30 +++++++++++---------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/unison-merge/src/Unison/Merge/Mergeblob1.hs b/unison-merge/src/Unison/Merge/Mergeblob1.hs index 6e5477a84b..5a0799f863 100644 --- a/unison-merge/src/Unison/Merge/Mergeblob1.hs +++ b/unison-merge/src/Unison/Merge/Mergeblob1.hs @@ -1,6 +1,6 @@ module Unison.Merge.Mergeblob1 ( Mergeblob1 (..), - hydratedDefnDependencies, + hydratedDefnsLabeledDependencies, makeMergeblob1, ) where @@ -8,8 +8,10 @@ where import Control.Lens import Data.List qualified as List import Data.Map.Strict qualified as Map +import Data.Set qualified as Set import Unison.DataDeclaration (Decl) import Unison.DataDeclaration qualified as DataDeclaration +import Unison.DataDeclaration.Dependencies qualified as Decl import Unison.DeclNameLookup (DeclNameLookup) import Unison.LabeledDependency qualified as LD import Unison.Merge.CombineDiffs (CombinedDiffOp, combineDiffs) @@ -36,6 +38,7 @@ import Unison.PrettyPrintEnv.Names qualified as PPE import Unison.PrettyPrintEnvDecl qualified as PPED import Unison.PrettyPrintEnvDecl.Names qualified as PPED import Unison.Reference (TermReference, TermReferenceId, TypeReference, TypeReferenceId) +import Unison.Reference qualified as Reference import Unison.Referent (Referent) import Unison.Symbol (Symbol) import Unison.Term (Term) @@ -65,19 +68,18 @@ data Mergeblob1 libdep = Mergeblob1 unconflicts :: DefnsF Unconflicts Referent TypeReference } -hydratedDefnDependencies :: - ThreeWay - ( DefnsF - (Map Name) - (TermReferenceId, (Term Symbol Ann, Type Symbol Ann)) - (TypeReferenceId, Decl Symbol Ann) - ) -> - ThreeWay (Set LD.LabeledDependency) -hydratedDefnDependencies hydratedDefns = - hydratedDefns - <&> \Defns {terms, types} -> - (terms & foldOf (folded . _2 . beside (to Term.labeledDependencies) (to Type.labeledDependencies))) - <> (types & foldOf (folded . _2 . to DataDeclaration.labeledDeclTypeDependencies)) +-- | Get a names object for all the hydrated definitions AND their direct dependencies +hydratedDefnsLabeledDependencies :: (DefnsF (Map Name) (TermReferenceId, (Term Symbol Ann, Type Symbol Ann)) (TypeReferenceId, Decl Symbol Ann)) -> Set LD.LabeledDependency +hydratedDefnsLabeledDependencies (Defns {terms, types}) = + let termDeps :: Set LD.LabeledDependency + termDeps = foldOf (folded . beside (to Reference.DerivedId . to LD.TermReference . to Set.singleton) (beside (to Term.labeledDependencies) (to Type.labeledDependencies))) terms + typeDeps :: Set LD.LabeledDependency + typeDeps = + types + & foldMap \(typeRefId, typeDecl) -> + let typeRef = Reference.DerivedId typeRefId + in Decl.labeledDeclDependenciesIncludingSelfAndFieldAccessors typeRef typeDecl + in termDeps <> typeDeps makeMergeblob1 :: forall libdep. From 020d9c6d1ceb42edc5448a4e4d87bbdecaa58d09 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Wed, 21 Aug 2024 12:02:38 -0700 Subject: [PATCH 08/11] Add semigroup/monoid to nametree --- unison-core/src/Unison/Util/Nametree.hs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/unison-core/src/Unison/Util/Nametree.hs b/unison-core/src/Unison/Util/Nametree.hs index e87bdde344..50ae6d1510 100644 --- a/unison-core/src/Unison/Util/Nametree.hs +++ b/unison-core/src/Unison/Util/Nametree.hs @@ -3,6 +3,7 @@ module Unison.Util.Nametree Nametree (..), traverseNametreeWithName, unfoldNametree, + unionWith, -- ** Flattening and unflattening flattenNametree, @@ -33,6 +34,16 @@ data Nametree a = Nametree } deriving stock (Functor, Foldable, Traversable, Generic, Show) +unionWith :: (a -> a -> a) -> Nametree a -> Nametree a -> Nametree a +unionWith f (Nametree x xs) (Nametree y ys) = + Nametree (f x y) (Map.unionWith (unionWith f) xs ys) + +instance (Semigroup a) => Semigroup (Nametree a) where + (<>) = unionWith (<>) + +instance (Monoid a) => Monoid (Nametree a) where + mempty = Nametree mempty mempty + instance Semialign Nametree where alignWith :: (These a b -> c) -> Nametree a -> Nametree b -> Nametree c alignWith f (Nametree x xs) (Nametree y ys) = From 50715280a5e00b1044baa471eb8ff1d97c9decc2 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Wed, 21 Aug 2024 15:26:45 -0700 Subject: [PATCH 09/11] Add optics for Defns --- unison-core/src/Unison/Util/Defns.hs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/unison-core/src/Unison/Util/Defns.hs b/unison-core/src/Unison/Util/Defns.hs index 5f56166d01..dc87e7ebc8 100644 --- a/unison-core/src/Unison/Util/Defns.hs +++ b/unison-core/src/Unison/Util/Defns.hs @@ -4,6 +4,8 @@ module Unison.Util.Defns DefnsF2, DefnsF3, DefnsF4, + terms_, + types_, alignDefnsWith, defnsAreEmpty, hoistDefnsF, @@ -17,6 +19,7 @@ module Unison.Util.Defns ) where +import Control.Lens (Lens) import Data.Align (Semialign, alignWith) import Data.Bifoldable (Bifoldable, bifoldMap) import Data.Bitraversable (Bitraversable, bitraverse) @@ -44,6 +47,12 @@ instance Bitraversable Defns where bitraverse f g (Defns x y) = Defns <$> f x <*> g y +terms_ :: Lens (Defns terms types) (Defns terms' types) terms terms' +terms_ f (Defns x y) = (\x' -> Defns x' y) <$> f x + +types_ :: Lens (Defns terms types) (Defns terms types') types types' +types_ f (Defns x y) = (\y' -> Defns x y') <$> f y + -- | A common shape of definitions - terms and types are stored in the same structure. type DefnsF f terms types = Defns (f terms) (f types) From 821ec0416721d2cb6d7619639ff0c8e5d063b653 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Wed, 21 Aug 2024 16:42:03 -0700 Subject: [PATCH 10/11] Add Eq, Ord to Defn combinators --- unison-core/src/Unison/Util/Defn.hs | 21 +++++++++++++++++++++ unison-core/src/Unison/Util/Defns.hs | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/unison-core/src/Unison/Util/Defn.hs b/unison-core/src/Unison/Util/Defn.hs index d897491de4..26a0fdd222 100644 --- a/unison-core/src/Unison/Util/Defn.hs +++ b/unison-core/src/Unison/Util/Defn.hs @@ -3,7 +3,28 @@ module Unison.Util.Defn ) where +import Data.Bifoldable (Bifoldable (..)) +import Data.Bifunctor (Bifunctor (..)) +import Data.Bitraversable (Bitraversable (..)) +import GHC.Generics (Generic) + -- | A "definition" is either a term or a type. data Defn term typ = TermDefn term | TypeDefn typ + deriving stock (Generic, Functor, Foldable, Traversable, Show, Eq, Ord) + +instance Bifunctor Defn where + bimap f g = \case + TermDefn x -> TermDefn (f x) + TypeDefn y -> TypeDefn (g y) + +instance Bifoldable Defn where + bifoldMap f g = \case + TermDefn x -> f x + TypeDefn y -> g y + +instance Bitraversable Defn where + bitraverse f g = \case + TermDefn x -> TermDefn <$> f x + TypeDefn y -> TypeDefn <$> g y diff --git a/unison-core/src/Unison/Util/Defns.hs b/unison-core/src/Unison/Util/Defns.hs index dc87e7ebc8..5c4eb8d41e 100644 --- a/unison-core/src/Unison/Util/Defns.hs +++ b/unison-core/src/Unison/Util/Defns.hs @@ -32,7 +32,7 @@ data Defns terms types = Defns { terms :: terms, types :: types } - deriving stock (Generic, Functor, Show) + deriving stock (Generic, Functor, Show, Eq, Ord) deriving (Monoid, Semigroup) via GenericSemigroupMonoid (Defns terms types) instance Bifoldable Defns where From 375cb30b124e5b7dd5137c8c5c19238e7cf68d94 Mon Sep 17 00:00:00 2001 From: Chris Penner Date: Thu, 22 Aug 2024 09:48:33 -0700 Subject: [PATCH 11/11] Add for to Set Utils --- lib/unison-prelude/src/Unison/Util/Set.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/unison-prelude/src/Unison/Util/Set.hs b/lib/unison-prelude/src/Unison/Util/Set.hs index 50d2cff56a..789708937b 100644 --- a/lib/unison-prelude/src/Unison/Util/Set.hs +++ b/lib/unison-prelude/src/Unison/Util/Set.hs @@ -4,6 +4,7 @@ module Unison.Util.Set mapMaybe, symmetricDifference, Unison.Util.Set.traverse, + Unison.Util.Set.for, flatMap, filterM, forMaybe, @@ -45,6 +46,9 @@ forMaybe xs f = traverse :: (Applicative f, Ord b) => (a -> f b) -> Set a -> f (Set b) traverse f = fmap Set.fromList . Prelude.traverse f . Set.toList +for :: (Ord b, Applicative f) => Set a -> (a -> f b) -> f (Set b) +for = flip Unison.Util.Set.traverse + flatMap :: (Ord b) => (a -> Set b) -> Set a -> Set b flatMap f = Set.unions . fmap f . Set.toList