h*'%1      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~0.10.0 Safe-Inferredquickcheck-state-machineList edit operationsThe  constructor is redundant, but it let us spot a recursion point when performing tree diffs.quickcheck-state-machineinsertquickcheck-state-machinedeletequickcheck-state-machinecopy unchangedquickcheck-state-machineswap, i.e. delete + insertquickcheck-state-machineList difference.diffBy (==) "hello" "world"9[Swp 'h' 'w',Swp 'e' 'o',Swp 'l' 'r',Cpy 'l',Swp 'o' 'd']diffBy (==) "kitten" "sitting"[Swp 'k' 's',Cpy 'i',Cpy 't',Cpy 't',Swp 'e' 'i',Cpy 'n',Ins 'g']\xs ys -> length (diffBy (==) xs ys) >= max (length xs) (length (ys :: String))\xs ys -> length (diffBy (==) xs ys) <= length xs + length (ys :: String)Note: currently this has O(n*m) memory requirements, for the sake of more obviously correct implementation. Safe-InferredE quickcheck-state-machineType used in the result of ediff. quickcheck-state-machineunchanged tree quickcheck-state-machine"A untyped Haskell-like expression."Having richer structure than just Tree allows to have richer diffs. quickcheck-state-machine applicationquickcheck-state-machinerecord constructorquickcheck-state-machinelist constructorquickcheck-state-machine"Record field name is a string too.quickcheck-state-machineConstructor name is a stringquickcheck-state-machine Diff two  .For examples see ediff in Data.TreeDiff.Class.       Safe-Inferred )*/=Vquickcheck-state-machine 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" []]]quickcheck-state-machineDifference between two  values.'let x = (1, Just 2) :: (Int, Maybe Int)let y = (1, Nothing)prettyEditExpr (ediff x y)__ 1 -(Just 2) +Nothingdata Foo = Foo { fooInt :: Either Char Int, fooBool :: [Maybe Bool], fooString :: String } deriving (Eq, Generic)instance ToExpr FooprettyEditExpr $ ediff (Foo (Right 2) [Just True] "fo") (Foo (Right 3) [Just True] "fo")Foo {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"}quickcheck-state-machineCompare different types.Note:? Use with care as you can end up comparing apples with oranges. xs == concat (unconcat uncons xs)quickcheck-state-machinetraverse_ (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"])instance ToExpr SBS.ShortByteString where toExpr = stringToExpr "mconcat" . bsUnconcat BS.null BS.elemIndex BS.splitAt . SBS.fromShort!quickcheck-state-machine+prettyExpr $ toExpr (123.456 :: Scientific)scientific 123456 `-3`$instance ToExpr Sci.Scientific where toExpr s = App "scientific" [ toExpr $ Sci.coefficient s, toExpr $ Sci.base10Exponent s ]prettyExpr $ toExpr UUID.nil+UUID "00000000-0000-0000-0000-000000000000"instance ToExpr UUID.UUID where6 toExpr u = App "UUID" [ toExpr $ UUID.toString u ]"quickcheck-state-machinetraverse_ (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"]#quickcheck-state-machinetraverse_ (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"]%quickcheck-state-machine-prettyExpr $ toExpr $ ModifiedJulianDay 58014Day "2017-09-18"&quickcheck-state-machinetraverse_ (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"]'quickcheck-state-machinetraverse_ (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"];quickcheck-state-machine"prettyExpr $ toExpr $ Identity 'a' Identity 'a'=quickcheck-state-machine(prettyExpr $ toExpr (3 % 12 :: Rational)_%_ 1 4Equickcheck-state-machineprettyExpr $ 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"]quickcheck-state-machinename of concat Safe-InferredYquickcheck-state-machineBecause we don't want to commit to single pretty printing library, we use explicit dictionary.dquickcheck-state-machine 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 "_,_"`_,_`equickcheck-state-machinePretty print an  + using explicit pretty-printing dictionary.fquickcheck-state-machinePretty print an  + using explicit pretty-printing dictionary.gquickcheck-state-machineLike f) but print unchanged parts only shallowlyhquickcheck-state-machineY via pretty library.iquickcheck-state-machine Pretty print   using pretty.;prettyExpr $ Rec "ex" (Map.fromList [("[]", App "bar" [])])ex {`[]` = bar}jquickcheck-state-machine Pretty print   using pretty.kquickcheck-state-machineCompact j.lquickcheck-state-machineY via  prettyprinter library (with colors).mquickcheck-state-machine Pretty print   using  prettyprinter.nquickcheck-state-machine Pretty print   using  prettyprinter.oquickcheck-state-machineCompact npquickcheck-state-machineLike l but color the background.qquickcheck-state-machine Pretty print   using  prettyprinter.rquickcheck-state-machine Pretty print   using  prettyprinter.squickcheck-state-machineCompact r.YZ[\]^_`abcefghijklmnopqrsdYZ[\]^_`abcefghijklmnopqrsd Safe-Inferred6 Safe-InferredD3   YZ[\]^_`abcefghijklmnopqrsd Safe-Inferred%~quickcheck-state-machineType 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.quickcheck-state-machineA 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.ExamplesLet's start from simple tree. We pretty print them as s-expressions.let 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:let 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)If 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:let 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[\]^_`abbcdefghijklmnopqrstuvwxyz{|}~6quickcheck-state-machine-0.10.0-LD6wdNFZV0PB1pPQf50kuDTest.StateMachine.TreeDiff.ListTest.StateMachine.TreeDiff.Expr Test.StateMachine.TreeDiff.Class!Test.StateMachine.TreeDiff.Pretty"Test.StateMachine.TreeDiff.DiffingTest.StateMachine.TreeDiff.Treequickcheck-state-machineTest.StateMachine.TreeDiffEditInsDelCpySwpdiffBy $fShowEditEditExprEditAppEditRecEditLstEditExpExprAppRecLst FieldNameConstructorNameexprDiff$fArbitraryExpr$fShowEditExpr$fEqExpr $fShowExprToExprtoExpr listToExprediffediff'defaultExprViaShow sopToExpr$fToExprVector$fToExprVector0$fToExprVector1$fToExprVector2$fToExprByteString$fToExprByteString0$fToExprUTCTime $fToExprDay $fToExprText $fToExprText0 $fToExprSeq$fToExprIntSet$fToExprIntMap $fToExprSet $fToExprMap $fToExprTree $fToExprLast $fToExprFirst $fToExprMax $fToExprMin $fToExprLast0$fToExprFirst0$fToExprProduct $fToExprSum $fToExprDual $fToExprVoid$fToExprNonEmpty$fToExprZipList $fToExprConst$fToExprIdentity $fToExprFixed $fToExprRatio$fToExpr(,,,,) $fToExpr(,,,) $fToExpr(,,) $fToExpr(,) $fToExprList$fToExprEither $fToExprMaybe $fToExprChar $fToExprProxy$fToExprWord64$fToExprWord32$fToExprWord16 $fToExprWord8 $fToExprWord $fToExprInt64 $fToExprInt32 $fToExprInt16 $fToExprInt8 $fToExprInt$fToExprDouble $fToExprFloat$fToExprNatural$fToExprInteger$fToExprOrdering $fToExprBool $fToExpr() $fToExprExprPrettyppConppRecppLstppCpyppInsppDelppSepppParensppHang escapeNameppExpr ppEditExprppEditExprCompact prettyPretty prettyExprprettyEditExprprettyEditExprCompact ansiWlPretty ansiWlExpransiWlEditExpransiWlEditExprCompactansiWlBgPretty ansiWlBgExpransiWlBgEditExpransiWlBgEditExprCompact $fToExprModel $fToExprRefss$fToExprMockHandleN$fToExprOpaque$fToExprReference$fToExprConcrete$fToExprSymbolic $fCanDiffx $fToExprRefs $fToExprVarEditTreeEditNodetreeDiff$fShowEditTreebaseGHC.Showshowunconcat bsUnconcat stringToExprcontainers-0.6.7 Data.TreeTree