packages feed

generic-override (empty) → 0.0.0.0

raw patch · 7 files changed

+243/−0 lines, 7 filesdep +basesetup-changed

Dependencies added: base

Files

+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for scoped-instances++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Estatico Studios LLC (c) 2020++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Estatico Studios LLC nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,66 @@+# generic-override++For the associated blog post describing how this works, see+[Overriding Type Class Instances](http://caryrobbins.com/dev/overriding-type-class-instances-2/).++-------------------------++This library provides the ability to override instances used by generic derivation.++## Example++```haskell+-- Needed for constructing our 'Override' type in the DerivingVia clause.+import Data.Override (Override(Override), As)+-- Provides aeson support for generic-override (lives in generic-override-aeson).+import Data.Override.Aeson ()+-- Basic imports we need for the example.+import Data.Aeson (ToJSON(toJSON))+import Data.Text (Text)+import qualified Data.Text as Text++-- | A simple record type. We'll use generic derivation for the 'ToJSON' instance+-- but override the instances used by derivation for the 'String' and 'baz'+-- fields.+data MyRec = MyRec+  { foo :: Int+  , bar :: String+  , baz :: Text+  } deriving stock (Show, Eq, Generic)+    deriving (ToJSON)+      via Override MyRec+            '[ -- Derive the 'String' field via 'CharArray'.+               String `As` CharArray+               -- Derive the 'baz' field via 'Uptext'.+             , "baz" `As` Uptext+             ]++-- | Newtype wrapper to override 'ToJSON Text' instances with ones that+-- encode the 'Text' as uppercase.+newtype Uptext = Uptext { unUptext :: Text }++instance ToJSON Uptext where+  toJSON = toJSON . Text.toUpper . unUptext++-- | Newtype wrapper to override 'ToJSON String' instances with ones that+-- encode the 'String' as a JSON array of characters.+newtype CharArray = CharArray { unCharArray :: String }++instance ToJSON CharArray where+  toJSON = toJSON . map (:[]) . unCharArray+```++Let's serialize an example `MyRec` to JSON -++```haskell+% ghci+> :{+  Data.ByteString.Lazy.Char8.putStrLn+    $ Data.Aeson.encode+    $ Data.Aeson.toJSON MyRec { foo = 12, bar = "hi", baz = "bye" }+  :}+{"foo":12,"bar":["h","i"],"baz":"BYE"}+```++For more examples, see the test suite in+[generic-override-aeson](./aeson/test/Test.hs).
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ generic-override.cabal view
@@ -0,0 +1,33 @@+cabal-version: 1.12+name: generic-override+version: 0.0.0.0+license: BSD3+license-file: LICENSE+copyright: 2020 Estatico Studios LLC+maintainer: carymrobbins@gmail.com+author: Cary Robbins+homepage: https://github.com/estatico/generic-override#readme+bug-reports: https://github.com/estatico/generic-override/issues+synopsis: Provides functionality for overriding instances for generic derivation+description:+    Please see the README on GitHub at <https://github.com/estatico/generic-override#readme>+category: Generics+build-type: Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+    type: git+    location: https://github.com/estatico/generic-override++library+    exposed-modules:+        Data.Override+        Data.Override.Internal+    hs-source-dirs: src+    other-modules:+        Paths_generic_override+    default-language: Haskell2010+    build-depends:+        base >=4.7 && <5
+ src/Data/Override.hs view
@@ -0,0 +1,9 @@+module Data.Override (module X) where++import Data.Override.Internal as X+  ( Override(Override), unOverride+  , Overridden(Overridden), unOverridden+  , As+  , Using+  , override+  )
+ src/Data/Override/Internal.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}+module Data.Override.Internal where++import Data.Type.Bool (If)+import Data.Type.Equality (type (==))+import GHC.Generics+import GHC.TypeLits (Symbol)++-- | The feature of this library. For use with DerivingVia.+-- Apply it to a type 'a' and supply a type-level list of instance+-- overrides 'xs'.+newtype Override a (xs :: [*]) = Override a+  deriving stock (Show, Eq)++-- | Unwrap an 'Override' value.+unOverride :: Override a xs -> a+unOverride (Override a) = a++-- | Construct an 'Override' using a proxy of overrides.+override :: a -> proxy xs -> Override a xs+override a _ = Override a++-- | Used to construct a type-level override. Usually used infix.+-- The 'o' should be either a type (kind '*') or a type-level string+-- (kind 'Symbol').+data As (o :: k) n++-- | Used at the leaf nodes of a generic 'Rep'+newtype Overridden (ms :: Maybe Symbol) a (xs :: [*]) = Overridden a++-- | Unwrap an 'Overridden' value.+unOverridden :: Overridden ms a xs -> a+unOverridden (Overridden a) = a++-- | Same as 'override' but for 'Overridden' types.+overridden+  :: forall a (ms :: Maybe Symbol) (xs :: [*]) proxy0 proxy1.+     a -> proxy0 ms -> proxy1 xs -> Overridden ms a xs+overridden a _ _ = Overridden a++instance (Generic a, GOverride xs (Rep a)) => Generic (Override a xs) where+  type Rep (Override a xs) = OverrideRep xs (Rep a)+  from = overrideFrom @xs . from . unOverride+  to = Override . to . overrideTo @xs++-- | Type class used to build the 'Generic' instance for 'Override'.+class GOverride (xs :: [*]) (f :: * -> *) where+  -- | Analogous to 'Rep'; rewrites the type for a given 'Rep' and injects+  -- 'Overridden' at the leaves.+  type OverrideRep xs f :: * -> *+  overrideFrom :: f x -> OverrideRep xs f x+  overrideTo :: OverrideRep xs f x -> f x++instance (GOverride xs f) => GOverride xs (M1 D c f) where+  type OverrideRep xs (M1 D c f) = M1 D c (OverrideRep xs f)+  overrideFrom (M1 x) = M1 (overrideFrom @xs x)+  overrideTo (M1 x) = M1 (overrideTo @xs x)++instance (GOverride xs f) => GOverride xs (M1 C c f) where+  type OverrideRep xs (M1 C c f) = M1 C c (OverrideRep xs f)+  overrideFrom (M1 x) = M1 (overrideFrom @xs x)+  overrideTo (M1 x) = M1 (overrideTo @xs x)++instance (GOverride xs f, GOverride xs g) => GOverride xs (f :*: g) where+  type OverrideRep xs (f :*: g) = OverrideRep xs f :*: OverrideRep xs g+  overrideFrom (f :*: g) = overrideFrom @xs f :*: overrideFrom @xs g+  overrideTo (f :*: g) = overrideTo @xs f :*: overrideTo @xs g++instance GOverride xs (M1 S ('MetaSel ms su ss ds) (K1 R c)) where+  type OverrideRep xs (M1 S ('MetaSel ms su ss ds) (K1 R c)) =+    M1 S ('MetaSel ms su ss ds) (K1 R (Overridden ms c xs))+  overrideFrom (M1 (K1 x)) = M1 (K1 (Overridden @ms x))+  overrideTo (M1 (K1 (Overridden x))) = M1 (K1 x)++-- | Type family used to determine which override from 'xs'+-- to replace 'x' with, if any. The 'ms' holds the field name+-- for 'x', if applicable.+type family Using (ms :: Maybe Symbol) (x :: *) (xs :: [*]) where+  -- No matching override found.+  Using ms x '[] = x++  -- Override the matching field.+  Using ms x (As (o :: Symbol) n ': xs) =+    If (ms == 'Just o) n (Using ms x xs)++  -- Override the matching type.+  Using ms x (As (o :: *) n ': xs) =+    If (x == o) n (Using ms x xs)