Advertisement
Guest User

Untitled

a guest
Jul 24th, 2016
67
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.30 KB | None | 0 0
  1. {- |
  2.  
  3. At the ZuriHac 2016 I worked on the new parsec-based parser for the *.cabal files.
  4. The obvious test case is to compare new and old parser results for all of Hackage.
  5. Traversing the Hackage is quite trivial. The difficult part is inspecting
  6. the result 'GenericPackageDescription's to spot the difference.
  7.  
  8. In the same event, Andres Löh showed his library @generics-sop@. Obvious choice
  9. to quickly put something together for the repetetive task. After all you can
  10. compare records field-wise. And if sum constructors are different, that's
  11. enough for our case as well!
  12.  
  13. Generic programming ftw.
  14. -}
  15.  
  16. {-# LANGUAGE DataKinds #-}
  17. {-# LANGUAGE DefaultSignatures #-}
  18. {-# LANGUAGE FlexibleContexts #-}
  19. {-# LANGUAGE MultiParamTypeClasses #-}
  20. {-# LANGUAGE PolyKinds #-}
  21. {-# LANGUAGE ScopedTypeVariables #-}
  22. {-# LANGUAGE TemplateHaskell #-}
  23. {-# LANGUAGE TypeFamilies #-}
  24. module SopDiff where
  25.  
  26. import Control.Applicative (liftA2)
  27. import Data.Foldable (traverse_)
  28. import Data.List (intercalate)
  29. import Generics.SOP
  30. import Generics.SOP.TH
  31.  
  32. -- | Because @'Data.Proxy.Proxy' :: 'Data.Proxy.Proxy' a@ is so long.
  33. data P a = P
  34.  
  35. -------------------------------------------------------------------------------
  36. -- Structure diffs
  37. -------------------------------------------------------------------------------
  38.  
  39. -- | Each thunk has a path, removed and added "stuff"
  40. data DiffThunk = DiffThunk { dtPath :: [String], dtA :: String, dtB :: String }
  41. deriving Show
  42.  
  43. -- | Diff result is a collection of thunks
  44. data DiffResult = DiffResult [DiffThunk]
  45. deriving Show
  46.  
  47. prefixThunk :: String -> DiffThunk -> DiffThunk
  48. prefixThunk pfx (DiffThunk path a b) = DiffThunk (pfx : path) a b
  49.  
  50. prefixResult :: String -> DiffResult -> DiffResult
  51. prefixResult name (DiffResult thunks) = DiffResult $ map (prefixThunk name) thunks
  52.  
  53. -- | Pretty print a result
  54. prettyResultIO :: DiffResult -> IO ()
  55. prettyResultIO (DiffResult []) = putStrLn "Equal"
  56. prettyResultIO (DiffResult xs) = traverse_ p xs
  57. where
  58. p (DiffThunk paths a b) = do
  59. putStrLn $ intercalate " " paths ++ " : "
  60. putStrLn $ "- " ++ a
  61. putStrLn $ "+ " ++ b
  62.  
  63. -- | We can join diff results
  64. instance Monoid DiffResult where
  65. mempty = DiffResult mempty
  66. mappend (DiffResult x) (DiffResult y) = DiffResult (mappend x y)
  67.  
  68. -- | And we have a class for things we can diff
  69. class Diff a where
  70. diff :: a -> a -> DiffResult
  71. default diff
  72. :: (Generic a, HasDatatypeInfo a, All2 Diff (Code a))
  73. => a -> a -> DiffResult
  74. diff = gdiff
  75.  
  76. -- | And generic implementation!
  77. gdiff :: forall a. (Generic a, HasDatatypeInfo a, All2 Diff (Code a)) => a -> a -> DiffResult
  78. gdiff x y = gdiffS (constructorInfo (P :: P a)) (unSOP $ from x) (unSOP $ from y)
  79.  
  80. gdiffS :: All2 Diff xss => NP ConstructorInfo xss -> NS (NP I) xss -> NS (NP I) xss -> DiffResult
  81. gdiffS (c :* _) (Z xs) (Z ys) = mconcat $ hcollapse $ hczipWith3 (P :: P Diff) f (fieldNames c) xs ys
  82. where
  83. f :: Diff a => K FieldName a -> I a -> I a -> K DiffResult a
  84. f (K fieldName) x y = K . prefixResult fieldName . unI $ liftA2 diff x y
  85. gdiffS (_ :* cs) (S xss) (S yss) = gdiffS cs xss yss
  86. gdiffS cs xs ys = DiffResult [DiffThunk [] (constructorNameOf cs xs) (constructorNameOf cs ys)]
  87.  
  88. eqDiff :: (Eq a, Show a) => a -> a -> DiffResult
  89. eqDiff x y
  90. | x == y = DiffResult []
  91. | otherwise = DiffResult [DiffThunk [] (show x) (show y)]
  92.  
  93. instance Diff Char where diff = eqDiff
  94. instance Diff Bool
  95. instance Diff a => Diff (Maybe a)
  96. instance Diff Int where diff = eqDiff
  97.  
  98. -- | This is terrible instance. Works for strings well enough though.
  99. instance (Show a, Eq a) => Diff [a] where diff = eqDiff
  100. --instance Diff a => Diff [a]
  101.  
  102. -------------------------------------------------------------------------------
  103. -- SOP helpers
  104. -------------------------------------------------------------------------------
  105.  
  106. constructorInfo :: (HasDatatypeInfo a, xss ~ Code a) => proxy a -> NP ConstructorInfo xss
  107. constructorInfo p = case datatypeInfo p of
  108. ADT _ _ cs -> cs
  109. Newtype _ _ c -> c :* Nil
  110.  
  111. constructorNameOf :: NP ConstructorInfo xss -> NS f xss -> ConstructorName
  112. constructorNameOf (c :* _) (Z _) = constructorName c
  113. constructorNameOf (_ :* cs) (S xs) = constructorNameOf cs xs
  114.  
  115. constructorName :: ConstructorInfo xs -> ConstructorName
  116. constructorName (Constructor name) = name
  117. constructorName (Infix name _ _) = name
  118. constructorName (Record name _) = name
  119.  
  120. -- | This is a little lie.
  121. fieldNames :: ConstructorInfo xs -> NP (K FieldName) xs
  122. fieldNames (Constructor name) = hpure (K name)
  123. fieldNames (Infix name _ _) = K ("-(" ++ name ++ ")") :* K ("(" ++ name ++ ")-") :* Nil
  124. fieldNames (Record _ fis) = hmap (\(FieldInfo fn) -> K fn) fis
  125.  
  126. -------------------------------------------------------------------------------
  127. -- Prelude examples
  128. -------------------------------------------------------------------------------
  129.  
  130. {-
  131.  
  132. λ *SopDiff > prettyResultIO $ diff (Just True) (Just False)
  133. Just :
  134. - True
  135. + False
  136. λ *SopDiff > prettyResultIO $ diff True True
  137. Equal
  138. λ *SopDiff > prettyResultIO $ diff True False
  139. :
  140. - True
  141. + False
  142. λ *SopDiff > prettyResultIO $ diff (Just True) (Just False)
  143. Just :
  144. - True
  145. + False
  146. λ *SopDiff > prettyResultIO $ diff (Just True) Nothing
  147. :
  148. - Just
  149. + Nothing
  150. λ *SopDiff > prettyResultIO $ diff (Just (Just True)) (Just (Just False))
  151. Just Just :
  152. - True
  153. + False
  154.  
  155. -}
  156.  
  157. {- The list doesn't work as well, as it cancels on the first constructor.
  158.  
  159. λ *SopDiff > prettyResultIO $ diff "foo" "food"
  160. :
  161. - "foo"
  162. + "food"
  163. λ *SopDiff > prettyResultIO $ gdiff "foo" "food"
  164. (:)- :
  165. - "oo"
  166. + "ood"
  167.  
  168. -- With commented out Diff a => Diff [a]
  169. λ *SopDiff > prettyResultIO $ gdiff "foo" "food"
  170. (:)- (:)- (:)- :
  171. - []
  172. + :
  173. -}
  174.  
  175. -------------------------------------------------------------------------------
  176. -- Examples
  177. -------------------------------------------------------------------------------
  178.  
  179. data Ex
  180. = Foo Int
  181. | Bar Ex2
  182. deriving (Show)
  183.  
  184. data Ex2 = Ex2
  185. { exName :: String
  186. , exDone :: Bool
  187. }
  188. deriving (Show)
  189.  
  190. deriveGeneric ''Ex
  191. deriveGeneric ''Ex2
  192.  
  193. instance Diff Ex
  194. instance Diff Ex2
  195.  
  196. {-
  197.  
  198. λ *SopDiff > prettyResultIO $ diff (Foo 1) (Foo 1)
  199. Equal
  200. λ *SopDiff > prettyResultIO $ diff (Foo 1) (Bar $ Ex2 "bar" True)
  201. :
  202. - Foo
  203. + Bar
  204. λ *SopDiff > prettyResultIO $ diff (Bar $ Ex2 "barr" False) (Bar $ Ex2 "bar" True)
  205. Bar exName :
  206. - "barr"
  207. + "bar"
  208. Bar exDone :
  209. - False
  210. + True
  211.  
  212. -}
  213.  
  214. Bar exDone :
  215. - False
  216. + True
  217.  
  218. -}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement