{-# LANGUAGE DataKinds #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE Rank2Types #-} -- | Naive implementation of data-prism approach. module Toml.Prism ( -- * Prism idea Prism (..) , match , mkAnyValuePrism -- * Value prisms , _Bool , _Integer , _Double , _Text , _Array -- * Useful utility functions , unsafeArray ) where import Control.Monad ((>=>)) import Data.Text (Text) import Toml.Type (AnyValue (..), TValue (TArray), Value (..), liftMatch, matchArray, matchBool, matchDouble, matchInteger, matchText, reifyAnyValues) import qualified Control.Category as Cat ---------------------------------------------------------------------------- -- Prism concepts and ideas ---------------------------------------------------------------------------- {- | Implementation of prism idea using simple data prism approach. Single value of type 'Prism' has two capabilities: 1. 'preview': first-class pattern-matching (deconstruct @object@ to possible @field@). 2. 'review': constructor of @object@ from @field@. -} data Prism object field = Prism { preview :: object -> Maybe field , review :: field -> object } instance Cat.Category Prism where id :: Prism object object id = Prism { preview = Just, review = id } (.) :: Prism field subfield -> Prism object field -> Prism object subfield fieldPrism . objectPrism = Prism { preview = preview objectPrism >=> preview fieldPrism , review = review objectPrism . review fieldPrism } -- | Creates prism for 'AnyValue'. mkAnyValuePrism :: (forall t . Value t -> Maybe a) -> (a -> Value tag) -> Prism AnyValue a mkAnyValuePrism matchValue toValue = Prism { review = AnyValue . toValue , preview = \(AnyValue value) -> matchValue value } -- | Allows to match against given 'Value' using provided prism for 'AnyValue'. match :: Prism AnyValue a -> Value t -> Maybe a match = liftMatch . preview ---------------------------------------------------------------------------- -- Prisms for value ---------------------------------------------------------------------------- -- | 'Bool' prism for 'AnyValue'. Usually used with 'arrayOf' combinator. _Bool :: Prism AnyValue Bool _Bool = mkAnyValuePrism matchBool Bool -- | 'Integer' prism for 'AnyValue'. Usually used with 'arrayOf' combinator. _Integer :: Prism AnyValue Integer _Integer = mkAnyValuePrism matchInteger Integer -- | 'Double' prism for 'AnyValue'. Usually used with 'arrayOf' combinator. _Double :: Prism AnyValue Double _Double = mkAnyValuePrism matchDouble Double -- | 'Text' prism for 'AnyValue'. Usually used with 'arrayOf' combinator. _Text :: Prism AnyValue Text _Text = mkAnyValuePrism matchText Text -- | 'Array' prism for 'AnyValue'. Usually used with 'arrayOf' combinator. _Array :: Prism AnyValue a -> Prism AnyValue [a] _Array elementPrism = mkAnyValuePrism (matchArray $ preview elementPrism) (unsafeArray . map (review elementPrism)) -- TODO: put this in 'Toml.Type' module? -- | Unsafe function for creating 'Array' from list of 'AnyValue'. This function -- assumes that every element in this list has the same type. Usually used when -- list of 'AnyValue' is created using single prism. unsafeArray :: [AnyValue] -> Value 'TArray unsafeArray [] = Array [] unsafeArray (AnyValue x : xs) = case reifyAnyValues x xs of Left err -> error $ "Can't create Array from list AnyValues: " ++ show err Right vals -> Array (x : vals)