Advertisement
Guest User

Untitled

a guest
May 26th, 2017
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.35 KB | None | 0 0
  1. {-# LANGUAGE DataKinds #-}
  2. {-# LANGUAGE FlexibleContexts #-}
  3. {-# LANGUAGE PolyKinds #-}
  4. {-# LANGUAGE ScopedTypeVariables #-}
  5. {-# LANGUAGE StandaloneDeriving #-}
  6. {-# LANGUAGE TemplateHaskell #-}
  7. {-# LANGUAGE TypeApplications #-}
  8. {-# LANGUAGE TypeFamilies #-}
  9. module CustomShowEnum where
  10.  
  11. import Data.Aeson
  12. import Data.Aeson.Types
  13. import Data.Maybe
  14. import Generics.SOP
  15. import Generics.SOP.NS
  16. import Generics.SOP.TH
  17. import Text.Read
  18.  
  19. -- | Computes a product (a list with a statically known
  20. -- number of elements) of all the constructor names.
  21. --
  22. -- Example:
  23. --
  24. -- >>> conNames (Proxy @OrderType)
  25. -- K "Confirmed" :* K "AwaitingShipping" :* K "Shipped" :* Nil
  26. --
  27. conNames ::
  28. forall a proxy .
  29. (Generic a, HasDatatypeInfo a)
  30. => proxy a -> NP (K String) (Code a)
  31. conNames _ =
  32. hmap
  33. (K . constructorName)
  34. (constructorInfo (datatypeInfo (Proxy @a)))
  35.  
  36. -- | Computes the name of the outermost constructor
  37. -- of a given value.
  38. --
  39. -- Examples:
  40. --
  41. -- >>> conName Confirmed
  42. -- "Confirmed"
  43. -- >>> conName (Just 3)
  44. -- "Just"
  45. -- >>> conName [1,2,3]
  46. -- ":"
  47. --
  48. conName ::
  49. forall a .
  50. (Generic a, HasDatatypeInfo a)
  51. => a -> String
  52. conName x =
  53. hcollapse
  54. (hzipWith
  55. const
  56. (conNames (Proxy @a))
  57. (unSOP (from x))
  58. )
  59.  
  60. -- | Computes a product (a list with a statically known
  61. -- number of elements) of all the values of an enumeration
  62. -- type.
  63. --
  64. -- Examples:
  65. --
  66. -- >>> enum @Bool
  67. -- K False :* K True :* Nil
  68. -- >>> enum @Ordering
  69. -- K LT :* K EQ :* K GT :* Nil
  70. --
  71. -- With the derived 'Show' instance for 'OrderType':
  72. --
  73. -- >>> enum @OrderType
  74. -- K Confirmed :* K AwaitingShipping :* K Shipped :* Nil
  75. --
  76. -- With the custom 'Show' instance for 'OrderType':
  77. --
  78. -- >>> enum @OrderType
  79. -- K confirmed :* K awaiting_shipping :* K shipped :* Nil
  80. --
  81. enum ::
  82. forall a .
  83. (Generic a, HasDatatypeInfo a, All ((~) '[]) (Code a))
  84. => NP (K a) (Code a)
  85. enum =
  86. hmap
  87. (mapKK to)
  88. (apInjs'_POP (POP (hcpure (Proxy @((~) '[])) Nil)))
  89.  
  90. -- | Computes a lookup table mapping the string constructor
  91. -- names to the actual values of an enum type.
  92. --
  93. -- Examples:
  94. --
  95. -- >>> conTable @Bool
  96. -- [("False", False), ("True", True)]
  97. -- >>> conTable @Ordering
  98. -- [("LT", LT), ("EQ", EQ), ("GT", GT)]
  99. --
  100. -- With the derived 'Show' instance for 'OrderType':
  101. --
  102. -- >>> conTable @OrderType
  103. -- [("Confirmed", Confirmed), ("AwaitingShipping", AwaitingShipping), ("Shipped", Shipped)]
  104. --
  105. -- With the custom 'Show' instance for 'OrderType':
  106. --
  107. -- >>> conTable @OrderType
  108. -- [("Confirmed", confirmed), ("AwaitingShipping", awaiting_shipping), ("Shipped", shipped)]
  109. --
  110. conTable ::
  111. forall a .
  112. (Generic a, HasDatatypeInfo a, All ((~) '[]) (Code a))
  113. => [(String, a)]
  114. conTable =
  115. hcollapse
  116. (hzipWith
  117. (mapKKK (,))
  118. (conNames (Proxy @a))
  119. enum
  120. )
  121.  
  122. -- | Custom show function for enum types. Takes a transformation
  123. -- function that is applied to the constructor name.
  124. --
  125. -- Examples:
  126. --
  127. -- >>> customShowEnum id AwaitingShipping
  128. -- "AwaitingShipping"
  129. -- >>> customShowEnum reverse Confirmed
  130. -- "demrifnoC"
  131. -- >>> customShowEnum (camelTo2 '_') AwaitingShipping
  132. -- "awaiting_shipping"
  133. --
  134. customShowEnum ::
  135. forall a .
  136. (Generic a, HasDatatypeInfo a, All ((~) '[]) (Code a))
  137. => (String -> String)
  138. -> a -> String
  139. customShowEnum f = f . conName
  140.  
  141. -- | Custom read function for enum types. Takes a transformation
  142. -- function that is applied to the constructor names.
  143. --
  144. -- Proceeds by first building an adjusted lookup table mapping
  145. -- the transformed names to the values, and then trying to find
  146. -- the given string in that lookup table.
  147. --
  148. -- Examples (with the derived 'Show' instance for 'OrderType'):
  149. --
  150. -- >>> readPrec_to_S (customReadEnum @OrderType id) 0 "AwaitingShipping"
  151. -- [(AwaitingShipping, "")]
  152. -- >>> readPrec_to_S (customReadEnum @OrderType (camelTo2 '_')) 0 "AwaitingShipping"
  153. -- []
  154. -- >>> readPrec_to_S (customReadEnum @OrderType (camelTo2 '_')) 0 "awaiting_shipping"
  155. -- [(AwaitingShipping, "")]
  156. -- >>> readPrec_to_S (customReadEnum @OrderType (camelTo2 '_')) 0 " ( awaiting_shipping) "
  157. -- [(AwaitingShipping, " ")]
  158. --
  159. customReadEnum ::
  160. forall a .
  161. (Generic a, HasDatatypeInfo a, All ((~) '[]) (Code a))
  162. => (String -> String)
  163. -> ReadPrec a
  164. customReadEnum f =
  165. let
  166. adjustedTable :: [(Lexeme, a)]
  167. adjustedTable = map (\ (n, x) -> (Ident (f n), x)) conTable
  168. in
  169. parens $ do
  170. n <- lexP
  171. maybe pfail return (lookup n adjustedTable)
  172.  
  173. -- | Example datatype with derived instances of the generic-sop
  174. -- 'Generic' and 'HasDatatypeInfo' classes.
  175. data OrderType = Confirmed | AwaitingShipping | Shipped
  176. deriveGeneric ''OrderType
  177.  
  178. -- | Default 'Show' instance for 'OrderType'.
  179. deriving instance Show OrderType
  180.  
  181. -- | Custom instance for 'Show', applying transformation function.
  182. -- instance Show OrderType where
  183. -- show = customShowEnum (camelTo2 '_')
  184.  
  185. -- | Custom instance for 'Read', applying transformation function.
  186. -- instance Read OrderType where
  187. -- readPrec = customReadEnum (camelTo2 '_')
  188.  
  189. class ToString a where
  190. toString :: a -> String
  191.  
  192. class FromString a where
  193. fromString :: String -> Maybe a
  194.  
  195. instance ToString OrderType where
  196. toString = customShowEnum (camelTo2 '_')
  197.  
  198. customFromString ::
  199. forall a .
  200. (Generic a, HasDatatypeInfo a, All ((~) '[]) (Code a))
  201. => (String -> String)
  202. -> String -> Maybe a
  203. customFromString f x =
  204. case readPrec_to_S (customReadEnum f) 0 x of
  205. [(r, "")] -> Just r
  206. _ -> Nothing
  207.  
  208. instance FromString OrderType where
  209. fromString = customFromString (camelTo2 '_')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement