higgledy 0.1.1.0 → 0.1.1.1
raw patch · 6 files changed
+158/−39 lines, 6 filesdep ~QuickCheckdep ~barbiesdep ~basePVP: minor bump suggested
API additions: PVP suggests at least a minor version bump
Dependency ranges changed: QuickCheck, barbies, base, generic-lens, hspec
API changes (from Hackage documentation)
+ Data.Generic.HKD.Build: build :: Build structure f k => k
+ Data.Generic.HKD.Build: class Build (structure :: Type) (f :: Type -> Type) (k :: Type) | f structure -> k
+ Data.Generic.HKD.Build: instance (Data.Generic.HKD.Build.Fill f structure xs, Data.Generic.HKD.Build.GBuild f structure xs k) => Data.Generic.HKD.Build.Build structure f k
+ Data.Generic.HKD.Build: instance (Data.Generic.HKD.Build.GFill f xs ys left, Data.Generic.HKD.Build.GFill f ys zs right) => Data.Generic.HKD.Build.GFill f xs zs (left GHC.Generics.:*: right)
+ Data.Generic.HKD.Build: instance (GHC.Generics.Generic shape, Data.Generic.HKD.Build.GFill f with '[] (GHC.Generics.Rep shape)) => Data.Generic.HKD.Build.Fill f shape with
+ Data.Generic.HKD.Build: instance Data.Generic.HKD.Build.GBuild f structure '[] (Data.Generic.HKD.Types.HKD structure f)
+ Data.Generic.HKD.Build: instance Data.Generic.HKD.Build.GBuild f structure xs k => Data.Generic.HKD.Build.GBuild f structure (x : xs) (x -> k)
+ Data.Generic.HKD.Build: instance Data.Generic.HKD.Build.GFill f (f x : xs) xs (GHC.Generics.Rec0 x)
+ Data.Generic.HKD.Build: instance Data.Generic.HKD.Build.GFill f xs ys inner => Data.Generic.HKD.Build.GFill f xs ys (GHC.Generics.M1 index meta inner)
Files
- README.md +33/−18
- higgledy.cabal +9/−8
- src/Data/Generic/HKD.hs +1/−0
- src/Data/Generic/HKD/Build.hs +107/−0
- src/Data/Generic/HKD/Position.hs +0/−1
- test/Main.hs +8/−12
README.md view
@@ -53,6 +53,8 @@ module Example where import Control.Lens ((.~), (^.), (&), Const (..), Identity, anyOf)+import Data.Funtor.Const (Const (..))+import Data.Funtor.Identity (Identity (..)) import Data.Generic.HKD import Data.Maybe (isJust, isNothing) import Data.Monoid (Last (..))@@ -163,14 +165,27 @@ -- Last {getLast = Just (Triple 123 () "ABC")} ``` +If none of the above suit your needs, maybe you want to try `build` on for+size. This function constructs an `HKD`-wrapped version of the type supplied to+it by taking all its parameters. In other words:++```haskell+eg6 :: f Int -> f () -> f String -> HKD Triple f+eg6 = build @Triple++eg7 :: HKD Triple []+eg7 = eg6 [1] [] ["Tom", "Tim"]+-- Triple [1] [] ["Tom","Tim"]+```+ ### Field Access The `field` lens, when given a type-applied field name, allows us to focus on fields within a record: ```haskell-eg6 :: Last Int-eg6 = eg0 ^. field @"age"+eg8 :: Last Int+eg8 = eg0 ^. field @"age" -- Last {getLast = Nothing} ``` @@ -179,8 +194,8 @@ choice): ```haskell-eg7 :: Partial User-eg7 = eg0 & field @"name" .~ pure "Evil Tom"+eg9 :: Partial User+eg9 = eg0 & field @"name" .~ pure "Evil Tom" & field @"likesDogs" .~ pure False -- User -- { name = Last {getLast = Just "Evil Tom"}@@ -193,8 +208,8 @@ completed for a given partial type: ```haskell-eg8 :: Bool-eg8 = anyOf (field @"name") (isJust . getLast) eg0+eg10 :: Bool+eg10 = anyOf (field @"name") (isJust . getLast) eg0 -- False ``` @@ -203,8 +218,8 @@ doesn't exist in our type: ```haskell-eg9 :: Identity ()-eg9 = eg3 ^. field @"oops"+eg11 :: Identity ()+eg11 = eg3 ^. field @"oops" -- error: -- • The type User does not contain a field named 'oops'. ```@@ -215,8 +230,8 @@ product types: ```haskell-eg10 :: Labels Triple-eg10 = mempty & position @1 .~ Const "hello"+eg12 :: Labels Triple+eg12 = mempty & position @1 .~ Const "hello" & position @2 .~ Const "world" -- Triple -- Const "hello"@@ -227,8 +242,8 @@ Again, this is a `Lens`, so we can just as easily _set_ values: ```haskell-eg11 :: Partial User-eg11 = eg7 & position @2 .~ pure 25+eg13 :: Partial User+eg13 = eg9 & position @2 .~ pure 25 -- User -- { name = Last {getLast = Just "Evil Tom"} -- , age = Last {getLast = Just 25}@@ -240,8 +255,8 @@ type errors are a delight: ```haskell-eg9 :: Identity ()-eg9 = deconstruct @Identity triple ^. position @4+eg14 :: Identity ()+eg14 = deconstruct @Identity triple ^. position @4 -- error: -- • The type Triple does not contain a field at position 4 ```@@ -253,8 +268,8 @@ this interface: ```haskell-eg10 :: Labels User-eg10 = label eg11+eg15 :: Labels User+eg15 = label eg13 -- User -- { name = Const "name" -- , age = Const "age"@@ -269,7 +284,7 @@ fields whose values satisfy some predicate: ```haskell-eg13 :: [String]-eg13 = labelsWhere (isNothing . getLast) eg7+eg16 :: [String]+eg16 = labelsWhere (isNothing . getLast) eg9 -- ["age"] ```
higgledy.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4 name: higgledy-version: 0.1.1.0+version: 0.1.1.1 synopsis: Partial types as a type constructor. description: Use the generic representation of an ADT to get a higher-kinded data-style interface automatically. homepage: https://github.com/i-am-tom/higgledy@@ -17,6 +17,7 @@ library exposed-modules: Data.Generic.HKD+ Data.Generic.HKD.Build Data.Generic.HKD.Construction Data.Generic.HKD.Field Data.Generic.HKD.Labels@@ -24,20 +25,20 @@ Data.Generic.HKD.Types -- other-modules: -- other-extensions:- build-depends: base ^>=4.12.0.0- , barbies ^>=1.1.0.0- , generic-lens ^>=1.1.0.0- , QuickCheck ^>=2.13.0+ build-depends: base ^>=4.12+ , barbies ^>=1.1.0+ , generic-lens ^>=1.1.0+ , QuickCheck ^>=2.12.6 hs-source-dirs: src default-language: Haskell2010 test-suite test- build-depends: base+ build-depends: base ^>=4.12 , doctest ^>=0.16.0 , higgledy- , hspec ^>=2.7.0+ , hspec ^>=2.6.1 , lens ^>=4.17- , QuickCheck+ , QuickCheck ^>= 2.12.6 main-is: Main.hs type: exitcode-stdio-1.0 hs-source-dirs: test
src/Data/Generic/HKD.hs view
@@ -10,6 +10,7 @@ ( module Exports ) where +import Data.Generic.HKD.Build as Exports import Data.Generic.HKD.Construction as Exports import Data.Generic.HKD.Field as Exports import Data.Generic.HKD.Labels as Exports
+ src/Data/Generic/HKD/Build.hs view
@@ -0,0 +1,107 @@+{-# OPTIONS_HADDOCK not-home #-}++{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++{-|+Module : Data.Generic.HKD.Build+Description : Construct an HKD structure with its component parameters.+Copyright : (c) Tom Harding, 2019+License : MIT+Maintainer : tom.harding@habito.com+Stability : experimental+-}+module Data.Generic.HKD.Build+ ( Build (..)+ ) where++import Data.Kind (Type)+import Data.GenericLens.Internal (HList (..))+import Data.Generic.HKD.Types (HKD (..), GHKD_)+import GHC.Generics+import Prelude hiding (uncurry)++class Fill (f :: Type -> Type) (structure :: Type) (types :: [Type])+ | structure f -> types, types -> f where+ fill :: HList types -> HKD structure f++class GFill (f :: Type -> Type) (xs :: [Type]) (ys :: [Type]) (rep :: Type -> Type)+ | xs rep -> ys, ys f rep -> xs, xs -> f where+ gfill :: HList xs -> (HList ys, GHKD_ f rep p)++instance GFill f xs ys inner+ => GFill f xs ys (M1 index meta inner) where+ gfill = fmap M1 . gfill @f++instance (GFill f xs ys left, GFill f ys zs right)+ => GFill f xs zs (left :*: right) where+ gfill xs = do+ let (ys, left) = gfill @f xs+ (zs, right) = gfill @f ys++ (zs, left :*: right)++instance GFill f (f x ': xs) xs (Rec0 x) where+ gfill (x :> xs) = (xs, K1 x)++instance (Generic shape, GFill f with '[] (Rep shape))+ => Fill f shape with where+ fill = HKD . snd . gfill @f @_ @'[]++-- | With many HKD applications, we're working with types like 'Maybe' where it+-- makes sense for us to start with 'mempty' and add values in as we go.+--+-- This isn't always the case, however: if all the component parts of our type+-- are gathered using some 'IO' action, we'd like to construct something like+-- @HKD MyType IO@, and @IO a@ isn't a 'Monoid' for all types @a@. When this+-- happens, we need to pass in our parameters /when/ we build our structure.+--+-- The 'build' function lets us construct our type by passing explicit values+-- for each parameter:+--+-- >>> :set -XDeriveGeneric -XTypeApplications+--+-- >>> :{+-- data User+-- = User { name :: String, age :: Int, likesDogs :: Bool }+-- deriving Generic+-- :}+-- +-- >>> :{+-- test :: _+-- test = build @User+-- :}+-- ...+-- ... • Found type wildcard ‘_’+-- ... standing for ‘f [Char] -> f Int -> f Bool -> HKD User f’+-- ...+--+-- Once we call the 'build' function, and indicate the type we want to build,+-- we are free to pick whichever 'f' we like and get to work!+class Build (structure :: Type) (f :: Type -> Type) (k :: Type)+ | f structure -> k where+ build :: k++class GBuild (f :: Type -> Type) (structure :: Type) (xs :: [Type]) (k :: Type)+ | f structure xs -> k where+ gbuild :: (HList xs -> HKD structure f) -> k++instance GBuild f structure xs k+ => GBuild f structure (x ': xs) (x -> k) where+ gbuild k x = gbuild @_ @_ @xs \xs -> k (x :> xs)++instance GBuild f structure '[] (HKD structure f) where+ gbuild k = k Nil++instance (Fill f structure xs, GBuild f structure xs k)+ => Build structure f k where+ build = gbuild @f @structure @xs fill
src/Data/Generic/HKD/Position.hs view
@@ -34,7 +34,6 @@ import qualified Data.GenericLens.Internal as G import Data.GenericLens.Internal (type (<?)) import qualified Data.Generics.Internal.VL.Lens as G-import Data.Generics.Internal.Profunctor.Lens (ALens) -- | Product types /without/ named fields can't be addressed by field name (for -- very obvious reason), so we instead need to address them with their
test/Main.hs view
@@ -12,14 +12,12 @@ import Control.Lens (Lens', (.~), (^.)) import Data.Function ((&), on)-import Data.Functor.Identity (Identity (..)) import Data.Generic.HKD import Data.Monoid (Last (..)) import GHC.Generics import Test.DocTest import Test.Hspec import Test.QuickCheck-import Test.QuickCheck.Arbitrary type Partial a = HKD a Last type WTF a = HKD a []@@ -180,16 +178,14 @@ partials = describe "HKD" do describe "Eq" do- it "is monotonic with respect to ordering (Partial)"- $ property \(x :: a) y -> (x <= y) == ((<=) `on` deconstruct @Last) x y+ it "is monotonic with respect to ordering (Partial)" $ property \(x :: a) y ->+ (x <= y) == ((<=) `on` deconstruct @Last) x y - it "is monotonic with respect to ordering (WTF)"- $ property \(x :: a) y -> (x <= y) == ((<=) `on` deconstruct @[]) x y+ it "is monotonic with respect to ordering (WTF)" $ property \(x :: a) y ->+ (x <= y) == ((<=) `on` deconstruct @[]) x y - it "round-trips"- $ property \(x :: a) ->- construct (deconstruct @Last x) == pure x+ it "round-trips" $ property \(x :: a) ->+ construct (deconstruct @Last x) == pure x - it "round-trips"- $ property \(x :: a) ->- construct (deconstruct @[] x) == pure x+ it "round-trips" $ property \(x :: a) ->+ construct (deconstruct @[] x) == pure x