!tp      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ NoneX tree-diffList edit operationsThe \ constructor is redundant, but it let us spot a recursion point when performing tree diffs. tree-diffinsert tree-diffdelete tree-diffcopy unchanged tree-diffswap, i.e. delete + insert tree-diffList difference.diffBy (==) "hello" "world"9[Swp 'h' 'w',Swp 'e' 'o',Swp 'l' 'r',Cpy 'l',Swp 'o' 'd']diffBy (==) "kitten" "sitting"A[Swp 'k' 's',Cpy 'i',Cpy 't',Cpy 't',Swp 'e' 'i',Cpy 'n',Ins 'g']O\xs ys -> length (diffBy (==) xs ys) >= max (length xs) (length (ys :: String))I\xs ys -> length (diffBy (==) xs ys) <= length xs + length (ys :: String)Note:g currently this has O(n*m) memory requirements, for the sake of more obviously correct implementation.None  tree-diffType used in the result of ediff.  tree-diffunchanged tree  tree-diff"A untyped Haskell-like expression."Having richer structure than just Tree allows to have richer diffs.  tree-diff application tree-diffrecord constructor tree-difflist constructor tree-diff"Record field name is a string too. tree-diffConstructor name is a string tree-diff Diff two  .For examples see ediff in Data.TreeDiff.Class.     None &',8>SUVXfIk tree-diffB converts a Haskell value into untyped Haskell-like syntax tree,  .(toExpr ((1, Just 2) :: (Int, Maybe Int))1App "_\215_" [App "1" [],App "Just" [App "2" []]] tree-diffDifference between two  values.'let x = (1, Just 2) :: (Int, Maybe Int)let y = (1, Nothing)prettyEditExpr (ediff x y)__ 1 -(Just 2) +Nothingqdata Foo = Foo { fooInt :: Either Char Int, fooBool :: [Maybe Bool], fooString :: String } deriving (Eq, Generic)instance ToExpr FooXprettyEditExpr $ ediff (Foo (Right 2) [Just True] "fo") (Foo (Right 3) [Just True] "fo")CFoo {fooBool = [Just True], fooInt = Right -2 +3, fooString = "fo"}}prettyEditExpr $ ediff (Foo (Right 42) [Just True, Just False] "old") (Foo (Right 42) [Nothing, Just False, Just True] "new")Foo< {fooBool = [-Just True, +Nothing, Just False, +Just True], fooInt = Right 42, fooString = -"old" +"new"} tree-diffCompare different types.Note:? Use with care as you can end up comparing apples with oranges.<prettyEditExpr $ ediff' ["foo", "bar"] [Just "foo", Nothing]'[-"foo", +Just "foo", -"bar", +Nothing] tree-diff8An alternative implementation for literal types. We use  representation of them. tree-diffGeneric .(data Foo = Foo Int Char deriving GenericgenericToExpr (Foo 42 'x')$App "Foo" [App "42" [],App "'x'" []] tree-diff Split on '\n'.(\xs -> xs == concat (unconcat uncons xs)( tree-diffprettyExpr $ toExpr UUID.nil+UUID "00000000-0000-0000-0000-000000000000") tree-diff+prettyExpr $ toExpr (123.456 :: Scientific)scientific 123456 `-3`* tree-diffutraverse_ (print . prettyExpr . toExpr . SBS.toShort . BS8.pack) ["", "\n", "foo", "foo\n", "foo\nbar", "foo\nbar\n"]"""\n""foo""foo\n"mconcat ["foo\n", "bar"]mconcat ["foo\n", "bar\n"]+ tree-diffgtraverse_ (print . prettyExpr . toExpr . BS8.pack) ["", "\n", "foo", "foo\n", "foo\nbar", "foo\nbar\n"]"""\n""foo""foo\n"BS.concat ["foo\n", "bar"]BS.concat ["foo\n", "bar\n"], tree-diffhtraverse_ (print . prettyExpr . toExpr . LBS8.pack) ["", "\n", "foo", "foo\n", "foo\nbar", "foo\nbar\n"]"""\n""foo""foo\n"LBS.concat ["foo\n", "bar"]LBS.concat ["foo\n", "bar\n"]. tree-diff-prettyExpr $ toExpr $ ModifiedJulianDay 58014Day "2017-09-18"/ tree-diffetraverse_ (print . prettyExpr . toExpr . T.pack) ["", "\n", "foo", "foo\n", "foo\nbar", "foo\nbar\n"]"""\n""foo""foo\n"T.concat ["foo\n", "bar"]T.concat ["foo\n", "bar\n"]0 tree-diffftraverse_ (print . prettyExpr . toExpr . LT.pack) ["", "\n", "foo", "foo\n", "foo\nbar", "foo\nbar\n"]"""\n""foo""foo\n"LT.concat ["foo\n", "bar"]LT.concat ["foo\n", "bar\n"]E tree-diff"prettyExpr $ toExpr $ Identity 'a' Identity 'a'G tree-diff(prettyExpr $ toExpr (3 % 12 :: Rational)_%_ 1 4O tree-diffprettyExpr $ toExpr 'a''a'!prettyExpr $ toExpr "Hello world" "Hello world""prettyExpr $ toExpr "Hello\nworld"concat ["Hello\n", "world"]\traverse_ (print . prettyExpr . toExpr) ["", "\n", "foo", "foo\n", "foo\nbar", "foo\nbar\n"]"""\n""foo""foo\n"concat ["foo\n", "bar"]concat ["foo\n", "bar\n"] tree-diffname of concatNoneXLhl tree-diff Parsers for   using parsers type-classes.AYou can use this with your parser-combinator library of choice: parsec,  attoparsec, trifecta...llNoneaNm tree-diff_Because we don't want to commit to single pretty printing library, we use explicit dictionary.x tree-diff Escape field or constructor nameputStrLn $ escapeName "Foo"FooputStrLn $ escapeName "__"__putStrLn $ escapeName "-3"`-3`"putStrLn $ escapeName "kebab-case" kebab-case#putStrLn $ escapeName "inner space" `inner space`2putStrLn $ escapeName $ show "looks like a string""looks like a string".putStrLn $ escapeName $ show "tricky" ++ " " `"tricky" `putStrLn $ escapeName "[]"`[]`putStrLn $ escapeName "_,_"`_,_`y tree-diffPretty print an  + using explicit pretty-printing dictionary.z tree-diffPretty print an  + using explicit pretty-printing dictionary.{ tree-diffLike z) but print unchanged parts only shallowly| tree-diffm via pretty library.} tree-diff Pretty print   using pretty.;prettyExpr $ Rec "ex" (Map.fromList [("[]", App "bar" [])])ex {`[]` = bar}~ tree-diff Pretty print   using pretty. tree-diffCompact ~. tree-diffm via ansi-wl-pprint library (with colors). tree-diff Pretty print   using ansi-wl-pprint. tree-diff Pretty print   using ansi-wl-pprint. tree-diffCompact  tree-diffLike  but color the background. tree-diff Pretty print   using ansi-wl-pprint. tree-diff Pretty print   using ansi-wl-pprint. tree-diffCompact .mnopqrstuvwxyz{|}~mnopqrstuvwyz{|}~x Noneb\5  lmnopqrstuvwxyz{|}~Nonem tree-diffMake a golden tests.B is testing framework agnostic, thus the type looks intimidating.An example using  tasty-golden,  goldenTest is imported from Test.Tasty.Golden.Advanced exTest :: TestTree exTest = S goldenTest "golden test" "fixtures/ex.expr" $ action constructing actual value The  will read an  ; from provided path to golden file, and compare it with a K of a result. If values differ, the (compact) diff of two will be printed.See  >https://github.com/phadej/tree-diff/blob/master/tests/Tests.hs for a proper example. tree-diff goldenTest tree-diff test name tree-diffpath to "golden file" tree-diff result valueNoneo' tree-diff A variant of ===/, which outputs a diff when values are inequal.None, tree-diffType used in the result of .It's essentially a ', but the forest list is changed from [tree a] to [ (tree a)]. This highlights that ) performs a list diff on each tree level. tree-diffA breadth-traversal diff.It's different from gdiff|, as it doesn't produce a flat edit script, but edit script iself is a tree. This makes visualising the diff much simpler.ExamplesDLet's start from simple tree. We pretty print them as s-expressions.Nlet x = Node 'a' [Node 'b' [], Node 'c' [return 'd', return 'e'], Node 'f' []]ppTree PP.char x(a b (c d e) f)>If we modify an argument in a tree, we'll notice it's changed:Nlet y = Node 'a' [Node 'b' [], Node 'c' [return 'x', return 'e'], Node 'f' []]ppTree PP.char y(a b (c x e) f)!ppEditTree PP.char (treeDiff x y)(a b (c -d +x e) f)dIf we modify a constructor, the whole sub-trees is replaced, though there might be common subtrees.8let z = Node 'a' [Node 'b' [], Node 'd' [], Node 'f' []]ppTree PP.char z (a b d f)!ppEditTree PP.char (treeDiff x z)(a b -(c d e) +d f)*If we add arguments, they are spotted too:Zlet w = Node 'a' [Node 'b' [], Node 'c' [return 'd', return 'x', return 'e'], Node 'f' []]ppTree PP.char w(a b (c d x e) f)!ppEditTree PP.char (treeDiff x w)(a b (c d +x e) f)     !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwwxyz{|}~$tree-diff-0.1-HMBpOJ7o0bI7YmgRHTXUcbData.TreeDiff.ListData.TreeDiff.ExprData.TreeDiff.ClassData.TreeDiff.ParserData.TreeDiff.PrettyData.TreeDiff.GoldenData.TreeDiff.QuickCheckData.TreeDiff.Tree Data.TreeDiffEditInsDelCpySwpdiffBy $fShowEditEditExprEditAppEditRecEditLstEditExpExprAppRecLst FieldNameConstructorNameexprDiff$fArbitraryExpr$fEqExpr $fShowExpr$fShowEditExprGToExprToExprtoExpr listToExprediffediff'defaultExprViaShow genericToExpr $fToExprValue$fToExprHashSet$fToExprHashMap$fToExprHashed$fToExprTagged$fToExprVector$fToExprVector0$fToExprVector1$fToExprVector2 $fToExprUUID$fToExprScientific$fToExprShortByteString$fToExprByteString$fToExprByteString0$fToExprUTCTime $fToExprDay $fToExprText $fToExprText0 $fToExprSeq$fToExprIntSet$fToExprIntMap $fToExprSet $fToExprMap $fToExprTree $fToExprLast $fToExprFirst $fToExprMax $fToExprMin$fToExprOption $fToExprLast0$fToExprFirst0$fToExprProduct $fToExprSum $fToExprDual $fToExprVoid$fToExprNonEmpty$fToExprZipList $fToExprConst$fToExprIdentity $fToExprFixed $fToExprRatio$fToExpr(,,,,) $fToExpr(,,,) $fToExpr(,,) $fToExpr(,) $fToExpr[]$fToExprEither $fToExprMaybe $fToExprChar $fToExprProxy$fToExprWord64$fToExprWord32$fToExprWord16 $fToExprWord8 $fToExprWord $fToExprInt64 $fToExprInt32 $fToExprInt16 $fToExprInt8 $fToExprInt$fToExprDouble $fToExprFloat$fToExprNatural$fToExprInteger$fToExprOrdering $fToExprBool $fToExpr() $fToExprExpr$fGSumToExprV1$fGSumToExpr:+: $fGToExprM1$fGLeafToExprK1$fGProductToExprM1$fGProductToExprU1$fGProductToExpr:*:$fGSumToExprM1$fShowAppOrRec exprParserPrettyppConppRecppLstppCpyppInsppDelppSepppParensppHang escapeNameppExpr ppEditExprppEditExprCompact prettyPretty prettyExprprettyEditExprprettyEditExprCompact ansiWlPretty ansiWlExpransiWlEditExpransiWlEditExprCompactansiWlBgPretty ansiWlBgExpransiWlBgEditExpransiWlBgEditExprCompact ediffGoldenediffEqEditTreeEditNodetreeDiff$fShowEditTreebaseGHC.Showshowunconcat stringToExprcontainers-0.6.0.1 Data.TreeTree