kdl-hs 0.2.0 → 0.2.1
raw patch · 7 files changed
+212/−5 lines, 7 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ KDL.Applicative: (<*>) :: Applicative f => f (a -> b) -> f a -> f b
+ KDL.Applicative: (>>) :: Monad m => m a -> m b -> m b
+ KDL.Applicative: (>>=) :: NoBind a => a
+ KDL.Applicative: fmap :: Functor f => (a -> b) -> f a -> f b
+ KDL.Applicative: infixl 1 >>
+ KDL.Applicative: infixl 4 <*>
+ KDL.Applicative: instance GHC.TypeError.Unsatisfiable ('GHC.TypeError.Text ">>= is not allowed in a KDL.do block") => KDL.Applicative.NoBind a
+ KDL.Applicative: pure :: Applicative f => a -> f a
+ KDL.Applicative: return :: Monad m => a -> m a
Files
- CHANGELOG.md +5/−0
- kdl-hs.cabal +3/−1
- src/KDL.hs +3/−2
- src/KDL/Applicative.hs +82/−0
- src/KDL/Arrow.hs +7/−0
- src/KDL/Decoder/Internal/Decoder.hs +3/−2
- test/KDL/ApplicativeSpec.hs +109/−0
CHANGELOG.md view
@@ -1,3 +1,8 @@+## v0.2.1++* Add `KDL.Applicative`+* Re-export `Control.Arrow` and `Control.Category` from `KDL.Arrow`, for convenience+ ## v0.2.0 * Remove monad `Decoder` newtype, just give the `Monad` instance to the canonical `Decoder` type and use the same `Decoder` type everywhere
kdl-hs.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: kdl-hs-version: 0.2.0+version: 0.2.1 synopsis: KDL language parser and API description: KDL language parser and API. homepage: https://github.com/brandonchinn178/kdl-hs#readme@@ -24,6 +24,7 @@ hs-source-dirs: src exposed-modules: KDL+ KDL.Applicative KDL.Arrow KDL.Decoder KDL.Decoder.Arrow@@ -59,6 +60,7 @@ hs-source-dirs: test main-is: Main.hs other-modules:+ KDL.ApplicativeSpec KDL.ParserSpec KDL.Decoder.ArrowSpec KDL.Decoder.MonadSpec
src/KDL.hs view
@@ -4,8 +4,8 @@ > import KDL qualified This provides a Monad interface for decoding KDL files, which is sufficient for-most cases. You may wish to use "KDL.Arrow" if you would like to-statically analyze a decoder's schema, e.g. to generate documentation.+most cases. You may wish to use "KDL.Applicative" or "KDL.Arrow" if you would+like to statically analyze a decoder's schema, e.g. to generate documentation. = Quickstart @@ -65,6 +65,7 @@ @ -} module KDL (+ -- * Re-exports module X, ) where
+ src/KDL/Applicative.hs view
@@ -0,0 +1,82 @@+{-# LANGUAGE DataKinds #-}++{-|+This module is intended to be imported qualified as:++> import KDL.Applicative qualified as KDL++This module is equivalent to "KDL", except when @ApplicativeDo@ and+@QualifiedDo@ are enabled, @KDL.do@ will ensure you don't accidentally use+monadic operations.++The Applicative decoder can do most of the things the Arrow decoder can do,+except run different decoders based on a previously decoded result. If you+need to do that, either use "KDL.Arrow", or follow this example:++@+# Example KDL config++rules {+ - a+ - b {+ foo 123 # only allowed in b, not a+ }+}+@++@+{\-# LANGUAGE ApplicativeDo #-\}+{\-# LANGUAGE QualifiedDo #-\}++import KDL.Arrow ((>>>), (|||))+import KDL.Arrow qualified++decoder = KDL.do+ rules <-+ KDL.dashNodesWith "rules" $+ (toEither \<$> KDL.arg) >>> fromEither+ pure rules+ where+ toEither = \case+ "a" -> Left $ ()+ "b" -> Right . Left $ ()+ name -> Right . Right $ "Invalid rule: " <> name+ fromEither =+ -- "a"+ pure RuleA |||+ -- "b"+ (RuleB \<$> KDL.children (KDL.argAt "foo")) |||+ -- else+ KDL.Arrow.fail+@+-}+module KDL.Applicative (+ -- * QualifiedDo+ Prelude.fmap,+ Prelude.pure,+ Prelude.return,+ (Prelude.<*>),+ (Prelude.>>),+ (>>=),++ -- * Re-exports+ module X,+) where++import GHC.TypeError qualified as GHC+import KDL.Decoder as X+import KDL.Parser as X+import KDL.Render as X+import KDL.Types as X+import Prelude hiding ((>>=))++class NoBind a where+ -- | Gives a compile-time error if used.+ --+ -- It seems like QualifiedDo still needs a definition for this, even with+ -- ApplicativeDo enabled.+ --+ -- https://discourse.haskell.org/t/qualifieddo-applicativedo-still-requires/13491+ (>>=) :: a+instance (GHC.Unsatisfiable (GHC.Text ">>= is not allowed in a KDL.do block")) => NoBind a where+ (>>=) = GHC.unsatisfiable
src/KDL/Arrow.hs view
@@ -68,9 +68,16 @@ @ -} module KDL.Arrow (+ -- * KDL re-exports module X,++ -- * base re-reexports+ module Control.Arrow,+ module Control.Category, ) where +import Control.Arrow+import Control.Category import KDL.Decoder.Arrow as X import KDL.Decoder.Schema as X import KDL.Parser as X
src/KDL/Decoder/Internal/Decoder.hs view
@@ -144,15 +144,16 @@ in go many (DecodeArrow sch run) = some (DecodeArrow sch run) <|> pure [] -type Decoder o a = DecodeArrow o () a- -- | Eliminates all schema information; avoid whenever possible. instance Monad (DecodeArrow o a) where+ return = pure DecodeArrow _ run1 >>= k = DecodeArrow SchemaUnknown $ \a -> do x <- run1 a let DecodeArrow _ run2 = k x run2 a++type Decoder o a = DecodeArrow o () a liftDecodeM :: (a -> DecodeM b) -> DecodeArrow o a b liftDecodeM f = DecodeArrow (SchemaAnd []) (Trans.lift . f)
+ test/KDL/ApplicativeSpec.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE DisambiguateRecordFields #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QualifiedDo #-}++module KDL.ApplicativeSpec (spec) where++import Data.Int (Int64)+import Data.Proxy (Proxy (..))+import Data.Text (Text)+import Data.Typeable (typeRep)+import KDL.Applicative qualified as KDL+import KDL.Arrow ((>>>), (|||))+import KDL.Arrow qualified+import Skeletest++spec :: Spec+spec = do+ describe "documentSchema" $ do+ it "gets the schema of a decoder" $ do+ let decoder = KDL.document $ KDL.do+ x <-+ KDL.nodeWith "foo" $+ fmap show . KDL.argWith $+ KDL.oneOf+ [ Left <$> KDL.valueDecoder @Bool+ , Right <$> KDL.valueDecoder @Text+ ]++ ys <- KDL.many $ KDL.nodeWith "bar" $ KDL.arg @String++ z <- (parseBazType <$> KDL.argAt @Text "baz_type") >>> KDL.argAtWith "baz" decodeBaz++ pure (x, ys, z)+ parseBazType = \case+ "int" -> Left $ ()+ "bool" -> Right . Left $ ()+ ty -> Right . Right $ "Invalid type: " <> ty+ decodeBaz =+ -- "int"+ KDL.valueDecoder @Int64+ -- "bool"+ ||| ((\b -> if b then 1 else 0) <$> KDL.valueDecoder @Bool)+ -- else+ ||| KDL.Arrow.fail+ expected =+ KDL.SchemaAnd+ [ KDL.SchemaOne . KDL.NodeNamed "foo" $+ KDL.TypedNodeSchema+ { typeHint = typeRep $ Proxy @String+ , validTypeAnns = []+ , nodeSchema =+ KDL.SchemaOne . KDL.NodeArg $+ KDL.TypedValueSchema+ { typeHint = typeRep $ Proxy @(Either Bool Text)+ , validTypeAnns = []+ , dataSchema =+ KDL.SchemaOr+ [ KDL.SchemaOne KDL.BoolSchema+ , KDL.SchemaOne KDL.TextSchema+ ]+ }+ }+ , KDL.SchemaOr+ [ KDL.SchemaSome . KDL.SchemaOne . KDL.NodeNamed "bar" $+ KDL.TypedNodeSchema+ { typeHint = typeRep $ Proxy @String+ , validTypeAnns = []+ , nodeSchema =+ KDL.SchemaOne . KDL.NodeArg $+ KDL.TypedValueSchema+ { typeHint = typeRep $ Proxy @String+ , validTypeAnns = ["string"]+ , dataSchema = KDL.SchemaOne KDL.TextSchema+ }+ }+ , KDL.SchemaAnd []+ ]+ , KDL.SchemaOne . KDL.NodeNamed "baz_type" $+ KDL.TypedNodeSchema+ { typeHint = typeRep $ Proxy @Text+ , validTypeAnns = []+ , nodeSchema =+ KDL.SchemaOne . KDL.NodeArg $+ KDL.TypedValueSchema+ { typeHint = typeRep $ Proxy @Text+ , validTypeAnns = ["text"]+ , dataSchema = KDL.SchemaOne KDL.TextSchema+ }+ }+ , KDL.SchemaOne . KDL.NodeNamed "baz" $+ KDL.TypedNodeSchema+ { typeHint = typeRep $ Proxy @Int64+ , validTypeAnns = []+ , nodeSchema =+ KDL.SchemaOne . KDL.NodeArg $+ KDL.TypedValueSchema+ { typeHint = typeRep $ Proxy @Int64+ , validTypeAnns = []+ , dataSchema =+ KDL.SchemaOr+ [ KDL.SchemaOne KDL.NumberSchema+ , KDL.SchemaOne KDL.BoolSchema+ ]+ }+ }+ ]+ KDL.documentSchema decoder `shouldBe` expected