diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2017, Ignat Insarov
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/primes-type.cabal b/primes-type.cabal
new file mode 100644
--- /dev/null
+++ b/primes-type.cabal
@@ -0,0 +1,47 @@
+-- This file has been generated from package.yaml by hpack version 0.17.1.
+--
+-- see: https://github.com/sol/hpack
+
+name:           primes-type
+version:        0.2.0.0
+synopsis:       Type-safe prime numbers.
+description:    This library provides type safe prime numbers. The idea is based upon the concept of a predicate type from type theory.
+category:       Algorithms, Numerical
+homepage:       https://github.com/kindaro/primes-type#readme
+bug-reports:    https://github.com/kindaro/primes-type/issues
+author:         Ignat Insarov
+maintainer:     kindaro@gmail.com
+copyright:      2017 Ignat Insarov
+license:        ISC
+license-file:   LICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
+
+source-repository head
+  type: git
+  location: https://github.com/kindaro/primes-type
+
+library
+  hs-source-dirs:
+      src
+  build-depends:
+      base >=4.8 && <4.11
+    , primes >=0.2 && <0.3
+  exposed-modules:
+      Data.Numbers.Primes.Type
+  other-modules:
+      Paths_primes_type
+  default-language: Haskell2010
+
+test-suite primes-type-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.8 && <5
+    , primes >=0.2 && <0.3
+    , primes-type
+    , HTF >=0.13 && < 0.14
+  default-language: Haskell2010
diff --git a/src/Data/Numbers/Primes/Type.hs b/src/Data/Numbers/Primes/Type.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Numbers/Primes/Type.hs
@@ -0,0 +1,59 @@
+{-# OPTIONS_GHC -funbox-strict-fields #-}
+
+module Data.Numbers.Primes.Type
+    ( Prime
+    , getValue
+    , getIndex
+    , deconstruct
+    , primeIndex
+    , getPrime
+    , maybePrime
+    , indexedPrimes
+    ) where
+
+import Data.List (elemIndex)
+import Data.Numbers.Primes
+
+-- | An abstract type for primes.
+--
+--   It will only ever hold a valid prime, together with its zero-based index.
+data Prime int = Prime { _value :: !int, _index :: !Int } deriving Show
+
+-- | Given a Prime, give back its value.
+getValue :: Prime int -> int
+getValue = _value
+
+-- | Given a Prime, give back its index.
+getIndex :: Prime int -> Int
+getIndex = _index
+
+-- | Given a Prime, give back its value and index as a tuple.
+deconstruct :: Prime int -> (int, Int)
+deconstruct p = (_value p, _index p)
+
+instance Integral int => Enum (Prime int) where
+    toEnum = getPrime
+    fromEnum = getIndex
+
+instance Eq (Prime int) where
+    x == y = getIndex x == getIndex y
+
+instance Ord (Prime int) where
+    x `compare` y = getIndex x `compare` getIndex y
+
+-- | If a given number is prime, give its index.
+primeIndex :: Integral n => n -> Maybe Int
+primeIndex x | isPrime x = elemIndex x primes
+             | otherwise = Nothing
+
+-- | Give n-th prime.
+getPrime :: Integral int => Int -> Prime int
+getPrime n = Prime (primes !! n) n
+
+-- | If a given number is prime, give it back wrapped as such.
+maybePrime :: (Integral int) => int -> Maybe (Prime int)
+maybePrime x = Prime x <$> primeIndex x
+
+-- | A list of indexed primes.
+indexedPrimes :: Integral int => [Prime int]
+indexedPrimes = getPrime <$> [0,1..]
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,103 @@
+{-# OPTIONS_GHC -F -pgmF htfpp #-}
+
+module Main where
+
+import Test.Framework
+import Data.Numbers.Primes
+import Data.Numbers.Primes.Type
+import Data.Word
+
+instance (Arbitrary a, Integral a) => Arbitrary (Prime a) where
+    arbitrary = getPrime <$> arbitrarySizedNatural
+
+main = htfMain htf_thisModulesTests
+
+-- | Some basic sanity checks.
+
+test_prime = do
+    assertEqual (Just $ getPrime 0) (maybePrime 2)
+    assertEqual (Just $ getPrime 5) (maybePrime 13)
+    assertEqual (primeIndex 2) (Just 0)
+    assertEqual (primeIndex 13) (Just 5)
+    assertEqual (maybePrime 4) (Nothing)
+
+-- | These two properties show that Prime and its value are
+--   isomorphic if and only if the value is prime.
+--   (n in N but not in P are mapped to Nothing with no inverse.)
+
+-- prop_maybePrime :: Integral int => int -> Bool -- ^ This is too general to suit a prop.
+prop_maybePrime_Int = withQCArgs (\prop -> prop { maxSize = 1024 } ) prop_maybePrime_Int'
+  where prop_maybePrime_Int' :: Int -> Bool
+        prop_maybePrime_Int' x = r == Just x || r == Nothing
+          where r :: Maybe Int
+                r = getValue <$> maybePrime x
+
+prop_maybePrime_Integer = withQCArgs (\prop -> prop { maxSize = 1024 } ) prop_maybePrime_Integer'
+  where prop_maybePrime_Integer' :: Integer -> Bool
+        prop_maybePrime_Integer' x = r == Just x || r == Nothing
+          where r :: Maybe Integer
+                r = getValue <$> maybePrime x
+
+prop_maybePrime_Word = withQCArgs (\prop -> prop { maxSize = 5 } ) prop_maybePrime_Word'
+  where prop_maybePrime_Word' :: Word8 -> Bool
+        prop_maybePrime_Word' x = r == Just x || r == Nothing
+          where r :: Maybe Word8
+                r = getValue <$> maybePrime x
+
+prop_getValue_Int = withQCArgs (\prop -> prop { maxSize = 1024 } ) prop_getValue_Int'
+  where
+    prop_getValue_Int' :: Int -> Bool
+    prop_getValue_Int' n = (maybePrime . getValue $ p) == Just p
+      where
+        p :: Prime Int
+        p | n < 0 = getPrime . abs $ n -- I know it's not super bright.
+          | otherwise = getPrime n
+
+-- | These two properties show that Prime and its index are isomorphic.
+
+prop_primeIndex :: Int -> Bool
+prop_primeIndex n = m == (getIndex <$> getPrime) m
+  where m = abs n
+
+prop_getIndex :: Int -> Bool
+prop_getIndex n = getPrime m == (getPrime . getIndex . getPrime) m
+  where m = abs n
+
+-- | This property verifies the correctness of `deconstruct`.
+
+prop_deconstruct i = deconstruct p == (getValue p, getIndex p)
+  where
+    p = getPrime (abs i)
+
+-- | These two properties show that the enumeration associated with Prime is isomorphic.
+
+prop_fromEnum :: Prime Int -> Bool
+prop_fromEnum p = p == (toEnum . fromEnum) p
+
+prop_toEnum :: Int -> Bool
+prop_toEnum n' = n == (fromEnum . (toEnum :: Int -> Prime Int)) n
+  where
+    n = abs n'
+
+-- | Equality & ordering of primes is the same as the respective relations on their indices.
+
+prop_Eq = withQCArgs (\prop -> prop { maxSize = 256 } ) prop_Eq'
+  where
+    prop_Eq' :: (Int, Int) -> Bool
+    prop_Eq' (i', j') = (i == j) == (getPrime i == getPrime j)
+      where
+        i = abs i'
+        j = abs j'
+
+prop_compare = withQCArgs (\prop -> prop { maxSize = 256 } ) prop_compare'
+  where
+    prop_compare' :: (Int, Int) -> Bool
+    prop_compare' (i', j') = i `compare` j == (getPrime i `compare` getPrime j)
+      where
+        i = abs i'
+        j = abs j'
+
+-- | The list of indexed primes corresponds to its unzips.
+test_indexedPrimes = assertEqual
+                        (deconstruct <$> take 100 indexedPrimes)
+                        (take 100 $ zip primes [0..])
