reflection 2.1.4 → 2.1.9
raw patch · 15 files changed
Files
- .hlint.yaml +2/−0
- .travis.yml +0/−161
- CHANGELOG.markdown +20/−0
- README.markdown +1/−1
- examples/Constraints.hs +16/−10
- examples/FromJSON.hs +24/−20
- examples/Monoid.hs +4/−2
- examples/ReaderLike.hs +3/−2
- examples/reflection-examples.cabal +25/−16
- fast/Data/Reflection.hs +108/−112
- reflection.cabal +40/−27
- slow/Data/Reflection.hs +51/−17
- tests/ReifyNatSpec.hs +50/−0
- tests/Spec.hs +1/−0
- tests/T47Spec.hs +64/−0
+ .hlint.yaml view
@@ -0,0 +1,2 @@+- ignore: {name: Unused LANGUAGE pragma}+- ignore: {name: Avoid lambda}
− .travis.yml
@@ -1,161 +0,0 @@-# This Travis job script has been generated by a script via-#-# runghc make_travis_yml_2.hs '-o' '.travis.yml' '--ghc-head' '--jobs=2' '--irc-channel=irc.freenode.org#haskell-lens' '--no-no-tests-no-bench' '--no-install-dependencies' 'cabal.project'-#-# For more information, see https://github.com/hvr/multi-ghc-travis-#-language: c-sudo: false--git:- submodules: false # whether to recursively clone submodules--notifications:- irc:- channels:- - "irc.freenode.org#haskell-lens"- skip_join: true- template:- - "\x0313reflection\x03/\x0306%{branch}\x03 \x0314%{commit}\x03 %{build_url} %{message}"--cache:- directories:- - $HOME/.cabal/packages- - $HOME/.cabal/store--before_cache:- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log- # remove files that are regenerated by 'cabal update'- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar- - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx-- - rm -rfv $HOME/.cabal/packages/head.hackage--matrix:- include:- - compiler: "ghc-8.6.1"- env: GHCHEAD=true- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.6.1], sources: [hvr-ghc]}}- - compiler: "ghc-8.4.3"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.3], sources: [hvr-ghc]}}- - compiler: "ghc-8.2.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.2], sources: [hvr-ghc]}}- - compiler: "ghc-8.0.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.2], sources: [hvr-ghc]}}- - compiler: "ghc-7.10.3"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.10.3], sources: [hvr-ghc]}}- - compiler: "ghc-7.8.4"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.4], sources: [hvr-ghc]}}- - compiler: "ghc-7.6.3"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.6.3], sources: [hvr-ghc]}}- - compiler: "ghc-7.4.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.4.2], sources: [hvr-ghc]}}- - compiler: "ghc-7.2.2"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.2.2], sources: [hvr-ghc]}}- - compiler: "ghc-7.0.4"- # env: TEST=--disable-tests BENCH=--disable-benchmarks- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.0.4], sources: [hvr-ghc]}}- - compiler: "ghc-head"- env: GHCHEAD=true- addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-head], sources: [hvr-ghc]}}-- allow_failures:- - compiler: "ghc-head"- - compiler: "ghc-7.0.4"- - compiler: "ghc-7.2.2"- - compiler: "ghc-8.6.1"--before_install:- - HC=${CC}- - HCPKG=${HC/ghc/ghc-pkg}- - unset CC- - ROOTDIR=$(pwd)- - mkdir -p $HOME/.local/bin- - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"- - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))- - echo $HCNUMVER--install:- - cabal --version- - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"- - BENCH=${BENCH---enable-benchmarks}- - TEST=${TEST---enable-tests}- - HADDOCK=${HADDOCK-true}- - UNCONSTRAINED=${UNCONSTRAINED-true}- - NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}- - GHCHEAD=${GHCHEAD-false}- - travis_retry cabal update -v- - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"- - rm -fv cabal.project cabal.project.local- - "sed -i.bak 's/^-- jobs:.*/jobs: 2/' ${HOME}/.cabal/config"- # Overlay Hackage Package Index for GHC HEAD: https://github.com/hvr/head.hackage- - |- if $GHCHEAD; then- sed -i 's/-- allow-newer: .*/allow-newer: *:base/' ${HOME}/.cabal/config- for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" ${HOME}/.cabal/config; done-- echo 'repository head.hackage' >> ${HOME}/.cabal/config- echo ' url: http://head.hackage.haskell.org/' >> ${HOME}/.cabal/config- echo ' secure: True' >> ${HOME}/.cabal/config- echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> ${HOME}/.cabal/config- echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> ${HOME}/.cabal/config- echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> ${HOME}/.cabal/config- echo ' key-threshold: 3' >> ${HOME}/.cabal.config-- grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'-- cabal new-update head.hackage -v- fi- - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'- - "printf 'packages: \".\"\\n' > cabal.project"- - touch cabal.project.local- - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"- - cat cabal.project || true- - cat cabal.project.local || true- - if [ -f "./configure.ac" ]; then- (cd "." && autoreconf -i);- fi- - rm -f cabal.project.freeze- - rm -rf .ghc.environment.* "."/dist- - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)--# Here starts the actual work to be performed for the package under test;-# any command which exits with a non-zero exit code causes the build to fail.-script:- # test that source-distributions can be generated- - (cd "." && cabal sdist)- - mv "."/dist/reflection-*.tar.gz ${DISTDIR}/- - cd ${DISTDIR} || false- - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;- - "printf 'packages: reflection-*/*.cabal\\n' > cabal.project"- - touch cabal.project.local- - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"- - cat cabal.project || true- - cat cabal.project.local || true-- # build & run tests, build benchmarks- - cabal new-build -w ${HC} ${TEST} ${BENCH} all-- # cabal check- - (cd reflection-* && cabal check)-- # haddock- - rm -rf ./dist-newstyle- - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi-- # Build without installed constraints for packages in global-db- - if $UNCONSTRAINED; then rm -f cabal.project.local; echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks all; else echo "Not building without installed constraints"; fi--# REGENDATA ["-o",".travis.yml","--ghc-head","--jobs=2","--irc-channel=irc.freenode.org#haskell-lens","--no-no-tests-no-bench","--no-install-dependencies","cabal.project"]-# EOF
CHANGELOG.markdown view
@@ -1,3 +1,23 @@+# 2.1.9 [2024.12.04]+* Drop support for pre-8.0 versions of GHC.++# 2.1.8 [2024.05.04]+* Fix a memory leak in `reifyTypeable`.++# 2.1.7 [2023.02.28]+* When building with `base-4.18` (GHC 9.6) or later, implement `reifyNat` and+ `reifySymbol` using the API provided by `GHC.TypeLits` instead of resorting+ to `unsafeCoerce`.++# 2.1.6 [2020.05.16]+* Fix a bug in which `give` (and possibly `reify`, `reifyNat`, and+ `reifySymbol`) could be unsoundly inlined by GHC 8.10 or older to produce+ incorrect runtime results.++# 2.1.5 [2019.08.27]+* Fix a bug in which `reifyNat` would yield incorrect results for very large+ `Integer`s on GHC 8.2 or later.+ # 2.1.4 [2018.07.02] * Allow building with GHC 8.6.
README.markdown view
@@ -1,7 +1,7 @@ reflection ========== -[](https://hackage.haskell.org/package/reflection) [](http://travis-ci.org/ekmett/reflection)+[](https://hackage.haskell.org/package/reflection) [](https://github.com/ekmett/reflection/actions?query=workflow%3AHaskell-CI) This package provides an implementation of the ideas presented in [Functional Pearl: Implicit Configurations](http://okmij.org/ftp/Haskell/tr-15-04.pdf) by Oleg Kiselyov and Chung-Chieh Shan. However, the API has been implemented in a much more efficient manner.
examples/Constraints.hs view
@@ -1,21 +1,24 @@-{-# LANGUAGE Rank2Types, TypeFamilies, TypeOperators, ConstraintKinds, PolyKinds, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, FlexibleContexts, UndecidableInstances #-}+{-# LANGUAGE Rank2Types, TypeFamilies, TypeOperators, ConstraintKinds, PolyKinds, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, FlexibleContexts, UndecidableInstances, CPP #-} module Constraints where -import Control.Newtype -- from newtype-import Data.Constraint -- from constraints-import Data.Constraint.Unsafe -- from constraints-import Data.Monoid -- from base-import Data.Proxy -- from tagged-import Data.Reflection -- from reflection+import Control.Newtype.Generics -- from newtype-generics+import Data.Constraint -- from constraints+import Data.Constraint.Unsafe -- from constraints+import Data.Proxy -- from tagged+import Data.Reflection -- from reflection+#if !(MIN_VERSION_base(4,11,0))+import Data.Semigroup -- from semigroups+#endif --- | Values in our dynamically constructed monoid over 'a'+-- | Values in our dynamically constructed monoid over @a@ newtype Lift (p :: * -> Constraint) (a :: *) (s :: *) = Lift { lower :: a } class ReifiableConstraint p where data Def (p :: * -> Constraint) (a :: *) reifiedIns :: Reifies s (Def p a) :- p (Lift p a s) -instance Newtype (Lift p a s) a where+instance Newtype (Lift p a s) where+ type O (Lift p a s) = a pack = Lift unpack = lower @@ -42,8 +45,11 @@ data Def Monoid a = Monoid { mappend_ :: a -> a -> a, mempty_ :: a } reifiedIns = Sub Dict +instance Reifies s (Def Monoid a) => Semigroup (Lift Monoid a s) where+ a <> b = Lift $ mappend_ (reflect a) (lower a) (lower b)+ instance Reifies s (Def Monoid a) => Monoid (Lift Monoid a s) where- mappend a b = Lift $ mappend_ (reflect a) (lower a) (lower b)+ mappend = (<>) mempty = a where a = Lift $ mempty_ (reflect a) data ClassProxy (p :: * -> Constraint) = ClassProxy
examples/FromJSON.hs view
@@ -1,27 +1,30 @@ -- Example of a dynamically generated FromJSON instance. ----- Can be useful when one needs to use a function with a --- FromJSON constraint, but some detail about the +-- Can be useful when one needs to use a function with a+-- FromJSON constraint, but some detail about the -- conversion from JSON is not known until runtime.+{-# LANGUAGE CPP #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} -import Data.Reflection -- from reflection-import Data.Monoid -- from base-import Data.Proxy -- from tagged-import Data.Text -- from text-import Data.Monoid -import Data.Aeson -- from aeson +import Data.Aeson -- from aeson+import qualified Data.Aeson.Key as Key import Data.Aeson.Types (Parser)+import Data.Proxy -- from tagged+import Data.Reflection -- from reflection+#if !(MIN_VERSION_base(4,11,0))+import Data.Semigroup -- from base+#endif+import Data.Text (Text, pack) -- from text import Control.Applicative -- These imports are only for constructing the example value-import Control.Lens (preview) -- from lens-import Data.Aeson.Lens (_Value,_String) -- form lens-aeson+import Lens.Micro.Aeson (_Value) -- form microlens-aeson+import Lens.Micro.Extras (preview) -- from microlens data Foo = Foo {@@ -31,19 +34,20 @@ fooParser :: Text -> Object -> Parser Foo fooParser prefix o = do- Foo <$> o .: (prefix <> "field1") <*> o .: (prefix <> "field2")+ Foo <$> o .: (Key.fromText prefix <> "field1") <*> o .: (Key.fromText prefix <> "field2") -- A wrapper over Foo carrying a phantom type s newtype J a s = J { runJ :: a }- --- If the phantom type s reifies the parsing function, we can --- use reflect to recover the function and implement ++-- If the phantom type s reifies the parsing function, we can+-- use reflect to recover the function and implement -- our FromJSON instance for J. instance Reifies s (Object -> Parser a) => FromJSON (J a s) where parseJSON (Object v) = J <$> reflect (Proxy :: Proxy s) v- + parseJSON _ = empty+ -- Convince the compiler that the phantom type in the proxy--- supplied by reify is the same as the phantom type in J. +-- supplied by reify is the same as the phantom type in J. -- -- Otherwise the FromJSON instance for J won't kick in. asProxyJ :: Proxy s -> J a s -> J a s@@ -53,18 +57,18 @@ exampleJSON = maybe Null id (preview _Value str) where str = "{ \"zzfield1\" : 5, \"zzfield2\" : 7 }"::Text- + main :: IO () main = do putStrLn "Enter prefix for the fields: " -- "zz" must be entered for the parse to succeed prefix <- fmap pack getLine- + -- fromJSON uses the dynamically generated FromJSON instance let result = reify (fooParser prefix) $ \proxy -> -- We must eliminate the J newtype before returning -- because, thanks to parametricity, -- the phantom type cannot escape the callback.- runJ . asProxyJ proxy <$> fromJSON exampleJSON - + runJ . asProxyJ proxy <$> fromJSON exampleJSON+ putStrLn (show (result :: Result Foo))
examples/Monoid.hs view
@@ -1,11 +1,13 @@ {-# LANGUAGE CPP, Rank2Types, FlexibleContexts, UndecidableInstances #-} module Monoid where +import Data.Proxy -- from tagged import Data.Reflection -- from reflection+#if !(MIN_VERSION_base(4,11,0)) import Data.Semigroup -- from base-import Data.Proxy -- from tagged+#endif --- | Values in our dynamically-constructed 'Monoid' over 'a'+-- | Values in our dynamically-constructed 'Monoid' over @a@ newtype M a s = M { runM :: a } deriving (Eq,Ord) -- | A dictionary describing a 'Monoid'
examples/ReaderLike.hs view
@@ -9,7 +9,6 @@ import Data.Proxy import Data.Reflection-import Control.Applicative import System.IO data MyConfig = MyConfig@@ -37,7 +36,7 @@ conf = reflect (Proxy :: Proxy p) run :: forall p. (Reifies p MyConfig) => Proxy p -> IO ()-run p = do+run _ = do d1 <- ask "Datum 1:" :: IO (Datum p) d2 <- ask "Datum 2:" :: IO (Datum p) -- look ma, no plumbing@@ -66,4 +65,6 @@ where conf = reflect (Proxy :: Proxy p) abs = undefined signum = undefined+ (-) = undefined+ negate = undefined fromInteger = Datum
examples/reflection-examples.cabal view
@@ -15,16 +15,19 @@ 2004 Oleg Kiselyov and Chung-chieh Shan build-type: Simple cabal-version: >= 1.10-tested-with: GHC == 7.0.4- , GHC == 7.2.2- , GHC == 7.4.2- , GHC == 7.6.3- , GHC == 7.8.4- , GHC == 7.10.3- , GHC == 8.0.2+tested-with: GHC == 8.0.2 , GHC == 8.2.2- , GHC == 8.4.3- , GHC == 8.6.1+ , GHC == 8.4.4+ , GHC == 8.6.5+ , GHC == 8.8.4+ , GHC == 8.10.7+ , GHC == 9.0.2+ , GHC == 9.2.8+ , GHC == 9.4.8+ , GHC == 9.6.6+ , GHC == 9.8.4+ , GHC == 9.10.1+ , GHC == 9.12.1 flag examples default: True@@ -34,24 +37,28 @@ buildable: False exposed-modules: Constraints Monoid- build-depends: base >= 4.8 && < 5,+ build-depends: base >= 4.9 && < 5, constraints,- newtype,+ newtype-generics >= 0.5.3, reflection,+ semigroups, tagged hs-source-dirs: . ghc-options: -Wall+ if impl(ghc >= 8.6)+ ghc-options: -Wno-star-is-type default-language: Haskell2010 executable reflection-from-json if !flag(examples) buildable: False main-is: FromJSON.hs- build-depends: aeson,- base >= 4.8 && < 5,- lens,- lens-aeson,+ build-depends: aeson >= 2 && < 2.3,+ base >= 4.9 && < 5,+ microlens,+ microlens-aeson >= 2.5.1, reflection,+ semigroups, tagged, text hs-source-dirs: .@@ -62,10 +69,12 @@ if !flag(examples) buildable: False main-is: ReaderLike.hs- build-depends: base >= 4.8 && < 5,+ build-depends: base >= 4.9 && < 5, reflection, tagged hs-source-dirs: . ghc-options: -Wall -threaded -rtsopts+ if impl(ghc >= 8.6)+ ghc-options: -Wno-star-is-type default-language: Haskell2010
fast/Data/Reflection.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}@@ -6,36 +7,23 @@ {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PatternGuards #-}+{-# LANGUAGE PolyKinds #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE ScopedTypeVariables #-}-#if __GLASGOW_HASKELL__ >= 706-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE PolyKinds #-} {-# LANGUAGE TypeOperators #-}-#define USE_TYPE_LITS 1-#endif #ifdef MIN_VERSION_template_haskell-# if __GLASGOW_HASKELL__ >= 800 -- TH-subset that works with stage1 & unregisterised GHCs {-# LANGUAGE TemplateHaskellQuotes #-}-# else-{-# LANGUAGE TemplateHaskell #-}-# endif #endif {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-cse #-} {-# OPTIONS_GHC -fno-full-laziness #-} {-# OPTIONS_GHC -fno-float-in #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# OPTIONS_GHC -fno-warn-unused-binds #-}--#ifndef MIN_VERSION_base-#define MIN_VERSION_base(x,y,z) 1-#endif+{-# OPTIONS_GHC -Wno-orphans #-}+{-# OPTIONS_GHC -Wno-unused-binds #-} ---------------------------------------------------------------------------- -- |@@ -77,10 +65,8 @@ -- * Reflection Reifies(..) , reify-#if __GLASGOW_HASKELL__ >= 708 , reifyNat , reifySymbol-#endif , reifyTypeable -- * Given , Given(..)@@ -108,31 +94,26 @@ ) where import Control.Applicative+import Control.Exception #ifdef MIN_VERSION_template_haskell import Control.Monad #endif import Data.Bits--#if __GLASGOW_HASKELL__ < 710-import Data.Foldable-#endif--import Data.Semigroup as Sem+import Data.Coerce (Coercible, coerce) import Data.Proxy--#if __GLASGOW_HASKELL__ < 710-import Data.Traversable-#endif-+import Data.Semigroup as Sem import Data.Typeable import Data.Word import Foreign.Ptr import Foreign.StablePtr -#if (__GLASGOW_HASKELL__ >= 707) || (defined(MIN_VERSION_template_haskell) && USE_TYPE_LITS) import GHC.TypeLits+#if MIN_VERSION_base(4,10,0)+import qualified Numeric.Natural as Numeric (Natural)+#else+import Control.Exception (ArithException(..), throw) #endif #ifdef __HUGS__@@ -149,10 +130,21 @@ import Unsafe.Coerce #endif -#ifdef HLINT-{-# ANN module "HLint: ignore Avoid lambda" #-}+#if MIN_VERSION_base(4,18,0)+import qualified GHC.TypeNats as TN #endif +-- Due to https://gitlab.haskell.org/ghc/ghc/issues/16893, inlining+-- unsafeCoerce too aggressively can cause optimization to become unsound on+-- old versions of GHC. As a workaround, we mark unsafeCoerce-using definitions+-- as NOINLINE where necessary.+-- See https://github.com/ekmett/reflection/issues/47.+#if __GLASGOW_HASKELL__ >= 811+# define INLINE_UNSAFE_COERCE INLINE+#else+# define INLINE_UNSAFE_COERCE NOINLINE+#endif+ ------------------------------------------------------------------------------ -- Reifies ------------------------------------------------------------------------------@@ -167,28 +159,27 @@ -- | Reify a value at the type level, to be recovered with 'reflect'. reify :: forall a r. a -> (forall (s :: *). Reifies s a => Proxy s -> r) -> r reify a k = unsafeCoerce (Magic k :: Magic a r) (const a) Proxy-{-# INLINE reify #-}+{-# INLINE_UNSAFE_COERCE reify #-} -#if __GLASGOW_HASKELL__ >= 707 instance KnownNat n => Reifies n Integer where reflect = natVal instance KnownSymbol n => Reifies n String where reflect = symbolVal-#endif -#if __GLASGOW_HASKELL__ >= 708- -------------------------------------------------------------------------------- -- KnownNat -------------------------------------------------------------------------------- -newtype MagicNat r = MagicNat (forall (n :: Nat). KnownNat n => Proxy n -> r)- -- | This upgraded version of 'reify' can be used to generate a 'KnownNat' suitable for use with other APIs. --+-- Attemping to pass a negative 'Integer' as an argument will result in an+-- 'Underflow' exception.+-- -- /Available only on GHC 7.8+/ --+-- >>> import GHC.TypeLits+-- -- >>> reifyNat 4 natVal -- 4 --@@ -196,28 +187,59 @@ -- 4 reifyNat :: forall r. Integer -> (forall (n :: Nat). KnownNat n => Proxy n -> r) -> r-reifyNat n k = unsafeCoerce (MagicNat k :: MagicNat r) n Proxy+#if MIN_VERSION_base(4,18,0)+-- With base-4.18 or later, we can use the API in GHC.TypeNats to define this+-- function directly.+reifyNat n k = TN.withSomeSNat (fromInteger n :: Numeric.Natural) $+ \(sn :: (SNat n)) -> TN.withKnownNat sn $ k (Proxy :: Proxy n)+{-# INLINE reifyNat #-}+#else+-- On older versions of base, we resort to unsafeCoerce.+reifyNat n k = unsafeCoerce (MagicNat k :: MagicNat r)+# if MIN_VERSION_base(4,10,0)+ -- Starting with base-4.10, the internal+ -- representation of KnownNat changed from Integer+ -- to Natural, so make sure to perform the same+ -- conversion before unsafeCoercing.+ (fromInteger n :: Numeric.Natural)+# else+ (if n < 0 then throw Underflow else n)+# endif+ Proxy+{-# INLINE_UNSAFE_COERCE reifyNat #-} +newtype MagicNat r = MagicNat (forall (n :: Nat). KnownNat n => Proxy n -> r)+#endif+ -------------------------------------------------------------------------------- -- KnownSymbol -------------------------------------------------------------------------------- -newtype MagicSymbol r = MagicSymbol (forall (n :: Symbol). KnownSymbol n => Proxy n -> r)- -- | This upgraded version of 'reify' can be used to generate a 'KnownSymbol' suitable for use with other APIs. -- -- /Available only on GHC 7.8+/ --+-- >>> import GHC.TypeLits+-- -- >>> reifySymbol "hello" symbolVal -- "hello" -- -- >>> reifySymbol "hello" reflect -- "hello" reifySymbol :: forall r. String -> (forall (n :: Symbol). KnownSymbol n => Proxy n -> r) -> r+#if MIN_VERSION_base(4,18,0)+-- With base-4.18 or later, we can use the API in GHC.TypeNats to define this+-- function directly.+reifySymbol s k = withSomeSSymbol s $ \(ss :: SSymbol s) -> withKnownSymbol ss (k (Proxy :: Proxy s))+{-# INLINE reifySymbol #-}+#else+-- On older versions of base, we resort to unsafeCoerce. reifySymbol n k = unsafeCoerce (MagicSymbol k :: MagicSymbol r) n Proxy-+{-# INLINE_UNSAFE_COERCE reifySymbol #-} #endif +newtype MagicSymbol r = MagicSymbol (forall (n :: Symbol). KnownSymbol n => Proxy n -> r)+ ------------------------------------------------------------------------------ -- Given ------------------------------------------------------------------------------@@ -238,7 +260,7 @@ -- are in scope, then the behavior is implementation defined. give :: forall a r. a -> (Given a => r) -> r give a k = unsafeCoerce (Gift k :: Gift a r) a-{-# INLINE give #-}+{-# INLINE_UNSAFE_COERCE give #-} -------------------------------------------------------------------------------- -- Explicit Numeric Reflection@@ -288,9 +310,7 @@ -- in the \"Functional Pearl: Implicit Configurations\" paper by Oleg Kiselyov and Chung-Chieh Shan. -- -- @instance Num (Q Exp)@ provided in this package allows writing @$(3)@--- instead of @$(int 3)@. Sometimes the two will produce the same--- representation (if compiled without the @-DUSE_TYPE_LITS@ preprocessor--- directive).+-- instead of @$(int 3)@. int :: Int -> TypeQ int n = case quotRem n 2 of (0, 0) -> conT ''Z@@ -307,12 +327,6 @@ | n >= 0 = int n | otherwise = error "nat: negative" -#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 704-instance Show (Q a) where- show _ = "Q"-instance Eq (Q a) where- _ == _ = False-#endif instance Num a => Num (Q a) where (+) = liftM2 (+) (*) = liftM2 (*)@@ -329,29 +343,13 @@ -- | This permits the use of $(5) as a type splice. instance Num Type where-#ifdef USE_TYPE_LITS LitT (NumTyLit a) + LitT (NumTyLit b) = LitT (NumTyLit (a+b)) a + b = AppT (AppT (VarT ''(+)) a) b LitT (NumTyLit a) * LitT (NumTyLit b) = LitT (NumTyLit (a*b)) (*) a b = AppT (AppT (VarT ''(GHC.TypeLits.*)) a) b-#if MIN_VERSION_base(4,8,0) a - b = AppT (AppT (VarT ''(-)) a) b-#else- (-) = error "Type.(-): undefined"-#endif fromInteger = LitT . NumTyLit-#else- (+) = error "Type.(+): undefined"- (*) = error "Type.(*): undefined"- (-) = error "Type.(-): undefined"- fromInteger n = case quotRem n 2 of- (0, 0) -> ConT ''Z- (q,-1) -> ConT ''PD `AppT` fromInteger q- (q, 0) -> ConT ''D `AppT` fromInteger q- (q, 1) -> ConT ''SD `AppT` fromInteger q- _ -> error "ghc is bad at math"-#endif abs = error "Type.abs" signum = error "Type.signum" @@ -386,27 +384,12 @@ signum = onProxyType1 signum fromInteger n = ConE 'Proxy `SigE` (ConT ''Proxy `AppT` fromInteger n) -#ifdef USE_TYPE_LITS addProxy :: Proxy a -> Proxy b -> Proxy (a + b) addProxy _ _ = Proxy mulProxy :: Proxy a -> Proxy b -> Proxy (a * b) mulProxy _ _ = Proxy-#if MIN_VERSION_base(4,8,0) subProxy :: Proxy a -> Proxy b -> Proxy (a - b) subProxy _ _ = Proxy-#else-subProxy :: Proxy a -> Proxy b -> Proxy c-subProxy _ _ = error "Exp.(-): undefined"-#endif--- fromInteger = LitT . NumTyLit-#else-addProxy :: Proxy a -> Proxy b -> Proxy c-addProxy _ _ = error "Exp.(+): undefined"-mulProxy :: Proxy a -> Proxy b -> Proxy c-mulProxy _ _ = error "Exp.(*): undefined"-subProxy :: Proxy a -> Proxy b -> Proxy c-subProxy _ _ = error "Exp.(-): undefined"-#endif #endif @@ -448,7 +431,7 @@ GO(T253,253) GO(T254,254) GO(T255,255) #define GO(Tn,n) \- newtype Tn = Tn Tn deriving Typeable; \+ newtype Tn = Tn Tn; \ instance B Tn where { \ reflectByte _ = n \ };@@ -466,9 +449,16 @@ _ -> impossible } -newtype W (b0 :: *) (b1 :: *) (b2 :: *) (b3 :: *) = W (W b0 b1 b2 b3) deriving Typeable-newtype Stable (w0 :: *) (w1 :: *) (a :: *) = Stable (Stable w0 w1 a) deriving Typeable+newtype W (b0 :: *) (b1 :: *) (b2 :: *) (b3 :: *) = W (W b0 b1 b2 b3)+newtype StableBox (w0 :: *) (w1 :: *) (a :: *) = StableBox (StableBox w0 w1 a)+newtype Stable (w0 :: *) (w1 :: *) (a :: *) = Stable (Stable w0 w1 a) +data Box a = Box a++stableBox :: p (Stable w1 w2 a) -> Proxy (StableBox w1 w2 a)+stableBox _ = Proxy+{-# INLINE stableBox #-}+ stable :: p b0 -> p b1 -> p b2 -> p b3 -> p b4 -> p b5 -> p b6 -> p b7 -> Proxy (Stable (W b0 b1 b2 b3) (W b4 b5 b6 b7) a) stable _ _ _ _ _ _ _ _ = Proxy@@ -482,35 +472,35 @@ intPtrToStablePtr = castPtrToStablePtr . intPtrToPtr {-# INLINE intPtrToStablePtr #-} -byte0 :: p (Stable (W b0 b1 b2 b3) w1 a) -> Proxy b0+byte0 :: p (StableBox (W b0 b1 b2 b3) w1 a) -> Proxy b0 byte0 _ = Proxy -byte1 :: p (Stable (W b0 b1 b2 b3) w1 a) -> Proxy b1+byte1 :: p (StableBox (W b0 b1 b2 b3) w1 a) -> Proxy b1 byte1 _ = Proxy -byte2 :: p (Stable (W b0 b1 b2 b3) w1 a) -> Proxy b2+byte2 :: p (StableBox (W b0 b1 b2 b3) w1 a) -> Proxy b2 byte2 _ = Proxy -byte3 :: p (Stable (W b0 b1 b2 b3) w1 a) -> Proxy b3+byte3 :: p (StableBox (W b0 b1 b2 b3) w1 a) -> Proxy b3 byte3 _ = Proxy -byte4 :: p (Stable w0 (W b4 b5 b6 b7) a) -> Proxy b4+byte4 :: p (StableBox w0 (W b4 b5 b6 b7) a) -> Proxy b4 byte4 _ = Proxy -byte5 :: p (Stable w0 (W b4 b5 b6 b7) a) -> Proxy b5+byte5 :: p (StableBox w0 (W b4 b5 b6 b7) a) -> Proxy b5 byte5 _ = Proxy -byte6 :: p (Stable w0 (W b4 b5 b6 b7) a) -> Proxy b6+byte6 :: p (StableBox w0 (W b4 b5 b6 b7) a) -> Proxy b6 byte6 _ = Proxy -byte7 :: p (Stable w0 (W b4 b5 b6 b7) a) -> Proxy b7+byte7 :: p (StableBox w0 (W b4 b5 b6 b7) a) -> Proxy b7 byte7 _ = Proxy argument :: (p s -> r) -> Proxy s argument _ = Proxy instance (B b0, B b1, B b2, B b3, B b4, B b5, B b6, B b7, w0 ~ W b0 b1 b2 b3, w1 ~ W b4 b5 b6 b7)- => Reifies (Stable w0 w1 a) a where+ => Reifies (StableBox w0 w1 a) (Box a) where reflect = r where r = unsafePerformIO $ const <$> deRefStablePtr p <* freeStablePtr p s = argument r@@ -525,22 +515,30 @@ (reflectByte (byte7 s) `shiftL` 56) {-# NOINLINE reflect #-} --- This had to be moved to the top level, due to an apparent bug in--- the ghc inliner introduced in ghc 7.0.x-reflectBefore :: forall (proxy :: * -> *) s b. (Proxy s -> b) -> proxy s -> b-reflectBefore f = const $! f Proxy-{-# NOINLINE reflectBefore #-}+instance Reifies (StableBox w0 w1 a) (Box b) => Reifies (Stable w0 w1 a) b where+ reflect p = case reflect (stableBox p) of+ Box a -> a +-- Ensure that exactly one dictionary of Reifies (StableBox ...) is created and evaluated per reifyTypeable call.+--+-- Evaluating the dictionary's thunk frees the allocated StablePtr, and the contents of the StablePtr replace the thunk.+-- Creating two dictionaries would mean a double free upon their evaluation, and leaving a dictionary unevaluated would+-- leak the StablePtr (see https://github.com/ekmett/reflection/issues/54).+--+-- To separate evaluation of the dictionary and evaluation of the actual argument passed to reifyTypeable, we insert a+-- Box in between.+withStableBox :: Reifies (StableBox w0 w1 a) (Box a) => (Reifies (Stable w0 w1 a) a => Proxy (Stable w0 w1 a) -> r) -> Proxy (Stable w0 w1 a) -> IO r+withStableBox k p = do+ _ <- evaluate $ reflect (stableBox p)+ evaluate $ k p+{-# NOINLINE withStableBox #-}+ -- | Reify a value at the type level in a 'Typeable'-compatible fashion, to be recovered with 'reflect'. -- -- This can be necessary to work around the changes to @Data.Typeable@ in GHC HEAD. reifyTypeable :: Typeable a => a -> (forall (s :: *). (Typeable s, Reifies s a) => Proxy s -> r) -> r-#if MIN_VERSION_base(4,4,0) reifyTypeable a k = unsafeDupablePerformIO $ do-#else-reifyTypeable a k = unsafePerformIO $ do-#endif- p <- newStablePtr a+ p <- newStablePtr (Box a) let n = stablePtrToIntPtr p reifyByte (fromIntegral n) (\s0 -> reifyByte (fromIntegral (n `shiftR` 8)) (\s1 ->@@ -550,9 +548,7 @@ reifyByte (fromIntegral (n `shiftR` 40)) (\s5 -> reifyByte (fromIntegral (n `shiftR` 48)) (\s6 -> reifyByte (fromIntegral (n `shiftR` 56)) (\s7 ->- reflectBefore (fmap return k) $- stable s0 s1 s2 s3 s4 s5 s6 s7))))))))-+ withStableBox k $ stable s0 s1 s2 s3 s4 s5 s6 s7)))))))) data ReifiedMonoid a = ReifiedMonoid { reifiedMappend :: a -> a -> a, reifiedMempty :: a } @@ -577,7 +573,7 @@ reifyMonoid f z m xs = reify (ReifiedMonoid f z) (unreflectedMonoid (m xs)) -- | Fold a value using its 'Foldable' instance using--- explicitly provided 'Monoid' operations. This is like 'fold'+-- explicitly provided 'Monoid' operations. This is like 'Data.Foldable.fold' -- where the 'Monoid' instance can be manually specified. -- -- @@@ -634,5 +630,5 @@ sequenceBy :: Traversable t => (forall x. x -> f x) -> (forall x y. f (x -> y) -> f x -> f y) -> t (f a) -> f (t a) sequenceBy pur app = reifyApplicative pur app (traverse ReflectedApplicative) -(#.) :: (b -> c) -> (a -> b) -> a -> c-(#.) _ = unsafeCoerce+(#.) :: Coercible c b => (b -> c) -> (a -> b) -> (a -> c)+(#.) _ = coerce (\x -> x :: b) :: forall a b. Coercible b a => a -> b
reflection.cabal view
@@ -1,5 +1,5 @@ name: reflection-version: 2.1.4+version: 2.1.9 license: BSD3 license-file: LICENSE author: Edward A. Kmett, Elliott Hird, Oleg Kiselyov and Chung-chieh Shan@@ -25,20 +25,28 @@ and Chung-chieh Shan (<http://okmij.org/ftp/Haskell/tr-15-04.pdf original paper>). However, the API has been streamlined to improve performance. .- Austin Seipp's tutorial <https://www.schoolofhaskell.com/user/thoughtpolice/using-reflection Reflecting values to types and back> provides a summary of the+ There are a handful of tutorials on how to use this library:+ .+ * Austin Seipp's tutorial <https://www.schoolofhaskell.com/user/thoughtpolice/using-reflection Reflecting values to types and back> provides a summary of the approach taken by this library, along with more motivating examples.-tested-with: GHC == 7.0.4- , GHC == 7.2.2- , GHC == 7.4.2- , GHC == 7.6.3- , GHC == 7.8.4- , GHC == 7.10.3- , GHC == 8.0.2+ .+ * Arnaud Spiwack's tutorial <https://www.tweag.io/posts/2017-12-21-reflection-tutorial.html All about reflection> explains how to use this library.+tested-with: GHC == 8.0.2 , GHC == 8.2.2- , GHC == 8.4.3- , GHC == 8.6.1+ , GHC == 8.4.4+ , GHC == 8.6.5+ , GHC == 8.8.4+ , GHC == 8.10.7+ , GHC == 9.0.2+ , GHC == 9.2.8+ , GHC == 9.4.8+ , GHC == 9.6.6+ , GHC == 9.8.4+ , GHC == 9.10.1+ , GHC == 9.12.1 extra-source-files:+ .hlint.yaml examples/reflection-examples.cabal examples/LICENSE examples/*.hs@@ -46,7 +54,6 @@ README.markdown slow/Data/Reflection.hs fast/Data/Reflection.hs- .travis.yml flag slow description:@@ -68,28 +75,18 @@ library ghc-options: -Wall+ if impl(ghc >= 8.6)+ ghc-options: -Wno-star-is-type - if impl(ghc >= 7.2)- default-extensions: Trustworthy+ default-extensions: Trustworthy build-depends:- base >= 2 && < 5-- if impl(ghc < 7.8)- build-depends:- tagged >= 0.4.4 && < 1-- if !impl(ghc >= 8.0)- build-depends:- semigroups >= 0.11 && < 0.19+ base >= 4.9 && < 5 default-language: Haskell98 if flag(template-haskell) && impl(ghc)- if !impl(ghc >= 8.0)- other-extensions: TemplateHaskell- -- else- -- other-extensions: TemplateHaskellQuotes -- Hackage doesn't know this extension yet+ -- other-extensions: TemplateHaskellQuotes -- Hackage doesn't know this extension yet build-depends: template-haskell if !flag(slow) && (impl(ghc) || impl(hugs))@@ -105,3 +102,19 @@ CPP exposed-modules: Data.Reflection++test-suite spec+ type: exitcode-stdio-1.0+ hs-source-dirs: tests+ main-is: Spec.hs+ other-modules: ReifyNatSpec+ T47Spec+ ghc-options: -Wall+ default-language: Haskell98+ build-tool-depends: hspec-discover:hspec-discover >= 1.8+ build-depends:+ base >= 4.9 && < 5,+ containers >= 0.1 && < 0.8,+ hspec >= 2 && < 3,+ QuickCheck >= 2 && < 3,+ reflection
slow/Data/Reflection.hs view
@@ -1,12 +1,15 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE MonoLocalBinds #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE Rank2Types #-}+{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-cse #-} {-# OPTIONS_GHC -fno-full-laziness #-} {-# OPTIONS_GHC -fno-float-in #-}-{-# OPTIONS_GHC -fno-warn-unused-binds #-}+{-# OPTIONS_GHC -Wno-unused-binds #-} ---------------------------------------------------------------------------- -- | -- Module : Data.Reflection@@ -49,6 +52,7 @@ import Foreign.StablePtr import System.IO.Unsafe import Control.Applicative+import Control.Exception import Data.Proxy import Data.Bits import Data.Word@@ -115,9 +119,18 @@ -- reified type. reflect :: proxy s -> a +newtype StableBox b0 b1 b2 b3 b4 b5 b6 b7 a =+ StableBox (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) newtype Stable b0 b1 b2 b3 b4 b5 b6 b7 a = Stable (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) +data Box a = Box a++stableBox :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)+ -> Proxy (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a)+stableBox _ = Proxy+{-# INLINE stableBox #-}+ stable :: p b0 -> p b1 -> p b2 -> p b3 -> p b4 -> p b5 -> p b6 -> p b7 -> Proxy (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) stable _ _ _ _ _ _ _ _ = Proxy@@ -131,35 +144,35 @@ intPtrToStablePtr = castPtrToStablePtr . intPtrToPtr {-# INLINE intPtrToStablePtr #-} -byte0 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b0+byte0 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b0 byte0 _ = Proxy -byte1 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b1+byte1 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b1 byte1 _ = Proxy -byte2 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b2+byte2 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b2 byte2 _ = Proxy -byte3 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b3+byte3 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b3 byte3 _ = Proxy -byte4 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b4+byte4 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b4 byte4 _ = Proxy -byte5 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b5+byte5 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b5 byte5 _ = Proxy -byte6 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b6+byte6 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b6 byte6 _ = Proxy -byte7 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b7+byte7 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b7 byte7 _ = Proxy argument :: (p s -> r) -> Proxy s argument _ = Proxy instance (B b0, B b1, B b2, B b3, B b4, B b5, B b6, B b7)- => Reifies (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) a where+ => Reifies (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) (Box a) where reflect = r where r = unsafePerformIO $ const <$> deRefStablePtr p <* freeStablePtr p s = argument r@@ -174,12 +187,34 @@ (reflectByte (byte7 s) `shiftL` 56) {-# NOINLINE reflect #-} --- This had to be moved to the top level, due to an apparent bug in--- the ghc inliner introduced in ghc 7.0.x-reflectBefore :: Reifies s a => (Proxy s -> b) -> proxy s -> b-reflectBefore f = const $! f Proxy-{-# NOINLINE reflectBefore #-}+instance Reifies (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) (Box b)+ => Reifies (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) b where+ reflect p = case reflect (stableBox p) of+ Box a -> a +-- Ensure that exactly one dictionary of Reifies (StableBox ...) is created and+-- evaluated per reifyTypeable call.+--+-- Evaluating the dictionary's thunk frees the allocated StablePtr, and the+-- contents of the StablePtr replace the thunk. Creating two dictionaries would+-- mean a double free upon their evaluation, and leaving a dictionary+-- unevaluated would leak the StablePtr+-- (see https://github.com/ekmett/reflection/issues/54).+--+-- To separate evaluation of the dictionary and evaluation of the actual+-- argument passed to reifyTypeable, we insert a Box in between.+withStableBox+ :: Reifies (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) (Box a)+ => (Reifies (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) a+ => Proxy (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)+ -> r)+ -> Proxy (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)+ -> IO r+withStableBox k p = do+ _ <- evaluate $ reflect (stableBox p)+ evaluate $ k p+{-# NOINLINE withStableBox #-}+ -- | Reify a value at the type level, to be recovered with 'reflect'. reify :: a -> (forall s. (Reifies s a) => Proxy s -> r) -> r reify a k = unsafeDupablePerformIO $ do@@ -193,5 +228,4 @@ reifyByte (fromIntegral (n `shiftR` 40)) (\s5 -> reifyByte (fromIntegral (n `shiftR` 48)) (\s6 -> reifyByte (fromIntegral (n `shiftR` 56)) (\s7 ->- reflectBefore (fmap return k) $- stable s0 s1 s2 s3 s4 s5 s6 s7))))))))+ withStableBox k $ stable s0 s1 s2 s3 s4 s5 s6 s7))))))))
+ tests/ReifyNatSpec.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}+module ReifyNatSpec where++import Data.Reflection+import Test.Hspec.QuickCheck+import Test.QuickCheck (NonNegative(..))++#if MIN_VERSION_base(4,10,0)+import GHC.TypeNats (natVal)+import Numeric.Natural (Natural)+#endif++#if __GLASGOW_HASKELL__ != 900+import Control.Exception (ArithException(..), evaluate)+import Test.QuickCheck (Negative(..))+#endif++import Test.Hspec++main :: IO ()+main = hspec spec++spec :: Spec+spec =+ describe "reifyNat" $ do+ prop "reify positive Integers and reflect them back" $+ \(NonNegative (i :: Integer)) -> reifyNat i $ \p -> reflect p `shouldBe` i+#if __GLASGOW_HASKELL__ != 900+ -- Inexplicably, this test fails on GHC 9.0 with hspec-2.8.4 or later.+ -- Moreover, I suspect that undefined behavior is involved in some way,+ -- as the output of hspec will occasionally be swallowed entirely. I have+ -- not been able to observe this phenomenon on any other version of GHC,+ -- so I will simply disable this test on 9.0.+ prop "should throw an Underflow exception on negative inputs" $+ \(Negative (i :: Integer)) ->+ reifyNat i (evaluate . reflect) `shouldThrow` (== Underflow)+#endif+#if MIN_VERSION_base(4,10,0)+ it "should reflect very large Naturals correctly" $ do -- #41+ let d42, d2_63, d2_64 :: Natural+ d42 = 42+ d2_63 = 2^(63 :: Natural)+ d2_64 = 2^(64 :: Natural)+ reifyNat (toInteger d42) $ \p -> natVal p `shouldBe` d42+ reifyNat (toInteger (d2_63-1)) $ \p -> natVal p `shouldBe` d2_63-1+ reifyNat (toInteger d2_63) $ \p -> natVal p `shouldBe` d2_63+ reifyNat (toInteger (d2_64-1)) $ \p -> natVal p `shouldBe` d2_64-1+ reifyNat (toInteger d2_64) $ \p -> natVal p `shouldBe` d2_64+#endif
+ tests/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ tests/T47Spec.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+-- | A regression test for issue #47.+module T47Spec where++import qualified Data.Map as M+import Data.Map (Map)+import Data.Reflection+import Test.Hspec++main :: IO ()+main = hspec spec++spec :: Spec+spec =+ describe "Given" $ do+ it "should give Normal properly" $+ give Normal (toJSON (Foo Bar)) `shouldBe`+ Object (M.fromList [("Foo",String "Bar")])+ it "should give ViaShow properly" $+ give ViaShow (toJSON (Foo Bar)) `shouldBe`+ Object (M.fromList [("Foo",String "SHOWBAR")])++----------------------------------------------------------------------------+-- Types+----------------------------------------------------------------------------++data Foo = Foo Bar++instance Show Foo where+ show _ = "SHOWFOO"++data Bar = Bar | BarBar++instance Show Bar where+ show _ = "SHOWBAR"++----------------------------------------------------------------------------+-- ToJSON instances+----------------------------------------------------------------------------++instance Given Style => ToJSON Foo where+ toJSON (Foo x) = Object $ M.singleton "Foo" (toJSON x)++instance Given Style => ToJSON Bar where+ toJSON x = case given of+ Normal -> String $ case x of+ Bar -> "Bar"+ BarBar -> "BarBar"+ ViaShow -> String $ show x++data Style = Normal | ViaShow++----------------------------------------------------------------------------+-- Minimized aeson+----------------------------------------------------------------------------++class ToJSON a where+ toJSON :: a -> Value++data Value+ = Object !(Map String Value)+ | String !String+ deriving (Eq, Show)