packages feed

singlethongs (empty) → 0.1

raw patch · 8 files changed

+358/−0 lines, 8 filesdep +basedep +singlethongsdep +template-haskell

Dependencies added: base, singlethongs, template-haskell

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# 0.1++* Initial version, more or less based on `singletons-2.5.1`.++* Builds with GHC 8.6.5, GHC 8.8.2, GHCJS 8.6.0.
+ LICENSE view
@@ -0,0 +1,33 @@+Copyright (c) 2020, Renzo Carbonara. All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++1. Redistributions of source code must retain the above copyright notice, this+list of conditions and the following disclaimer.++2. 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.++3. Neither the name of the author nor the names of its 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 HOLDER 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.++--++This project is based on "singletons", Copyright (c) 2012 Richard+Eisenberg, licenced under the terms of the BSD-3-Clause license at+https://hackage.haskell.org/package/singletons-2.5.1/src/LICENSE+
+ README.md view
@@ -0,0 +1,14 @@+# Singlethongs++Like [singletons](https://hackage.haskell.org/package/singletons), but much+smaller.++# Development++* Build all with `nix-build`.++* Build with some GHC or GHCJS version with `nix-build -A $xxx`, where `xxx` is+  one of `ghc865`, `ghc882`, `ghcjs86`.++* Enter a development environment with `nix-shell -A $xxx.env`, where `xxx` is+  one of `ghc865`, `ghc882`, `ghcjs86`.
+ lib/Singlethongs.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE DataKinds, GADTs, KindSignatures, TemplateHaskell, TypeFamilies #-}++{- | The goal of+the [singlethongs](https://hackage.haskell.org/package/singlethongs) library+is to offer the bare minimum of what+the [singletons](https://hackage.haskell.org/package/singletons) library offers+in a small package that's easy to compile across different GHC versions,+including GHCJS.++This module exports a minimal reproduction of what+the [singletons](https://hackage.haskell.org/package/singletons) package offers.+Namely 'Sing', 'SingI', 'SomeSing' and 'SingKind', as well as @TemplateHaskell@+support for generating the relevant instances for custom types. If there is+some feature that you thing could be added to this library,+please [suggest it](https://gitlab.com/k0001/singlethongs/issues).++The types exported by this module are not the same types as the types as the+one exported by+the [singletons](https://hackage.haskell.org/package/singletons) package.+Even if they have the same names and implementation, they are not seen as+equal by the type-checker. They are only intended to be a drop-in replacement.+-}+module Singlethongs+ ( Sing+ , SingI(..)+ , SomeSing(..)+ , withSomeSing+ , SingKind(..)+   -- * Template Haskell+ , singlethongs+   -- * Re-exports+ , TestEquality(testEquality)+ , (:~:)(Refl)+ ) where++import Singlethongs.Internal+import Singlethongs.TH+import Data.Type.Equality+
+ lib/Singlethongs/Internal.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE GADTs, KindSignatures, PolyKinds, TypeFamilies, RankNTypes #-}+module Singlethongs.Internal+ ( Sing+ , SingI(sing)+ , SomeSing(SomeSing)+ , withSomeSing+ , SingKind(Demote, fromSing, toSing)+ ) where++data family Sing (a :: k)++class SingI (a :: k) where+  sing :: Sing a++data SomeSing k where+  SomeSing :: Sing (a :: k) -> SomeSing k++withSomeSing :: SingKind k => Demote k -> (forall (a :: k). Sing a -> r) -> r+withSomeSing x f = case toSing x of SomeSing x' -> f x'+{-# INLINE withSomeSing #-}++class SingKind k where+  type Demote k :: *+  fromSing :: Sing (a :: k) -> Demote k+  toSing :: Demote k -> SomeSing k
+ lib/Singlethongs/TH.hs view
@@ -0,0 +1,124 @@+{-# LANGUAGE CPP, LambdaCase, TemplateHaskell #-}+module Singlethongs.TH (singlethongs) where++import Data.Type.Equality+import Language.Haskell.TH+import Singlethongs.Internal++{-| Generate 'Sing', 'SingI', 'SingKind' and 'TestEquality' instances for a+datatype.++Given a datatype like @Foo@ below, having one or more unary constructors:++@+data Foo = Bar | Qux++'singlethongs' ''Foo+@++The following code will be generated:++@+data instance 'Sing' (x :: Foo) where+  SBar :: 'Sing' 'Bar+  SQux :: 'Sing' 'Qux++instance 'SingKind' Fobo where+  type 'Demote' Foo = Foo+  'fromSing' SBar = Bar+  'fromSing' SQux = Qux+  'toSing' Bar = 'SomeSing' SBar+  'toSing' Qux = 'SomeSing' SQux++instance 'SingI' 'Bar where 'sing' = SBar+instance 'SingI' 'Qux where 'sing' = SQux++instance 'TestEquality' ('Sing' :: Foo -> *) where+  'testEquality' SBar SBar = 'Just' 'Refl'+  'testEquality' SQux SQux = 'Just' 'Refl'+  'testEquality' _ _ = 'Nothing'+@++In order to use this 'singlethongs' function, you will need to enable the+following GHC extensions:++@+\{\-\# LANGUAGE DataKinds, GADTs, KindSignatures, TemplateHaskell, TypeFamilies \#\-\}+@+-}++singlethongs :: Name -> Q [Dec]+singlethongs n0 = reify n0 >>= \case+  TyConI (DataD [] n1 [] Nothing cons@(_:_) []) -> do+    nCons <- traverse conName cons+    out0 <- genDataInstSing n1 nCons+    out1 <- genInstanceSingKind n1 nCons+    out2 <- genInstanceTestEquality n1 nCons+    out3 <- mconcat <$> traverse genInstanceSingI nCons+    pure (out0 <> out1 <> out2 <> out3)+  _ -> fail "Only enum types are acceptable"++conName :: Con -> Q Name+conName = \case+  NormalC n [] -> pure n+  _ -> fail "Only enum types are acceptable"++sName :: Name -> Name+sName a = mkName ("S" <> nameBase a)++genDataInstSing :: Name -> [Name] -> Q [Dec]+genDataInstSing nTy nCons = do+  let cons1 = flip fmap nCons $ \nCon ->+        GadtC [sName nCon] [] (AppT (ConT ''Sing) (PromotedT nCon))+  pure [mkSingDataInstD nTy cons1]++genInstanceSingI :: Name -> Q [Dec]+genInstanceSingI nCon = do+  let singD = FunD (mkName "sing") [Clause [] (NormalB (ConE (sName nCon))) []]+  pure [InstanceD Nothing [] (AppT (ConT ''SingI) (PromotedT nCon)) [singD]]++genInstanceSingKind :: Name -> [Name] -> Q [Dec]+genInstanceSingKind nTy nCons = do+  let fromSingD = FunD (mkName "fromSing") $ flip fmap nCons $ \nCon ->+        Clause [ConP (sName nCon) []] (NormalB (ConE nCon)) []+      toSingD = FunD (mkName "toSing") $ flip fmap nCons $ \nCon ->+        Clause [ConP nCon []]+               (NormalB (AppE (ConE 'SomeSing) (ConE (sName nCon)))) []+  pure [InstanceD Nothing [] (AppT (ConT ''SingKind) (ConT nTy))+         [mkDemoteD nTy, fromSingD, toSingD] ]++genInstanceTestEquality :: Name -> [Name] -> Q [Dec]+genInstanceTestEquality nTy nCons = do+  let teD = FunD (mkName "testEquality") $ mconcat+       [ flip fmap nCons $ \nCon ->+           let p = ConP (sName nCon) []+           in Clause [p, p] (NormalB (AppE (ConE 'Just) (ConE 'Refl))) []+       , case nCons of+           [_] -> []+           _ -> [Clause [WildP, WildP] (NormalB (ConE 'Nothing)) []]+       ]+  pure [InstanceD Nothing []+          (AppT (ConT ''TestEquality)+                (SigT (ConT ''Sing)+                      (AppT (AppT ArrowT (ConT nTy)) StarT)))+          [teD ]]+++mkDemoteD :: Name -> Dec+mkDemoteD nTy =+#if MIN_VERSION_template_haskell(2,15,0)+  TySynInstD (TySynEqn Nothing (AppT (ConT ''Demote) (ConT nTy)) (ConT nTy))+#else+  TySynInstD ''Demote (TySynEqn [ConT nTy] (ConT nTy))+#endif++mkSingDataInstD :: Name -> [Con] -> Dec+mkSingDataInstD nTy cons =+#if MIN_VERSION_template_haskell(2,15,0)+  DataInstD [] Nothing (AppT (ConT ''Sing) (SigT (VarT (mkName "x")) (ConT nTy)))+            Nothing cons []+#else+  DataInstD [] ''Sing [SigT (VarT (mkName "x")) (ConT nTy)] Nothing cons []+#endif++
+ singlethongs.cabal view
@@ -0,0 +1,42 @@+cabal-version: 2.4+name: singlethongs+version: 0.1+license: BSD-3-Clause+license-file: LICENSE+extra-source-files:  README.md CHANGELOG.md+author: Renzo Carbonara+maintainer: renλren.zone+copyright: Copyright (c) Renzo Carbonara 2020+category: Data+build-type: Simple+synopsis: Like singletons, but much smaller.+description: Like singletons, but much smaller.+homepage: https://gitlab.com/k0001/singlethongs+bug-reports: https://gitlab.com/k0001/singlethongs/issues+tested-with:+  GHC == 8.6.0,+  GHC == 8.6.5,+  GHC == 8.8.2++library+  hs-source-dirs: lib+  ghc-options: -Wall -Werror=incomplete-patterns -O2+  ghcjs-options: -Wall -Werror=incomplete-patterns -O3+  default-language: Haskell2010+  exposed-modules:+    Singlethongs+  other-modules:+    Singlethongs.Internal+    Singlethongs.TH+  build-depends:+    base ==4.*,+    template-haskell,++test-suite test+  default-language: Haskell2010+  type: exitcode-stdio-1.0+  hs-source-dirs: test+  main-is: Main.hs+  build-depends:+    base,+    singlethongs,
+ test/Main.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE DataKinds, GADTs, KindSignatures, TemplateHaskell, TypeFamilies,+             ScopedTypeVariables, TypeApplications #-}+{-# OPTIONS_GHC -ddump-splices #-}++module Main (main) where++import Control.Monad (when)++-- We import qualified to test that TH names don't make silly assumptions.+import qualified Singlethongs as S+++data A = Ax deriving (Eq)+S.singlethongs ''A+{- =====>+data instance S.Sing (x :: A) where+  SAx :: S.Sing 'Ax++instance S.SingKind A where+  type S.Demote A = A+  fromSing SAx = Ax+  toSing Ax = S.SomeSing SAx++instance S.TestEquality (S.Sing :: A -> *) where+  testEquality SAx SAx = Just S.Refl++instance S.SingI 'Ax where sing = SAx+-}++t_A :: Bool+t_A = and+  [ S.fromSing (SAx :: S.Sing 'Ax) == (Ax :: S.Demote A)+  , S.withSomeSing @A Ax (\sAx -> S.fromSing sAx == Ax)+  , S.testEquality SAx SAx == Just S.Refl+  ]++data B = Bx | By deriving (Eq)+S.singlethongs ''B+{- =====>++data instance S.Sing (x :: B) where+  SBx :: S.Sing 'Bx+  SBy :: S.Sing 'By++instance S.SingKind B where+  type S.Demote B = B+  fromSing SBx = Bx+  fromSing SBy = By+  toSing Bx = S.SomeSing SBx+  toSing By = S.SomeSing SBy++instance S.TestEquality (S.Sing :: B -> *) where+  testEquality SBx SBx = Just S.Refl+  testEquality SBy SBy = Just S.Refl+  testEquality _ _ = Nothing++instance S.SingI  'Bx where sing = SBx+instance S.SingI  'By where sing = SBy+-}++t_B :: Bool+t_B = and+  [ S.fromSing (SBx :: S.Sing 'Bx) == (Bx :: S.Demote B)+  , S.withSomeSing @B Bx (\sBx -> S.fromSing sBx == Bx)+  , S.fromSing (SBy :: S.Sing 'By) == (By :: S.Demote B)+  , S.withSomeSing @B By (\sBy -> S.fromSing sBy == By)+  , S.testEquality SBx SBx == Just S.Refl+  , S.testEquality SBx SBy == Nothing+  , S.testEquality SBy SBx == Nothing+  , S.testEquality SBy SBy == Just S.Refl+  ]++main :: IO ()+main = do+  when (not t_A) $ fail "Something wrong with t_A"+  when (not t_B) $ fail "Something wrong with t_B"