packages feed

it-has (empty) → 0.1.0.0

raw patch · 7 files changed

+355/−0 lines, 7 filesdep +QuickCheckdep +basedep +generic-lenssetup-changed

Dependencies added: QuickCheck, base, generic-lens, it-has

Files

+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for it-has++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Dobromir Nikolov (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 Dobromir Nikolov 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,76 @@+![Haskell CI](https://github.com/dnikolovv/it-has/workflows/Haskell%20CI/badge.svg)++# it-has++This is a (nearly) drop-in replacement of [data-has](http://hackage.haskell.org/package/data-has). The differences with the original package are that this one misses `hasLens` and uses `Generic` for its default implementation. Your initial reaction may be to start mourning the loss of `hasLens`, but first take a look at the cool things you can do without it!++Reduce boilerplate! You can trim down this:++```haskell+data Config =+  Config+    { configLogEnv      :: !LogEnv+    , configJwtSettings :: !JWTSettings+    , configMetrics     :: !Metrics+    , configEkgStore    :: !EKG.Store }++-- Heavy manual instances, data-has only has default implementation for tuples+instance Has LogEnv Config where+  getter = configLogEnv+  modifier f v = v { configLogEnv = f (configLogEnv v) }++instance Has JWTSettings Config where+  getter = configJwtSettings+  modifier f v = v { configJwtSettings = f (configJwtSettings v) }++instance Has Metrics Config where+  getter = configMetrics+  modifier f v = v { configMetrics = f (configMetrics v) }++instance Has EKG.Store Config where+  getter = configEkgStore+  modifier f v = v { configEkgStore = f (configEkgStore v) }+```++To this:++```haskell+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric  #-}++data Config =+  Config+    { configLogEnv      :: !LogEnv+    , configJwtSettings :: !JWTSettings+    , configMetrics     :: !Metrics+    , configEkgStore    :: !EKG.Store+    } deriving (Generic, Has LogEnv, Has JWTSettings, Has Metrics, Has EKG.Store)+```++Another trick is that you can "force" a sum type to have a specific field defined.++E.g. you may want to define an `Error` type and enforce that it always has an `ErrorText` attached to it.++```haskell+newtype ErrorText =+ ErrorText Text+ +data Error =+ ValidationError |+ NotFound |+ Critical |+ Unauthorized+```++You can do that by deriving `Has ErrorText`. The compiler will error until you have added an `ErrorText` field to each representation.++```haskell+data Error =+ ValidationError ErrorText |+ NotFound ErrorText |+ Critical ErrorText |+ Unauthorized ErrorText+ deriving (Generic, Has ErrorText)+```++For more documentation and examples, please refer to the [original package](http://hackage.haskell.org/package/data-has).
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ it-has.cabal view
@@ -0,0 +1,55 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.31.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: bfe8f0c0584b30d3e56a036f44845a917b11b45b35dc75b6133a965887d5b41b++name:           it-has+version:        0.1.0.0+synopsis:       Automatically derivable Has instances.+description:    Please see the README on GitHub at <https://github.com/dnikolovv/it-has#readme>+category:       Data+homepage:       https://github.com/dnikolovv/it-has#readme+bug-reports:    https://github.com/dnikolovv/it-has/issues+author:         Dobromir Nikolov+maintainer:     dnikolovv@hotmail.com+copyright:      Dobromir Nikolov+license:        BSD3+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+  type: git+  location: https://github.com/dnikolovv/it-has++library+  exposed-modules:+      Data.Has+  other-modules:+      Paths_it_has+  hs-source-dirs:+      src+  build-depends:+      base >=4.7 && <5+    , generic-lens >=1.2.0 && <1.3+  default-language: Haskell2010++test-suite it-has-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_it_has+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      QuickCheck+    , base >=4.7 && <5+    , generic-lens >=1.2.0 && <1.3+    , it-has+  default-language: Haskell2010
+ src/Data/Has.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE DefaultSignatures     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeApplications      #-}+{-# LANGUAGE FlexibleInstances     #-}++{-|+Module      : Data.Has+Description : Automatically derivable Has instances.+Copyright   : (c) Dobromir Nikolov, 2020+License     : BSD3+Maintainer  : dnikolovv@hotmail.com+Stability   : experimental+Portability : PORTABLE++This is a (nearly) drop-in replacement of [data-has](http://hackage.haskell.org/package/data-has). The differences with the original package are that this one misses `hasLens` and uses `Generic` for its default implementation. Your initial reaction may be to start mourning the loss of `hasLens`, but first take a look at the cool things you can do without it!++Reduce boilerplate! You can trim down this:++@+data Config =+  Config+    { configLogEnv      :: !LogEnv+    , configJwtSettings :: !JWTSettings+    , configMetrics     :: !Metrics+    , configEkgStore    :: !EKG.Store }++-- Heavy manual instances, data-has only has default implementation for tuples+instance Has LogEnv Config where+  getter = configLogEnv+  modifier f v = v { configLogEnv = f (configLogEnv v) }++instance Has JWTSettings Config where+  getter = configJwtSettings+  modifier f v = v { configJwtSettings = f (configJwtSettings v) }++instance Has Metrics Config where+  getter = configMetrics+  modifier f v = v { configMetrics = f (configMetrics v) }++instance Has EKG.Store Config where+  getter = configEkgStore+  modifier f v = v { configEkgStore = f (configEkgStore v) }+@++To this:++@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric  #-}++data Config =+  Config+    { configLogEnv      :: !LogEnv+    , configJwtSettings :: !JWTSettings+    , configMetrics     :: !Metrics+    , configEkgStore    :: !EKG.Store+    } deriving (Generic, Has LogEnv, Has JWTSettings, Has Metrics, Has EKG.Store)+@++Another trick is that you can "force" a sum type to have a specific field defined.++E.g. you may want to define an `Error` type and enforce that it always has an `ErrorText` attached to it.++@+newtype ErrorText =+ ErrorText Text++data Error =+ ValidationError |+ NotFound |+ Critical |+ Unauthorized+@++You can do that by deriving `Has ErrorText`. The compiler will error until you have added an `ErrorText` field to each representation.++@+data Error =+ ValidationError ErrorText |+ NotFound ErrorText |+ Critical ErrorText |+ Unauthorized ErrorText+ deriving (Generic, Has ErrorText)+@++For more documentation and examples, please refer to the [original package](http://hackage.haskell.org/package/data-has).+-}+module Data.Has where++import           Data.Generics.Internal.VL.Lens (over)+import           Data.Generics.Product.Typed++-- | A type class for an extensible product.+class Has a b where+  getter :: b -> a+  modifier :: (a -> a) -> b -> b++  default getter :: HasType a b => b -> a+  getter = getTyped @a++  default modifier :: HasType a b => (a -> a) -> b -> b+  modifier = over $ typed @a++instance Has a a where+  getter = id+  modifier = id
+ test/Spec.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE DeriveAnyClass     #-}+{-# LANGUAGE DeriveGeneric      #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DerivingVia        #-}+{-# LANGUAGE TemplateHaskell    #-}++import           Control.Monad (void)+import           Data.Has+import           GHC.Generics    (Generic)+import           Test.QuickCheck (Arbitrary, quickCheckAll)++newtype LogConfig =+  LogConfig String+  deriving (Show, Eq)+  deriving (Arbitrary) via (String)++newtype SomeUrl =+  SomeUrl String+  deriving (Show, Eq)+  deriving (Arbitrary) via (String)++newtype ConnectionString =+  ConnectionString String+  deriving (Show, Eq)+  deriving (Arbitrary) via (String)++newtype DatabaseConfig =+  DatabaseConfig String+  deriving (Show, Eq)+  deriving (Arbitrary) via (String)++data SomeConfig =+  SomeConfig+    { someConfigLogConfig      :: LogConfig+    , someConfigDatabaseConfig :: DatabaseConfig+    , someConfigSomeUrl        :: SomeUrl+    } deriving+      ( Eq+      , Generic+      , Has LogConfig+      , Has DatabaseConfig+      , Has SomeUrl )++newtype ErrorText =+  ErrorText String+  deriving (Show, Eq)+  deriving (Arbitrary) via (String)++data Error =+  ValidationError ErrorText |+  Unauthorized ErrorText+  deriving (Generic, Has ErrorText)++prop_returnsSetValuesForRecord :: LogConfig -> DatabaseConfig -> SomeUrl -> Bool+prop_returnsSetValuesForRecord logConfig dbConfig someUrl =+  let config = SomeConfig logConfig dbConfig someUrl+  in getter config == logConfig &&+     getter config == dbConfig &&+     getter config == someUrl++prop_setsCorrectValues :: (LogConfig, DatabaseConfig, SomeUrl) -> (LogConfig, DatabaseConfig, SomeUrl) -> Bool+prop_setsCorrectValues (logConfig, dbConfig, someUrl) (newLogConfig, newDbConfig, newSomeUrl) =+  let config = SomeConfig logConfig dbConfig someUrl+      expectedConfig = SomeConfig newLogConfig newDbConfig newSomeUrl+      updatedConfig = modifier (const newLogConfig) . modifier (const newDbConfig) . modifier (const newSomeUrl) $ config+  in updatedConfig == expectedConfig++prop_returnsForcedFieldsForSumType :: ErrorText -> ErrorText -> Bool+prop_returnsForcedFieldsForSumType firstErrorText secondErrorText =+  let firstError = ValidationError firstErrorText+      secondError = Unauthorized secondErrorText+  in getter firstError == firstErrorText &&+     getter secondError == secondErrorText++return []++main :: IO ()+main = void runTests++runTests :: IO Bool+runTests = $quickCheckAll