packages feed

auto-lift-classes (empty) → 1

raw patch · 7 files changed

+445/−0 lines, 7 filesdep +auto-lift-classesdep +basedep +reflectionsetup-changed

Dependencies added: auto-lift-classes, base, reflection

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for autoshow1++## 0.1.0.0 -- YYYY-mm-dd++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2018 - 2023, Koji Miyazato++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 Koji Miyazato 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ auto-lift-classes.cabal view
@@ -0,0 +1,35 @@+cabal-version:       2.0+name:                auto-lift-classes+version:             1+synopsis:            Deriving (Show|Read)(1|2)+description:         Deriving (Show|Read)(1|2) from the usual, stock deriveable+                     instances like @Show a => Show (f a)@.+license:             BSD3+license-file:        LICENSE+author:              Koji Miyazato+maintainer:          viercc@gmail.com+copyright:           (c) 2018-2023, Koji Miyazato+category:            Data, Reflection+build-type:          Simple+extra-source-files:  CHANGELOG.md+tested-with:         GHC ==8.6.5, GHC ==8.8.4, GHC ==8.10.7, GHC ==9.0.2, GHC ==9.2.5, GHC ==9.4.4++source-repository head+  type:     git+  location: https://github.com/viercc/auto-lift-classes+  branch:   master++library+  exposed-modules:    AutoLift, AutoLift.Machinery+  build-depends:      base >= 4.12 && < 5.0,+                      reflection >= 1.0+  hs-source-dirs:     src+  default-language:   Haskell2010+  ghc-options:        -Wall -Wcompat++test-suite run-example+  type:                exitcode-stdio-1.0+  hs-source-dirs:      example+  main-is:             test.hs+  build-depends:       base, auto-lift-classes+  default-language:    Haskell2010
+ example/test.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE RoleAnnotations #-}+module Main where++import Data.Functor.Classes+import Text.Read++import AutoLift++data Test a = Test Int a [a]+    deriving (Show, Read, Eq)+    deriving (Show1, Read1) via (Reflected1 Test)++{-++-- The following does not compile due to type role annotation++data BadTest a = BadTest a [a]+    deriving (Show, Read, Eq)+    deriving (Show1, Read1) via (Reflected1 BadTest)++type role BadTest nominal++-}++test :: Test Char+test = Test 1 'a' "bcd"++testShow :: String+testShow = show test++testShow1 :: String+testShow1 = showsPrec1 0 test ""++readStr :: String+readStr = "Test 2 'e' \"fg\"   "++testRead :: [(Test Char, String)]+testRead = reads readStr++testRead1 :: [(Test Char, String)]+testRead1 = readsPrec1 0 readStr++data Crest a b = Crest Int [a] [b]+    deriving (Show, Read, Eq)+    deriving (Show1, Read1) via (Reflected1 (Crest a))+    deriving (Show2, Read2) via (Reflected2 Crest)++crest :: Crest Char Bool+crest = Crest 2 "foo" [False, True]++crestShow, crestShow1, crestShow2 :: String+crestShow = show crest+crestShow1 = showsPrec1 0 crest ""+crestShow2 = showsPrec2 0 crest ""++crestReadStr :: String+crestReadStr = "Crest 3 \"foo\" [True] rest of text"++crestRead, crestRead1, crestRead2 :: [(Crest Char Bool, String)]+crestRead = reads crestReadStr+crestRead1 = readsPrec1 0 crestReadStr+crestRead2 = readsPrec2 0 crestReadStr++main :: IO ()+main = putStrLn . unlines $+  [ "testShow:  " ++ show testShow+  , "testShow1: " ++ show testShow1+  , "testShow == testShow1: " ++ show (testShow == testShow1)+  , ""+  , "testRead:  " ++ show testRead+  , "testRead1: " ++ show testRead1+  , "testRead == testRead1: " ++ show (testRead == testRead1)+  , ""+  , "crestShow:  " ++ show crestShow+  , "crestShow1: " ++ show crestShow1+  , "crestShow2: " ++ show crestShow2+  , "crestShow == crestShow1: " ++ show (crestShow == crestShow1)+  , "crestShow == crestShow2: " ++ show (crestShow == crestShow2)+  , ""+  , "crestRead:  " ++ show crestRead+  , "crestRead1: " ++ show crestRead1+  , "crestRead2: " ++ show crestRead2+  , "crestRead == crestRead1: " ++ show (crestRead == crestRead1)+  , "crestRead == crestRead2: " ++ show (crestRead == crestRead2)+  ]
+ src/AutoLift.hs view
@@ -0,0 +1,158 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UndecidableInstances #-}++-- | Derive lifted version of 'Show' or 'Read' classes, like @'Show1' f@ or @'Read1' f@,+--   from derivable instance @forall a. Show a => Show (f a)@.+module AutoLift+  ( Reflected1 (..),+    Reflected2 (..),++    -- * Reexports+    Show1 (..),+    Read (..),+    Read1 (..),+    ReadPrec,+  )+where++import AutoLift.Machinery+import Data.Coerce+import Data.Functor.Classes+import Text.Read++-- | A newtype wrapper to derive @'Show1' f@ and @'Read1' f@ from the following,+--   often derivable instance.+--+--   > instance Show a => Show (f a)+--   > instance Read a => Read (f a)+--+-- ==== Example+--+-- Suppose you define a new type constructor @Foo@, and+-- derived its @Show@ instance.+--+-- >>> data Foo a = Foo [a] Int a deriving Show+--+-- The derived @Show (Foo a)@ instance is defined for all @a@ with @Show a@ instance.+--+-- > instance Show a => Show (Foo a)+--+-- @Reflected1@ allows you to derive @'Show1' Foo@ instance from the above instance.+--+-- >>> :set -XStandaloneDeriving -XDerivingVia+-- >>> deriving via (Reflected1 Foo) instance Show1 Foo+--+-- Let's try the derived @Show1@ instance, by showing @Foo Bool@, where+-- @True@ is shown as @yes@ and @False@ as @no@, instead of the normal @Show Bool@ instance.+--+-- >>> import Text.Show (showListWith)+-- >>> let yesno b = (++) (if b then "yes" else "no")+-- >>> liftShowsPrec (const yesno) (showListWith yesno) 0 (Foo [True, False] 5 False) ""+-- "Foo [yes,no] 5 no"+newtype Reflected1 f a = Reflected1 (f a)++wrapShowDict1 :: ShowDict (f a) -> ShowDict (Reflected1 f a)+wrapShowDict1 = coerce++wrapReadDict1 :: ReadDict (f a) -> ReadDict (Reflected1 f a)+wrapReadDict1 = coerce++instance+  ( forall a. Show a => Show (f a),+    forall xx yy. Coercible xx yy => Coercible (f xx) (f yy)+  ) =>+  Show1 (Reflected1 f)+  where+  liftShowsPrec showsPrecB showListB =+    let showFB = wrapShowDict1 $ autoShow1 @f (ShowDict showsPrecB showListB)+     in _showsPrec showFB+  liftShowList showsPrecB showListB =+    let showFB = wrapShowDict1 $ autoShow1 @f (ShowDict showsPrecB showListB)+     in _showList showFB++instance+  ( forall a. Read a => Read (f a),+    forall xx yy. Coercible xx yy => Coercible (f xx) (f yy)+  ) =>+  Read1 (Reflected1 f)+  where+  liftReadPrec readPrecB readListPrecB =+    let readFB = wrapReadDict1 $ autoRead1 @f (ReadDict readPrecB readListPrecB)+     in _readPrec readFB++  liftReadListPrec readPrecB readListPrecB =+    let readFB = wrapReadDict1 $ autoRead1 @f (ReadDict readPrecB readListPrecB)+     in _readListPrec readFB++-- | A newtype wrapper to derive @'Show2' f@ and @'Read2' f@ from the following,+--   often derivable instance.+--+--   > instance (Show a, Show b) => Show (f a b)+--   > instance (Read a, Read b) => Read (f a b)+--+-- ==== Example+--+-- Suppose you define a new type constructor @Bar@, and+-- derived its @Show@ instance.+--+-- >>> data Bar a b = Bar [(Int,a,b)] deriving Show+--+-- The derived @Show (Bar a b)@ instance is defined for all @a@ and @b@ with @Show@ instances.+--+-- > instance (Show a, Show b) => Show (Bar a b)+--+-- @Reflected2@ allows you to derive @'Show2' Bar@ instance from the above instance.+--+-- >>> :set -XStandaloneDeriving -XDerivingVia+-- >>> deriving via (Reflected2 Bar) instance Show2 Bar+--+-- Let's try the derived @Show2@ instance by showing @Bar Bool Char@, where+-- @True@ is shown as @yes@ and @False@ as @no@, instead of the normal @Show Bool@ instance.+--+-- >>> import Text.Show (showListWith)+-- >>> let yesno b = (++) (if b then "yes" else "no")+-- >>> liftShowsPrec2 (const yesno) (showListWith yesno) showsPrec showList 0 (Bar [(1, True, 'A'), (2, False, 'B')]) ""+-- "Bar [(1,yes,'A'),(2,no,'B')]"+newtype Reflected2 f a b = Reflected2 (f a b)++wrapShowDict2 :: ShowDict (f a b) -> ShowDict (Reflected2 f a b)+wrapShowDict2 = coerce++wrapReadDict2 :: ReadDict (f a b) -> ReadDict (Reflected2 f a b)+wrapReadDict2 = coerce++instance+  ( forall a b. (Show a, Show b) => Show (f a b),+    forall x1 y1 x2 y2.+    (Coercible x1 y1, Coercible x2 y2) =>+    Coercible (f x1 x2) (f y1 y2)+  ) =>+  Show2 (Reflected2 f)+  where+  liftShowsPrec2 showsPrecC showListC showsPrecD showListD =+    let showFCD = wrapShowDict2 $ autoShow2 @f (ShowDict showsPrecC showListC) (ShowDict showsPrecD showListD)+     in _showsPrec showFCD+  liftShowList2 showsPrecC showListC showsPrecD showListD =+    let showFCD = wrapShowDict2 $ autoShow2 @f (ShowDict showsPrecC showListC) (ShowDict showsPrecD showListD)+     in _showList showFCD++instance+  ( forall a b. (Read a, Read b) => Read (f a b),+    forall x1 y1 x2 y2.+    (Coercible x1 y1, Coercible x2 y2) =>+    Coercible (f x1 x2) (f y1 y2)+  ) =>+  Read2 (Reflected2 f)+  where+  liftReadPrec2 readPrecC readListPrecC readPrecD readListPrecD =+    let readFCD = wrapReadDict2 $ autoRead2 @f (ReadDict readPrecC readListPrecC) (ReadDict readPrecD readListPrecD)+     in _readPrec readFCD++  liftReadListPrec2 readPrecC readListPrecC readPrecD readListPrecD =+    let readFCD = wrapReadDict2 $ autoRead2 @f (ReadDict readPrecC readListPrecC) (ReadDict readPrecD readListPrecD)+     in _readListPrec readFCD
+ src/AutoLift/Machinery.hs view
@@ -0,0 +1,129 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE UndecidableInstances #-}+-- | Internal workings of "AutoLift". You usually don't need to import+--   this module.+module AutoLift.Machinery (+    AdHoc(..),+    ShowDict(..), showDict, autoShow1, autoShow2,+    ReadDict(..), readDict, autoRead1, autoRead2+) where++import Data.Reflection+import Data.Proxy+import Data.Coerce+import Text.Read++-- | Apply ad hoc instances on type @a@.+newtype AdHoc s a = AdHoc a++-- * Show++-- | Injected dictionary of Show+data ShowDict a = ShowDict+  { _showsPrec :: Int -> a -> ShowS+  , _showList :: [a] -> ShowS+  }++showDict :: forall a. Show a => ShowDict a+showDict = ShowDict { _showsPrec = showsPrec, _showList = showList }+{-# INLINE showDict #-}++instance (Reifies s (ShowDict a)) => Show (AdHoc s a) where+  showsPrec = coerce $ _showsPrec (reflect (Proxy @s))+  {-# INLINABLE showsPrec #-}++  showList = coerce $ _showList (reflect (Proxy @s))+  {-# INLINABLE showList #-}++{-++u/Iceland_jack taught me the technique to use QuantifiedConstraint on Coercible constraint. Thanks!++https://www.reddit.com/r/haskell_jp/comments/a75z0s/blog_reflection%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF/ed3efcv/++-}++-- | Automatic Show1+autoShow1 :: forall f b.+     (forall a. Show a => Show (f a))+  => (forall x y. Coercible x y => Coercible (f x) (f y))+  => ShowDict b+  -> ShowDict (f b)+autoShow1 showB = reify showB body+  where+    body :: forall name. Reifies name (ShowDict b) => Proxy name -> ShowDict (f b)+    body _ = coerce $ showDict @(f (AdHoc name b))+{-# INLINABLE autoShow1 #-}++-- | Automatic Show2+autoShow2 :: forall f c d.+     (forall a b. (Show a, Show b) => Show (f a b))+  => (forall x1 x2 y1 y2.+         (Coercible x1 y1, Coercible x2 y2) => Coercible (f x1 x2) (f y1 y2)+       )+  => ShowDict c+  -> ShowDict d+  -> ShowDict (f c d)+autoShow2 showC showD =+  reify showC $ \proxyC ->+    reify showD $ \proxyD ->+      body proxyC proxyD+  where+    body :: forall name1 name2. (Reifies name1 (ShowDict c), Reifies name2 (ShowDict d))+         => Proxy name1 -> Proxy name2 -> ShowDict (f c d)+    body _ _ = coerce $ showDict @(f (AdHoc name1 c) (AdHoc name2 d))+{-# INLINABLE autoShow2 #-}++-- * Read++-- | Injected dictionary of 'Read'+data ReadDict a = ReadDict+  { _readPrec :: ReadPrec a+  , _readListPrec :: ReadPrec [a]+  }++readDict :: forall a. Read a => ReadDict a+readDict = ReadDict{ _readPrec = readPrec, _readListPrec = readListPrec }+{-# INLINE readDict #-}++instance (Reifies s (ReadDict a)) => Read (AdHoc s a) where+  readPrec = coerce $ _readPrec (reflect (Proxy @s))+  {-# INLINABLE readPrec #-}+  readListPrec = coerce $ _readListPrec (reflect (Proxy @s))+  {-# INLINABLE readListPrec #-}++-- | Automatic Read1+autoRead1 :: forall f b.+     (forall a. Read a => Read (f a))+  => (forall x y. Coercible x y => Coercible (f x) (f y))+  => ReadDict b+  -> ReadDict (f b)+autoRead1 readB =+  reify readB body+  where+    body :: forall name. (Reifies name (ReadDict b)) => Proxy name -> ReadDict (f b)+    body _ = coerce (readDict @(f (AdHoc name b)))+{-# INLINABLE autoRead1 #-}++autoRead2 :: forall f c d.+     (forall a b. (Read a, Read b) => Read (f a b))+  => (forall x1 x2 y1 y2.+         (Coercible x1 y1, Coercible x2 y2) => Coercible (f x1 x2) (f y1 y2)+       )+  => ReadDict c+  -> ReadDict d+  -> ReadDict (f c d)+autoRead2 readC readD =+  reify readC $ \proxyC ->+    reify readD $ \proxyD ->+      body proxyC proxyD+  where+    body :: forall name1 name2. (Reifies name1 (ReadDict c), Reifies name2 (ReadDict d))+         => Proxy name1 -> Proxy name2 -> ReadDict (f c d)+    body _ _ = coerce (readDict @(f (AdHoc name1 c) (AdHoc name2 d)))+{-# INLINABLE autoRead2 #-}