packages feed

typed-digits (empty) → 0.1.0.0

raw patch · 10 files changed

+384/−0 lines, 10 filesdep +Globdep +QuickCheckdep +basesetup-changed

Dependencies added: Glob, QuickCheck, base, doctest, hspec, hspec-discover, nats, singletons, typed-digits

Files

+ ChangeLog.md view
@@ -0,0 +1,13 @@+# Changelog for typed-digits++Notable changes to this project will be documented in this file.++## Unreleased changes++None.++## 0.1.0.0 - 2019-12-12++Alpha release.++
+ LICENSE view
@@ -0,0 +1,19 @@+Copyright 2019 Arran D. Stewart++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to+deal in the Software without restriction, including without limitation the+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or+sell copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS+IN THE SOFTWARE.
+ README.md view
@@ -0,0 +1,31 @@+[![Build Status](https://travis-ci.org/arranstewart/hs-typed-digits.svg?branch=master)](https://travis-ci.org/arranstewart/hs-typed-digits)+[![Hackage](https://img.shields.io/hackage/v/typed-digits.svg)](https://hackage.haskell.org/package/typed-digits)++# typed-digits++Provides a Digit type, with the base of the digit available+as a type-level Nat. Thus, it can be guaranteed at compile-time+that digits of different bases can't be added together.++## Usage example++For more convenient provision of type-level parameters,+it's recommended to use the `DataKinds` and `TypeApplications` extensions.+Then, you can give type-level parameters using `@`, as seen below, instead of+giving the full type.++```haskell+>>> :set -XDataKinds -XTypeApplications+>>> import Data.TypedDigits+>>> let d = digit @9 3+>>> d+Just 3 (base 9)+```++## Source tree contents ++-   `src`: Library source files+-   `test`: Unit tests+-   `doctest`: Documentation tests using [`doctest`](https://github.com/sol/doctest)++
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ doctest/Main.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE CPP #-}++import Test.DocTest+import System.FilePath.Glob++-- doctest >= 0.15.0 adds useful "--verbose" flag+#if MIN_VERSION_doctest(0,15,0)+hasVerbose = True+#else+hasVerbose = False+#endif++main = do+  srcFiles <- globDir1 (compile "**/*.hs") "src"+  let doctestOpts = if hasVerbose+                    then ["-isrc", "--verbose"]+                    else ["-isrc"]+  doctest $ doctestOpts ++ srcFiles+
+ src/Data/TypedDigits.hs view
@@ -0,0 +1,30 @@+{-|++Module      : Data.TypedDigits+Description : Digits, indexed by their base at the type level+Portability : portable++= Description++Digits, indexed by their base at the type level.++-}++module Data.TypedDigits (+    Digit+  -- * Constructing 'Digits'+  , digit+  , digit'+  -- * Querying 'Digits'+  , getVal  +  , getBase+  , getBaseT+  -- * Arithmetic operations on 'Digits'+  , (<+>)+  , (<->)+  , KnownNat+  )+  where++import Data.TypedDigits.Internal+
+ src/Data/TypedDigits/Internal.hs view
@@ -0,0 +1,121 @@++{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}++{-|++Module      : Data.TypedDigits.Internal+Description : Data.TypedDigits internals+Portability : portable++This module is considered internal, and may change+without warning.+The Package Versioning Policy does not apply to it.++= Description++Digits, indexed by their base at the type level.++-}++module Data.TypedDigits.Internal (+    module Data.TypedDigits.Internal+  , KnownNat +  )+  where++import Data.Maybe+import Data.Proxy+import Data.Singletons.TypeLits (Nat, Sing(..), KnownNat )+import Data.Singletons          (singByProxy, SingI(..), fromSing )++-- | digits, indexed by their base at the type level+newtype Digit (base :: Nat) = Digit { getVal :: Int }++-- | @getBaseT p@: if @p@ is of some type @a base@,+-- then reflect the base into a value.+--+-- Useful for getting the base, just given a 'Proxy'; e.g.:+--+-- >>> :set -XDataKinds+-- >>> :set -XTypeApplications+-- >>> let p = Proxy :: Proxy 3+-- >>> getBaseT p+-- 3+getBaseT :: forall a base . (KnownNat base) => a base -> Int+getBaseT _ = fromIntegral $ fromSing s+  where+      s :: Sing base+      s = singByProxy (Proxy :: Proxy base)+++-- | @digit x@: construct a digit. 0 <= @x@ < base must hold+-- true.+--+-- Nicer if TypeApplications is enabled, then you can say:+--+-- >>> :set -XDataKinds+-- >>> :set -XTypeApplications+-- >>> digit @9 8+-- Just 8 (base 9)+digit :: forall base . (KnownNat base) => Int -> Maybe (Digit base)+digit x = +    if x >= 0 && x < fromIntegral (getBaseT (Proxy :: Proxy base))+    then Just $ Digit x+    else Nothing++-- | Like 'digit', but throws an 'ErrorCall' if the value is+-- out of range.+-- +-- sample usage:+-- +-- >>> :set -XDataKinds+-- >>> :set -XTypeApplications+-- >>> digit' @9 7+-- 7 (base 9)+digit' :: KnownNat base => Int -> Digit base+digit' x = fromMaybe (error "bad value for base") (digit x)++-- get the Sing type fore the base, given some digit+digitSing :: KnownNat base => Digit base -> Sing base+digitSing Digit{} =+    sing++-- | @getBase d@: return the base of the digit @d@+getBase :: KnownNat base => Digit base -> Int+getBase d = fromIntegral $ fromSing (digitSing d)++deriving instance Eq (Digit base)+deriving instance Ord (Digit base)++instance (KnownNat base) => Show (Digit base) where+  show d = show (getVal d) ++ " (base " ++ show (getBase d) ++ ")"++instance KnownNat base => Enum (Digit base) where+  toEnum = digit'+  fromEnum = getVal++instance (KnownNat base) => Bounded (Digit base) where+  minBound = digit' 0++  maxBound = digit' $ n - 1+    where+      n = getBaseT (Proxy :: Proxy base)++-- | Making 'Digit' an instance of 'Num' does not seem+-- sensible, but it's nevertheless useful to be able to+-- add and subtract digits, so '<+>' and '<->' are provided+-- for this.+--+-- @a <+> b@: Add @a@ and @b@. Throws an 'ErrorCall' if the+-- result is out of range.+(<+>) :: KnownNat base => Digit base -> Digit base -> Digit base+a <+> b = digit' $ getVal a + getVal b++-- | @a <-> b@: Subtract @b@ from @a@. Throws an 'ErrorCall' if the+-- result is out of range.+(<->) :: KnownNat base => Digit base -> Digit base -> Digit base+a <-> b = digit' $ getVal a - getVal b+
+ test/Data/TypedDigitsSpec.hs view
@@ -0,0 +1,76 @@++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeApplications #-}++module Data.TypedDigitsSpec (main, spec) where+++import Test.Hspec+import Test.QuickCheck++import Control.Monad+import Data.Proxy++import Data.TypedDigits+++--isInverseOf :: Eq a => (a -> b) -> (b -> a) -> a -> Bool+--isInverseOf f g x =+--  g (f x) == x+--+--+--+--sameAs :: Eq b => (a -> b) -> (a -> b) -> a -> Bool+--sameAs f g x =+--  f x == g x+--+--dflor = D.digits `sameAs` DD.digits 10+--+-- `main` is here so that this module can be run from GHCi on its own.  It is+-- not needed for automatic spec discovery.+main :: IO ()+main = hspec spec+--+--digitsImpls :: [(String, Int -> [Int])]+--digitsImpls = [+--    ("digits",      D.digits)+--  , ("digitsIter",  D.digitsIter)+--  , ("digitsPFree", D.digitsPFree)+--  ]++-- Let's do things in base 12.+type TestBase1 = 12++testBase1 :: Int+testBase1 = fromIntegral $ getBaseT (Proxy :: Proxy TestBase1)++-- digits can be added - up to their valid range+propAddable :: Property+propAddable =+  forAll (choose (0, testBase1-1)) $ \x ->+    let y = testBase1 - x - 1+        +        xd = digit' @TestBase1 x+        yd = digit' @TestBase1 y+    in  getVal (xd <+> yd) == testBase1 - 1+++spec :: Spec+spec = do +  describe "Digits" $ do+    it "can be added (up to their valid range)" $+      property propAddable+    it "have minBound 0" $ +      getVal (minBound :: Digit TestBase1) `shouldBe` 0+    it "have maxBound = base - 1" $ +      getVal (maxBound :: Digit TestBase1) `shouldBe` (testBase1 - 1)+  describe "digit" $ do+    it "yields Nothing if passed a negative" $+      digit @TestBase1 (-1) `shouldBe` Nothing+    it "yields Nothing if passed the base itself" $+      digit @TestBase1 testBase1 `shouldBe` Nothing+    it "yields the corresponding digit if passed anything between" $+      forM_ [0 .. testBase1 - 1] $ \n -> +        let x = digit @TestBase1 n+        in  fmap getVal x `shouldBe` Just n+
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ typed-digits.cabal view
@@ -0,0 +1,72 @@+name:           typed-digits+version:        0.1.0.0+synopsis:       Digits, indexed by their base at the type level+description:    Digits, indexed by their base at the type level.+                .+                Please see the README, for more details, available on GitHub at <https://github.com/arranstewart/hs-typed-digits#readme>.+category:       Data+homepage:       https://github.com/arranstewart/hs-typed-digits#readme+bug-reports:    https://github.com/arranstewart/hs-typed-digits/issues+author:         Arran Stewart+maintainer:     haskell-maintenance@arranstewart.info+copyright:      2019 Arran Stewart+license:        MIT+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md+cabal-version: >= 1.10+tested-with: GHC ==8.6.5 || ==8.4.4 || ==8.2.2++source-repository head+  type: git+  location: https://github.com/arranstewart/hs-typed-digits++Flag Noisy+    Description: Enable more compilation warnings+    Manual: True+    Default: False++library+  exposed-modules:+      Data.TypedDigits+      Data.TypedDigits.Internal+  hs-source-dirs:+      src+  build-depends:+      base >=4.7 && <5+    , nats+    , singletons >= 2.3+  default-language: Haskell98+  if flag(Noisy)+    ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints++test-suite test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Data.TypedDigitsSpec+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , typed-digits+    , hspec+    , hspec-discover+    , QuickCheck+  default-language: Haskell98++test-suite doctest+  type: exitcode-stdio-1.0+  main-is: Main.hs+  hs-source-dirs:+      doctest+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , typed-digits+    , doctest+    , Glob+  default-language: Haskell98