packages feed

some-dict-of (empty) → 0.1.0.0

raw patch · 7 files changed

+306/−0 lines, 7 filesdep +basedep +constraintsdep +some-dict-ofsetup-changed

Dependencies added: base, constraints, some-dict-of

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2021 Matthew Parsons++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 Felipe Lessa 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,71 @@+# `some-dict-of`++Have you ever needed an existential wrapper that just guaranteed class+membership, but you didn't need to know the specifics of the type in question?++Well, you probably *don't* - it's not idiomatic Haskell for the most part. But+sometimes this comes up, and I wanted a nice packaging of the technique. I wrote+this mostly to support the `discover-instances` library.++## `SomeDictOf`++Let's consider a type that wraps anything that can be shown, along with a value+of that type. We can make one by hand like this:++```haskell+data SomeShowable where+    SomeShowable :: forall a. Show a => a -> SomeShowable++showList :: [SomeShowable] -> [String]+showList = map (\(SomeShowable a) -> show a)+```++With `SomeDictOf`, we can generalize this pattern to other classes and even+other containers.++```haskell+someShowable :: SomeDictOf Identity Show+someShowable =+    SomeDictOf (Identity (3 :: Int))+```++We're carrying an `Int`, but the type does not reveal this. We can create a list+of values like this, and we can call `show` on them.++```haskell+showValues+    :: [SomeDictOf Identity Show]+    -> [String]+showValues =+    map (\(SomeDictOf (Identity showable)) -> show showable)+```++We can also carry around evidence of a type class instance, and then write+generic stuff based on it. Consider the+[`PersistEntity`](https://hackage.haskell.org/package/persistent-2.13.1.1/docs/Database-Persist-Class-PersistEntity.html#t:PersistEntity)+from the `persistent` database library.++```haskell+tables :: [SomeDictOf Proxy PersistEntity]+tables = [ SomeDictOf (Proxy @User), SomeDictOf (Proxy @Organization) ]+```++Now we can iterate over these types and, say, load all the rows out of the+database and verify they parse.++```haskell+checkRows :: SqlPersistT [[PersistValue]]+checkRows = do+    forM tables $ \(SomeDictOf (Proxy :: Proxy table)) -> do+        results <- selectList [] [] :: SqlPersistT m [Entity table]+        pure (map toPersistValue results)+```++Or we can do some metaprogramming based on their `EntityDef`s, since+`PersistEntity` has a method `Proxy a -> EntityDef`.++```haskell+getDefinitions :: [EntityDef]+getDefinitions =+    map (\(SomeDictOf p) -> entityDef p) tables+```
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ changelog.md view
@@ -0,0 +1,3 @@+# 0.1.0.0++- Initial Release
+ some-dict-of.cabal view
@@ -0,0 +1,52 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.34.4.+--+-- see: https://github.com/sol/hpack++name:           some-dict-of+version:        0.1.0.0+synopsis:       Carry evidence of constraints around+description:    Please see the README on GitHub at <https://github.com/parsonsmatt/some-dict-of#readme>+category:       Constraints+homepage:       https://github.com/parsonsmatt/some-dict-of#readme+bug-reports:    https://github.com/parsonsmatt/some-dict-of/issues+author:         Matt Parsons+maintainer:     parsonsmatt@gmail.com+copyright:      Matt Parsons+license:        BSD3+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    changelog.md++source-repository head+  type: git+  location: https://github.com/parsonsmatt/some-dict-of++library+  exposed-modules:+      SomeDictOf+  other-modules:+      Paths_some_dict_of+  hs-source-dirs:+      src+  build-depends:+      base >=4.7 && <5+    , constraints+  default-language: Haskell2010++test-suite discover-instances-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_some_dict_of+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , constraints+    , some-dict-of+  default-language: Haskell2010
+ src/SomeDictOf.hs view
@@ -0,0 +1,132 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}++module SomeDictOf+    ( module SomeDictOf+    -- * Re-exports+    , module Data.Functor.Identity+    , module Data.Proxy+    , module Data.Constraint+    ) where++import Data.Constraint+import Data.Functor.Identity+import Data.Kind+import Data.Proxy++-- | A datatype that carries evidence of type class membership for some+-- type, along with a datatype indexed by that type. This is confusing, so+-- let's look at some examples.+--+-- The alias @'SomeDict' clazz@ uses 'Proxy' for the datatype index. This means+-- that the wrapper is a @'Proxy' :: 'Proxy' a@, and you know that the type+-- @a@ has an instance of @clazz a@. We can construct a value+--+-- @+-- showSomeDict :: SomeDictOf Identity Show+-- showSomeDict =+--     SomeDict (Identity 3 :: Identity Int)+-- @+--+-- A value of @'SomeDict' 'Show'@ contains 'Proxy' value. When you pattern+-- match on the 'SomeDict', you know that the given @'Proxy' :: 'Proxy' a@+-- has an instance of 'Show'.+--+-- For the most part, this isn't useful,+--+-- @since 0.1.0.0+data SomeDictOf (f :: k -> Type) (c :: k -> Constraint) where+    SomeDictOf :: c a => f a -> SomeDictOf f c++-- | In the case where you only need evidence that the dictionary exists,+-- then you can use this type. By carrying a 'Proxy' instead of a real+-- value, we can summon these up whenever we have an instance of the type in+-- question.+--+-- @since 0.1.0.0+type SomeDict = SomeDictOf Proxy++-- | Construct a 'SomeDict' with the type in question. Use with+-- @TypeApplications@.+--+-- Example:+--+-- @+-- showDict :: SomeDict Show+-- showDict = someDict @Int+--+-- entityDict :: SomeDict PersistEntity+-- entityDict = someDict @User+-- @+--+-- @since 0.1.0.0+someDict :: forall a c. c a => SomeDict c+someDict = SomeDictOf (Proxy :: Proxy a)++-- | Construct a 'SomeDict' based on a 'Dict' from the "Data.Constraint" module.+--+-- @since 0.1.0.0+fromDict :: forall a c. Dict (c a) -> SomeDict c+fromDict Dict = SomeDictOf (Proxy @a)++-- | Unpack a 'SomeDictOf' and attain access to the evidence that the+-- underlying type satisfies the instance in question.+--+-- @since 0.1.0.0+withSomeDictOf+    :: SomeDictOf f c+    -> (forall a. c a => f a -> r)+    -> r+withSomeDictOf (SomeDictOf p) k =+    k p++-- | Transform the type index used in the 'SomeDictOf'. The provided function is+-- unable to inspect the type, but you will have the class operations available+-- to it.+--+-- Example:+--+-- @+-- provideMempty ;: SomeDictOf Proxy Monoid -> SomeDictOf Identity Monoid+-- provideMempty = mapSomeDictOf (\proxy -> Identity mempty)+-- @+--+-- @since 0.1.0.0+mapSomeDictOf+    :: (forall a. c a => f a -> g a)+    -> SomeDictOf f c+    -> SomeDictOf g c+mapSomeDictOf f (SomeDictOf fa) =+    SomeDictOf (f fa)++-- |  Not really sure what this might be useful for.+--+-- Examples:+--+-- @+-- shows :: SomeDictOf [] Show+-- shows = SomeDictOf [True, False, True]+--+-- forgotten :: [SomeDictOf Proxy Show]+-- forgotten = forgetContents show+--+-- main = do+--     forM forgotten $ \(SomeDictOf Proxy) -> do+--          print 10+-- @+--+-- The above program should output 10 three times.+--+-- @since 0.1.0.0+forgetContents+    :: forall f c. Functor f+    => SomeDictOf f c+    -> f (SomeDictOf Proxy c)+forgetContents (SomeDictOf (xs :: f a)) =+    fmap (\(_ :: a) -> SomeDictOf (Proxy @a)) xs
+ test/Spec.hs view
@@ -0,0 +1,16 @@+module Main where++import SomeDictOf++main :: IO ()+main = putStrLn "it compiles at least"++-- note: if this changes, update the README+showValues+    :: [SomeDictOf Identity Show]+    -> [String]+showValues =+    map (\(SomeDictOf (Identity showable)) -> show showable)++slamMempty :: SomeDictOf Proxy Monoid -> SomeDictOf Identity Monoid+slamMempty = mapSomeDictOf (\pa -> Identity mempty)