y      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ NoneTList edit operationsThe \ constructor is redundant, but it let us spot a recursion point when performing tree diffs.insertdeletecopy unchangedswap, i.e. delete + insertList 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.NoneType 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. A 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)    None Type used in the result of ediff.unchanged tree"A untyped Haskell-like expression."Having richer structure than just Tree allows to have richer diffs. applicationrecord constructorlist constructor"Record field name is a string too.Constructor name is a string Diff two .For examples see ediff in Data.TreeDiff.Class.      NoneT Parsers for  using parsers type-classes.AYou can use this with your parser-combinator library of choice: parsec,  attoparsec, trifecta...  None _Because we don't want to commit to single pretty printing library, we use explicit dictionary.' 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 "_,_"`_,_`(Pretty print an + using explicit pretty-printing dictionary.)Pretty print an   + using explicit pretty-printing dictionary.* via pretty library.+ Pretty print  using pretty.;prettyExpr $ Rec "ex" (Map.fromList [("[]", App "bar" [])])ex {`[]` = bar}, Pretty print    using pretty.- via ansi-wl-pprint library (with colors).. Pretty print  using ansi-wl-pprint./ Pretty print    using ansi-wl-pprint.0Like - but color the background.1 Pretty print  using ansi-wl-pprint.2 Pretty print    using ansi-wl-pprint. !"#$%&'()*+,-./012 !"#$%&'()*+,-./012 !"#$%&()*+,-./012'  !"#$%&'()*+,-./012None %&*6:OT34B 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" []]]6Difference between two 3 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"}7Compare 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]88An alternative implementation for literal types. We use  representation of them.9LprettyExpr $ sopToExpr (gdatatypeInfo (Proxy :: Proxy String)) (gfrom "foo") _:_ 'f' "oo" Split on '\n'.(\xs -> xs == concat (unconcat uncons xs)CprettyExpr $ toExpr UUID.nil+UUID "00000000-0000-0000-0000-000000000000"D+prettyExpr $ toExpr (123.456 :: Scientific)scientific 123456 `-3`Egtraverse_ (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"]Fhtraverse_ (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"]H-prettyExpr $ toExpr $ ModifiedJulianDay 58014Day "2017-09-18"Ietraverse_ (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"]Jftraverse_ (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"]_"prettyExpr $ toExpr $ Identity 'a' Identity 'a'a(prettyExpr $ toExpr (3 % 12 :: Rational)_%_ 1 4iprettyExpr $ 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"]N3456789name of concat:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|345678967344589L34456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{| None0   !"#$%&'()*+,-./0123456789None}Make a golden tests.}K is testing framework agnostic, thus the test framework looks intimdating.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 4A of a result. If values differ, the diff of two will be printed.See  >https://github.com/phadej/tree-diff/blob/master/tests/Tests.hs for a proper example.} goldenTest test namepath to "golden file" result value}}}None~ A variant of ===/, which outputs a diff when values are inequal.~~~~     !"#$%&&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~(tree-diff-0.0.0.1-318euvSBVM4JiQKZH5qLm8Data.TreeDiff.ListData.TreeDiff.TreeData.TreeDiff.ExprData.TreeDiff.ParserData.TreeDiff.PrettyData.TreeDiff.ClassData.TreeDiff.GoldenData.TreeDiff.QuickCheck Data.TreeDiffEditInsDelCpySwpdiffBy $fShowEditEditTreeEditNodetreeDiff$fShowEditTreeEditExprEditAppEditRecEditLstEditExpExprAppRecLst FieldNameConstructorNameexprDiff$fArbitraryExpr$fEqExpr $fShowExpr$fShowEditExpr exprParserPrettyppConppRecppLstppCpyppInsppDelppSepppParensppHang escapeNameppExpr ppEditExpr prettyPretty prettyExprprettyEditExpr ansiWlPretty ansiWlExpransiWlEditExpransiWlBgPretty ansiWlBgExpransiWlBgEditExprToExprtoExpr listToExprediffediff'defaultExprViaShow sopToExpr $fToExprValue$fToExprHashSet$fToExprHashMap$fToExprHashed$fToExprTagged$fToExprVector$fToExprVector0$fToExprVector1$fToExprVector2 $fToExprUUID$fToExprScientific$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 ediffGoldenediffEqbimapcontainers-0.5.7.1 Data.TreeTree treeToEditarbNamelstPapprecPfieldPlitPrecPlitP'identPstringPatomPvalidvalid'ppExpr'baseGHC.Showshowunconcat sopNPToExpr stringToExpr bsUnconcat