prairie 0.1.0.0 → 0.1.1.0
raw patch · 6 files changed
+102/−23 lines, 6 filesdep ~template-haskellPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: template-haskell
API changes (from Hackage documentation)
- Prairie.Distributed: Distributed :: (forall x. () => Field rec x -> f x) -> Distributed (f :: Type -> Type) rec
- Prairie.Distributed: instance Prairie.Class.SymbolToField sym rec typ => GHC.Records.HasField sym (Prairie.Distributed.Distributed f rec) (f typ)
- Prairie.Distributed: newtype Distributed (f :: Type -> Type) rec
+ Prairie.Class: Distributed :: (forall x. () => Field rec x -> f x) -> Distributed (f :: Type -> Type) rec
+ Prairie.Class: newtype Distributed (f :: Type -> Type) rec
+ Prairie.Class: sequenceRecordA :: forall m (f :: Type -> Type). (Record rec, Applicative m) => Distributed (Compose m f) rec -> m (Distributed f rec)
+ Prairie.Distributed: instance Prairie.Class.SymbolToField sym rec typ => GHC.Records.HasField sym (Prairie.Class.Distributed f rec) (f typ)
+ Prairie.TH: compatConP' :: Name -> [Pat] -> Pat
Files
- ChangeLog.md +7/−0
- prairie.cabal +2/−2
- src/Prairie/Class.hs +38/−1
- src/Prairie/Distributed.hs +0/−20
- src/Prairie/TH.hs +42/−0
- test/Spec.hs +13/−0
ChangeLog.md view
@@ -1,5 +1,12 @@ # Changelog for prairie +## 0.1.1.0++- [#24](https://github.com/parsonsmatt/prairie/pull/24)+ - Introduce `sequenceRecordA`, for monadic tabulation of values, using `Distributed`.+- [#29](https://github.com/parsonsmatt/prairie/pull/29)+ - Bump `template-haskell` bound to 2.25+ ## 0.1.0.0 - [#22](https://github.com/parsonsmatt/prairie/pull/22)
prairie.cabal view
@@ -1,7 +1,7 @@ cabal-version: 1.12 name: prairie-version: 0.1.0.0+version: 0.1.1.0 description: Please see the README on GitHub at <https://github.com/parsonsmatt/prairie#readme> homepage: https://github.com/parsonsmatt/prairie#readme bug-reports: https://github.com/parsonsmatt/prairie/issues@@ -43,7 +43,7 @@ , containers , mtl , semigroupoids- , template-haskell >=2.15 && <2.24+ , template-haskell >=2.15 && <2.25 , text , foldable1-classes-compat
src/Prairie/Class.hs view
@@ -19,6 +19,7 @@ import Data.Aeson (FromJSON (..), ToJSON (..), withText) import Data.Constraint (Dict (..)) import Data.Functor.Apply (Apply (..))+import Data.Functor.Compose (Compose) import Data.Functor.Const (Const (..)) import Data.Functor.Identity (Identity (..)) import Data.Kind (Constraint, Type)@@ -30,7 +31,6 @@ import Data.Typeable (Typeable, eqT, (:~:) (..)) import GHC.OverloadedLabels (IsLabel (..)) import GHC.TypeLits (Symbol)- import Prairie.Internal (Lens', set, view) -- | Instances of this class have a datatype 'Field' which allow you to@@ -88,6 +88,10 @@ -- | Construct a 'Record' by providing an 'Applicative' action -- returning a value for each 'Field' on the 'Record'. --+ -- Valid input functions are written in the "function format" of the record, contrasting+ -- the @f@-wrapped "data format" of its return type: the @rec@ value as though it were+ -- written in Haskell record syntax (that does not support 'Applicative' actions).+ -- -- Example: -- -- @@@ -111,6 +115,19 @@ -- allows you to use 'Foldable1' and 'NonEmpty'. tabulateRecordApply :: (Apply m) => (forall ty. Field rec ty -> m ty) -> m rec + -- | Unwrap one 'Applicative' layer of a stack @m (f ty)@ in "function format".+ -- The resulting @m@-action returns the "function format" in @f@-actions,+ -- which can then be addressed directly later in the @m@ action without needing+ -- to tabulate the entire record right away. This is especially useful when the+ -- inner functor has side-effects worth avoiding, like the concatenating behaviour+ -- of @'Monoid' m => 'Const' m@ or the validation of 'Maybe' and @'Either' e@+ -- when the specific field's @m@ or @'Maybe' ty@/@'Either' e ty@ value is of interest+ -- prior to processing the record entirely through that functor.+ --+ -- @since 0.1.1.0+ sequenceRecordA+ :: (Applicative m) => Distributed (Compose m f) rec -> m (Distributed f rec)+ -- | Assign a 'Text' label for a record 'Field'. -- -- This allows 'Field's to be converted to 'Text', which is useful for@@ -124,6 +141,26 @@ recordFieldLabel :: Field rec ty -> Text default recordFieldLabel :: (Show (Field rec ty)) => Field rec ty -> Text recordFieldLabel = Text.pack . show++-- | A @'Distributed' f rec@ is a 'Record' with fields that are wrapped in+-- the @f@ type constructor.+--+-- The simplest example is the trivial 'Identity' - @'Distributed'+-- 'Identity' rec@ wraps each field in 'Identity', which doesn't do+-- anything.+--+-- But you can also represent a @'Distributed' 'IO' rec@, where each field+-- is an 'IO' action.+--+-- This type is equivalently powerful to the @Higher Kinded Data@ pattern,+-- but significantly more flexible, since you don't need to munge the+-- underlying datatype with the complexity of this.+--+-- You can use @OverloadedRecordDot@ to access fields on this directly, or+-- you can also use 'getRecordFieldDistributed'.+--+-- @since 0.1.0.0+newtype Distributed f rec = Distributed (forall x. Field rec x -> f x) -- | An enumeration of fields on the record. --
src/Prairie/Distributed.hs view
@@ -28,26 +28,6 @@ import GHC.Records import Prairie.Class --- | A @'Distributed' f rec@ is a 'Record' with fields that are wrapped in--- the @f@ type constructor.------ The simplest example is the trivial 'Identity' - @'Distributed'--- 'Identity' rec@ wraps each field in 'Identity', which doesn't do--- anything.------ But you can also represent a @'Distributed' 'IO' rec@, where each field--- is an 'IO' action.------ This type is equivalently powerful to the @Higher Kinded Data@ pattern,--- but significantly more flexible, since you don't need to munge the--- underlying datatype with the complexity of this.------ You can use @OverloadedRecordDot@ to access fields on this directly, or--- you can also use 'getRecordFieldDistributed'.------ @since 0.1.0.0-newtype Distributed f rec = Distributed (forall x. Field rec x -> f x)- -- | This instance allows you to use the @OverloadedRecordDot@ with -- distributed records. --
src/Prairie/TH.hs view
@@ -8,6 +8,7 @@ import Data.Char (toLower, toUpper) import Data.Constraint (Dict (..)) import Data.Functor.Apply (Apply (..))+import Data.Functor.Compose import qualified Data.List as List import qualified Data.Text as Text import Data.Traversable (for)@@ -185,6 +186,37 @@ [ Clause [VarP fromFieldName] (NormalB body) [] ] + mkSequenceRecordA <- do+ fromFieldName <- newName "s"+ let+ mkMatch (VarP lamArg) (fieldName, _typ) = do+ let+ pat = compatConP $ mkConstrFieldName fieldName+ in+ Match pat (NormalB (VarE lamArg)) []+ lambda <- do+ vars <- traverse (fmap VarP . newName . nameBase . fst) names'types+ pure $+ LamE+ vars+ (ConE 'Distributed `AppE` LamCaseE (List.zipWith mkMatch vars names'types))+ let+ body =+ List.foldl'+ ( \acc (n, _) ->+ VarE '(<*>)+ `AppE` acc+ `AppE` (VarE 'getCompose `AppE` (VarE fromFieldName `AppE` ConE (mkConstrFieldName n)))+ )+ (VarE 'pure `AppE` lambda)+ names'types++ pure $+ FunD+ 'sequenceRecordA+ [ Clause [compatConP' 'Distributed [VarP fromFieldName]] (NormalB body) []+ ]+ mkRecordFieldLabel <- do fieldName <- newName "fieldName" body <- pure $@@ -230,6 +262,7 @@ , recordFieldLensDec , mkTabulateRecord , mkTabulateRecordApply+ , mkSequenceRecordA , mkRecordFieldLabel ] )@@ -290,4 +323,13 @@ #else compatConP constrFieldName = ConP constrFieldName []+#endif++compatConP' :: Name -> [Pat] -> Pat+#if MIN_VERSION_template_haskell(2,18,0)+compatConP' constrFieldName ps =+ ConP constrFieldName [] ps+#else+compatConP' constrFieldName ps =+ ConP constrFieldName ps #endif
test/Spec.hs view
@@ -28,6 +28,7 @@ import Control.Monad import Data.Aeson import Data.Functor.Apply (Apply (..))+import Data.Functor.Compose import Data.List.NonEmpty (NonEmpty (..)) import Data.Monoid import Prairie.AsRecord@@ -120,6 +121,18 @@ { name = "Matt" , age = 33 }++ it "sequenceRecordA" $ do+ Distributed user' <- do+ sequenceRecordA @User @IO @Maybe $ Distributed \case+ UserName -> Compose do+ print @Int 10 >> pure (Just "Matt")+ UserAge -> Compose do+ print @Int 20 >> pure Nothing+ user' UserName `shouldBe` Just "Matt"+ user' UserAge `shouldBe` Nothing+ tabulateRecordA user' `shouldBe` Nothing+ describe "Fold" $ do describe "foldRecord" $ do it "can count the fields" $ do