diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2022 Gautier DI FOLCO
+
+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/sized-wrapper.cabal b/sized-wrapper.cabal
new file mode 100644
--- /dev/null
+++ b/sized-wrapper.cabal
@@ -0,0 +1,86 @@
+cabal-version:       3.0
+name:                sized-wrapper
+version:             0.1.0.0
+author:              Gautier DI FOLCO
+maintainer:          gautier.difolco@gmail.com
+category:            Data
+build-type:          Simple
+license:             ISC
+license-file:        LICENSE
+synopsis:            Create a Sized version of any container
+description:         Create a Sized version of any container.
+Homepage:            http://github.com/blackheaven/sized-wrapper/sized-wrapper
+tested-with:         GHC==9.2.2, GHC==9.0.2, GHC==8.10.7
+
+library
+  default-language:   Haskell2010
+  build-depends:
+        base == 4.*
+  hs-source-dirs: src
+  exposed-modules:
+      Data.Sized
+  other-modules:
+      Paths_sized_wrapper
+  autogen-modules:
+      Paths_sized_wrapper
+  default-extensions:
+      DataKinds
+      DefaultSignatures
+      DeriveAnyClass
+      DeriveGeneric
+      DerivingStrategies
+      DerivingVia
+      DuplicateRecordFields
+      FlexibleContexts
+      GADTs
+      GeneralizedNewtypeDeriving
+      KindSignatures
+      LambdaCase
+      OverloadedLists
+      OverloadedStrings
+      RankNTypes
+      RecordWildCards
+      ScopedTypeVariables
+      TypeApplications
+      TypeFamilies
+      TypeOperators
+  ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints
+
+test-suite sized-wrapper-test
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Spec.hs
+  other-modules:
+      Data.SizedSpec
+      Paths_sized_wrapper
+  autogen-modules:
+      Paths_sized_wrapper
+  default-extensions:
+      DataKinds
+      DefaultSignatures
+      DeriveAnyClass
+      DeriveGeneric
+      DerivingStrategies
+      DerivingVia
+      DuplicateRecordFields
+      FlexibleContexts
+      GADTs
+      GeneralizedNewtypeDeriving
+      KindSignatures
+      LambdaCase
+      OverloadedLists
+      OverloadedStrings
+      RankNTypes
+      RecordWildCards
+      ScopedTypeVariables
+      TypeApplications
+      TypeFamilies
+      TypeOperators
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints
+  build-depends:
+      base
+    , sized-wrapper
+    , hspec
+    , hspec-core
+    , hspec-discover
+  default-language: Haskell2010
diff --git a/src/Data/Sized.hs b/src/Data/Sized.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Sized.hs
@@ -0,0 +1,366 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE UndecidableInstances #-}
+{-# OPTIONS_GHC -Wno-redundant-constraints #-}
+
+-- |
+-- Module        : Data.Sized
+-- Copyright     : Gautier DI FOLCO
+-- License       : BSD2
+--
+-- Maintainer    : Gautier DI FOLCO <gautier.difolco@gmail.com>
+-- Stability     : Unstable
+-- Portability   : GHC
+--
+-- Create Sized version of any container.
+module Data.Sized
+  ( -- * Base type
+    Sized,
+    getSized,
+    trustedSized,
+    unknownSized,
+    Unknown,
+    Between,
+    Exactly,
+    AtLeast,
+    AtMost,
+
+    -- * Singleton constructor
+    SizedSingleton (..),
+    singleton,
+    MkSizedSingletonApplicative (..),
+
+    -- * From container
+    Size (..),
+    SizedFromContainer (..),
+    MkSizedFromContainerFoldable (..),
+
+    -- * Operations
+    overSized,
+    overSized2,
+    overSized3,
+    overSized4,
+    overSized5,
+    trustedChangeOverSized,
+    trustedChangeOverSized2,
+    trustedChangeOverSized3,
+    trustedChangeOverSized4,
+    trustedChangeOverSized5,
+    fmapSized,
+    withSized,
+    precise,
+    approximate,
+    (<<>>),
+    withSizedAppend,
+    withSizedRetract,
+    withSizedSubtract,
+    withSizedProduct,
+
+    -- * Type-level
+    IsMoreGeneral,
+    IsNotEmpty,
+    type (<+>),
+    RestrictAtMost,
+    type (<->),
+    type (<*>),
+    Includes,
+  )
+where
+
+import Data.Foldable (toList)
+import Data.Ix (inRange)
+import Data.Kind
+import Data.Maybe (fromJust)
+import Data.Proxy
+import GHC.TypeLits as TypeLits
+
+-- | Sized proofed value.
+newtype Sized s a = Sized
+  { -- | Extract the Sized proven value
+    getSized :: a
+  }
+  deriving stock (Eq, Ord, Show)
+
+-- * Different kind of sizes
+
+-- | Unknown/any size
+data Unknown
+
+-- | Exactly 'n'
+data Exactly (n :: Nat)
+
+-- | At least 'n'
+data AtLeast (n :: Nat)
+
+-- | At most 'n'
+data AtMost (n :: Nat)
+
+-- | Between (included) '(n..m)'
+data Between (n :: Nat) (m :: Nat)
+
+-- * Operations
+
+-- | Wrap and unwrap 'Sized' (unsafe, be sure 'f' is size-conservative)
+overSized :: (a -> b) -> Sized s a -> Sized s b
+overSized f = trustedSized . f . getSized
+{-# INLINE overSized #-}
+
+-- | Wrap and unwrap 'Sized' (unsafe, be sure 'f' is size-conservative)
+overSized2 :: (a -> b -> c) -> Sized s a -> Sized s b -> Sized s c
+overSized2 f a = trustedSized . f (getSized a) . getSized
+{-# INLINE overSized2 #-}
+
+-- | Wrap and unwrap 'Sized' (unsafe, be sure 'f' is size-conservative)
+overSized3 :: (a -> b -> c -> d) -> Sized s a -> Sized s b -> Sized s c -> Sized s d
+overSized3 f a b = trustedSized . f (getSized a) (getSized b) . getSized
+{-# INLINE overSized3 #-}
+
+-- | Wrap and unwrap 'Sized' (unsafe, be sure 'f' is size-conservative)
+overSized4 :: (a -> b -> c -> d -> e) -> Sized s a -> Sized s b -> Sized s c -> Sized s d -> Sized s e
+overSized4 f a b c = trustedSized . f (getSized a) (getSized b) (getSized c) . getSized
+{-# INLINE overSized4 #-}
+
+-- | Wrap and unwrap 'Sized' (unsafe, be sure 'f' is size-conservative)
+overSized5 :: (a -> b -> c -> d -> e -> f) -> Sized s a -> Sized s b -> Sized s c -> Sized s d -> Sized s e -> Sized s f
+overSized5 f a b c d = trustedSized . f (getSized a) (getSized b) (getSized c) (getSized d) . getSized
+{-# INLINE overSized5 #-}
+
+-- | Wrap and unwrap 'Sized' (unsafe, be sure 'f' is respects output size)
+trustedChangeOverSized :: (a -> b) -> Sized s0 a -> Sized s1 b
+trustedChangeOverSized f = trustedSized . f . getSized
+{-# INLINE trustedChangeOverSized #-}
+
+-- | Wrap and unwrap 'Sized' (unsafe, be sure 'f' is respects output size)
+trustedChangeOverSized2 :: (a -> b -> c) -> Sized s0 a -> Sized s1 b -> Sized s2 c
+trustedChangeOverSized2 f a = trustedSized . f (getSized a) . getSized
+{-# INLINE trustedChangeOverSized2 #-}
+
+-- | Wrap and unwrap 'Sized' (unsafe, be sure 'f' is respects output size)
+trustedChangeOverSized3 :: (a -> b -> c -> d) -> Sized s0 a -> Sized s1 b -> Sized s2 c -> Sized s3 d
+trustedChangeOverSized3 f a b = trustedSized . f (getSized a) (getSized b) . getSized
+{-# INLINE trustedChangeOverSized3 #-}
+
+-- | Wrap and unwrap 'Sized' (unsafe, be sure 'f' is respects output size)
+trustedChangeOverSized4 :: (a -> b -> c -> d -> e) -> Sized s0 a -> Sized s1 b -> Sized s2 c -> Sized s3 d -> Sized s4 e
+trustedChangeOverSized4 f a b c = trustedSized . f (getSized a) (getSized b) (getSized c) . getSized
+{-# INLINE trustedChangeOverSized4 #-}
+
+-- | Wrap and unwrap 'Sized' (unsafe, be sure 'f' is respects output size)
+trustedChangeOverSized5 :: (a -> b -> c -> d -> e -> f) -> Sized s0 a -> Sized s1 b -> Sized s2 c -> Sized s3 d -> Sized s4 e -> Sized s5 f
+trustedChangeOverSized5 f a b c d = trustedSized . f (getSized a) (getSized b) (getSized c) (getSized d) . getSized
+{-# INLINE trustedChangeOverSized5 #-}
+
+-- | 'fmap' over a 'Sized' container
+fmapSized :: Functor f => (a -> b) -> Sized s (f a) -> Sized s (f b)
+fmapSized f = overSized (fmap f)
+{-# INLINE fmapSized #-}
+
+-- | Apply an unsafe function over empty, which is safe over 'Sized'
+withSized :: (a -> Maybe b) -> Sized s a -> b
+withSized f = fromJust . f . getSized
+{-# INLINE withSized #-}
+
+-- | Trusted value
+trustedSized :: a -> Sized s a
+trustedSized = Sized
+{-# INLINE trustedSized #-}
+
+-- | Unknown value
+unknownSized :: a -> Sized Unknown a
+unknownSized = Sized
+{-# INLINE unknownSized #-}
+
+-- | Singleton constructible value
+class SizedSingleton a where
+  type SizedSingletonElement a :: Type
+  sizedSingleton :: Proxy a -> SizedSingletonElement a -> a
+
+-- | Build a 'Sized' value from a singleton value
+singleton :: SizedSingleton a => Proxy a -> SizedSingletonElement a -> Sized (Exactly 1) a
+singleton p = trustedSized . sizedSingleton p
+{-# INLINE singleton #-}
+
+-- | Build 'SizedSingleton' for 'Applicative' defined types
+--   to be used with 'DerivingVia':
+--
+--   > deriving instance SizedSingleton [a] via (MkSizedSingletonApplicative [a])
+newtype MkSizedSingletonApplicative a
+  = MkSizedSingletonApplicative a
+
+instance Applicative f => SizedSingleton (f a) where
+  type SizedSingletonElement (f a) = a
+  sizedSingleton _ = pure
+
+-- * From container
+
+-- | Used to attempt conversion from possibly any size to 'Sized'.
+class SizedFromContainer a where
+  calculateSize :: a -> Int
+  isAtLeast :: Int -> a -> Bool
+  isAtLeast n = (>= n) . calculateSize
+  isAtMost :: Int -> a -> Bool
+  isAtMost n = (<= n) . calculateSize
+  isExactly :: Int -> a -> Bool
+  isExactly n = (== n) . calculateSize
+  isBetween :: Int -> Int -> a -> Bool
+  isBetween n m = inRange (n, m) . calculateSize
+
+-- | Convert a container from possibly any size to 'Sized'.
+class Size s where
+  sized :: SizedFromContainer a => a -> Maybe (Sized s a)
+
+instance Size Unknown where
+  sized = Just . trustedSized
+
+instance KnownNat n => Size (Exactly n) where
+  sized = mkSized $ isExactly $ fromInteger $ natVal $ Proxy @n
+
+instance KnownNat n => Size (AtLeast n) where
+  sized = mkSized $ isAtLeast $ fromInteger $ natVal $ Proxy @n
+
+instance KnownNat n => Size (AtMost n) where
+  sized = mkSized $ isAtMost $ fromInteger $ natVal $ Proxy @n
+
+instance (KnownNat n, KnownNat m, n <= m) => Size (Between n m) where
+  sized = mkSized $ isBetween (fromInteger $ natVal $ Proxy @n) (fromInteger $ natVal $ Proxy @m)
+
+mkSized :: (a -> Bool) -> a -> Maybe (Sized s a)
+mkSized p x =
+  if p x
+    then Just $ trustedSized x
+    else Nothing
+
+-- | Build 'MkSizedFromContainerFoldable' for 'Foldable' defined types
+--   to be used with 'DerivingVia':
+--
+--   > deriving instance SizedFromContainer [a] via (MkSizedFromContainerFoldable [a])
+newtype MkSizedFromContainerFoldable a
+  = MkSizedFromContainerFoldable a
+
+instance Foldable f => SizedFromContainer (f a) where
+  calculateSize = length
+  isExactly n = (== [n]) . drop n . index
+  isAtLeast n = not . null . drop n . index
+  isAtMost n = null . drop n . toList
+  isBetween n m x =
+    let indexed = index x
+     in elem n indexed && notElem (succ m) indexed
+
+index :: Foldable f => f a -> [Int]
+index = (0 :) . zipWith const [1 ..] . toList
+
+-- | Give a more precise sizing
+precise :: (SizedFromContainer a, Size s) => Sized s' a -> Maybe (Sized s a)
+precise = sized . getSized
+{-# INLINE precise #-}
+
+type family IsMoreGeneral general restrictive :: Constraint where
+  IsMoreGeneral (AtLeast n) (AtLeast m) = (n <= m)
+  IsMoreGeneral (AtMost n) (AtMost m) = (m <= n)
+  IsMoreGeneral (AtLeast n) (Exactly m) = (n <= m)
+  IsMoreGeneral (AtMost n) (Exactly m) = (m <= n)
+  IsMoreGeneral (AtLeast n) (Between m o) = (n <= m)
+  IsMoreGeneral (AtMost n) (Between m o) = (o <= n)
+  IsMoreGeneral (Between n m) (Between o p) = (n <= o, p <= m)
+  IsMoreGeneral (Between n m) (Exactly o) = (n <= o, o <= m)
+  IsMoreGeneral Unknown a = ()
+
+type IsNotEmpty s = IsMoreGeneral s (Exactly 1)
+
+-- | Give a more general sizing
+approximate :: IsMoreGeneral s s' => Sized s' a -> Sized s a
+approximate = trustedSized . getSized
+{-# INLINE approximate #-}
+
+-- | Concatenative operation of 'Sized'
+withSizedAppend :: (a -> b -> c) -> Sized s a -> Sized s' b -> Sized (s <+> s') c
+withSizedAppend f x y = trustedSized $ f (getSized x) (getSized y)
+
+type family (<+>) a b where
+  (<+>) Unknown b = Unknown
+  (<+>) a Unknown = Unknown
+  (<+>) (AtMost n) (AtMost m) = AtMost (n + m)
+  (<+>) (AtMost n) (Exactly m) = AtMost (n + m)
+  (<+>) (Exactly n) (Exactly m) = Exactly (n + m)
+  (<+>) (Exactly n) (AtMost m) = AtMost (n + m)
+  (<+>) (Exactly n) (AtLeast m) = AtLeast (n + m)
+  (<+>) (AtLeast n) (Exactly m) = AtLeast (n + m)
+  (<+>) (AtLeast n) (AtLeast m) = AtLeast (n + m)
+  (<+>) (AtLeast n) (AtMost m) = Between n (n + m)
+  (<+>) (AtMost n) (AtLeast m) = Between m (n + m)
+  (<+>) (AtLeast n) (Between m o) = Between (n + m) (n + o)
+  (<+>) (AtMost n) (Between m o) = Between m (n + o)
+  (<+>) (Between n m) (Exactly o) = Between (n + o) (m + o)
+  (<+>) (Exactly o) (Between n m) = Between (n + o) (m + o)
+  (<+>) (Between m o) (AtLeast n) = Between (n + m) (n + o)
+  (<+>) (Between m o) (AtMost n) = Between m (n + o)
+  (<+>) (Between n m) (Between o p) = Between (n + o) (m + p)
+
+-- | Semigroup append
+(<<>>) :: Semigroup a => Sized s a -> Sized s' a -> Sized (s <+> s') a
+(<<>>) = withSizedAppend (<>)
+{-# INLINE (<<>>) #-}
+
+infixr 6 <<>>
+
+type family RestrictAtMost a where
+  RestrictAtMost Unknown = Unknown
+  RestrictAtMost (AtMost n) = AtMost n
+  RestrictAtMost (AtLeast n) = AtLeast 0
+  RestrictAtMost (Between n m) = AtMost m
+  RestrictAtMost (Exactly n) = AtMost n
+
+withSizedRetract :: (a -> b) -> Sized s a -> Sized (RestrictAtMost s) b
+withSizedRetract f = trustedSized . f . getSized
+
+type family (<->) a b where
+  (<->) Unknown b = Unknown
+  (<->) a Unknown = Unknown
+  (<->) (AtMost n) (AtMost m) = AtMost (n - m)
+  (<->) (Exactly n) (AtMost m) = AtMost (n - m)
+  (<->) (AtMost n) (Exactly m) = AtMost (n - m)
+  (<->) (Exactly n) (Exactly m) = Exactly (n - m)
+  (<->) (Exactly n) (AtLeast m) = AtLeast (n - m)
+  (<->) (AtLeast n) (Exactly m) = AtLeast (n - m)
+  (<->) (AtLeast n) (AtLeast m) = AtLeast (n - m)
+  (<->) (AtLeast n) (AtMost m) = AtLeast (n - m)
+  (<->) (AtMost n) (AtLeast m) = AtLeast 0
+  (<->) (AtLeast n) (Between m o) = AtLeast (m - n)
+  (<->) (AtMost n) (Between m o) = Between (m - n) o
+  (<->) (Between n m) (Exactly o) = Between (n - o) (m - o)
+  (<->) (Exactly o) (Between n m) = Between (o - n) (o - m)
+  (<->) (Between m o) (AtLeast n) = AtLeast 0
+  (<->) (Between m o) (AtMost n) = Between m (o - n)
+  (<->) (Between n m) (Between o p) = Between (n - o) (m - p)
+
+withSizedSubtract :: (a -> b -> c) -> Sized s a -> Sized s' b -> Sized (s <-> s') c
+withSizedSubtract f a b = trustedSized $ f (getSized a) (getSized b)
+
+type family (<*>) a b where
+  (<*>) Unknown b = Unknown
+  (<*>) a Unknown = Unknown
+  (<*>) (AtMost n) (AtMost m) = AtMost (n TypeLits.* m)
+  (<*>) (Exactly n) (AtMost m) = AtMost (n TypeLits.* m)
+  (<*>) (AtMost n) (Exactly m) = AtMost (n TypeLits.* m)
+  (<*>) (Exactly n) (Exactly m) = Exactly (n TypeLits.* m)
+  (<*>) (AtLeast n) (Exactly m) = AtLeast (n TypeLits.* m)
+  (<*>) (Exactly n) (AtLeast m) = AtLeast (n TypeLits.* m)
+  (<*>) (AtLeast n) (AtLeast m) = AtLeast (n TypeLits.* m)
+  (<*>) (AtLeast n) (AtMost m) = AtLeast 0
+  (<*>) (AtMost n) (AtLeast m) = AtLeast 0
+  (<*>) (AtLeast n) (Between m o) = AtLeast (m TypeLits.* n)
+  (<*>) (AtMost n) (Between m o) = AtLeast 0
+  (<*>) (Between n m) (Exactly o) = Between (n TypeLits.* o) (m TypeLits.* o)
+  (<*>) (Exactly o) (Between n m) = Between (n TypeLits.* o) (m TypeLits.* o)
+  (<*>) (Between m o) (AtLeast n) = AtLeast (m TypeLits.* n)
+  (<*>) (Between m o) (AtMost n) = AtLeast 0
+  (<*>) (Between n m) (Between o p) = Between (n TypeLits.* o) (m TypeLits.* p)
+
+withSizedProduct :: (a -> b -> c) -> Sized s a -> Sized s' b -> Sized (s <*> s') c
+withSizedProduct f a b = trustedSized $ f (getSized a) (getSized b)
+
+type family Includes (sized :: Type) (size :: Nat) :: Constraint where
+  Includes (AtLeast n) m = (n <= m)
+  Includes (AtMost n) m = (m <= n)
+  Includes (Between n m) o = (n <= o, o <= m)
+  Includes Unknown a = ()
diff --git a/test/Data/SizedSpec.hs b/test/Data/SizedSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Data/SizedSpec.hs
@@ -0,0 +1,99 @@
+{-# LANGUAGE NoOverloadedLists #-}
+{-# LANGUAGE NoOverloadedStrings #-}
+
+module Data.SizedSpec
+  ( main,
+    spec,
+  )
+where
+
+import Data.Proxy
+import Data.Sized
+import Test.Hspec
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec = do
+  it "Singleton creation should be equivalent to a single element list" $
+    singleton (Proxy @[Int]) 42 `shouldBe` trustedSized [42]
+  describe "sized creation" $ do
+    describe "Exactly" $ do
+      it "(Exactly 3) on three items list should be equivalent to the wrapped list" $
+        sized @(Exactly 3) @[Int] [1, 2, 3] `shouldBe` Just (trustedSized [1, 2, 3])
+      it "(Exactly 0) on empty list should be equivalent to the wrapped list" $
+        sized @(Exactly 0) @[Int] [] `shouldBe` Just (trustedSized [])
+      it "(Exactly 3) on empty list should be Nothing" $
+        sized @(Exactly 3) @[Int] [] `shouldBe` Nothing
+    describe "AtLeast" $ do
+      it "(AtLeast 3) on three items list should be equivalent to the wrapped list" $
+        sized @(AtLeast 3) @[Int] [1, 2, 3] `shouldBe` Just (trustedSized [1, 2, 3])
+      it "(AtLeast 0) on empty list should be equivalent to the wrapped list" $
+        sized @(AtLeast 0) @[Int] [] `shouldBe` Just (trustedSized [])
+      it "(AtLeast 3) on empty list should be Nothing" $
+        sized @(AtLeast 3) @[Int] [] `shouldBe` Nothing
+    describe "AtMost" $ do
+      it "(AtMost 3) on three items list should be equivalent to the wrapped list" $
+        sized @(AtMost 3) @[Int] [1, 2, 3] `shouldBe` Just (trustedSized [1, 2, 3])
+      it "(AtMost 0) on empty list should be equivalent to the wrapped list" $
+        sized @(AtMost 0) @[Int] [] `shouldBe` Just (trustedSized [])
+      it "(AtMost 3) on a 4-items list should be Nothing" $
+        sized @(AtMost 3) @[Int] [1, 2, 3, 4] `shouldBe` Nothing
+    describe "Between" $ do
+      it "(Between 3 3) on three items list should be equivalent to the wrapped list" $
+        sized @(Between 3 3) @[Int] [1, 2, 3] `shouldBe` Just (trustedSized [1, 2, 3])
+      it "(Between 0 0) on empty list should be equivalent to the wrapped list" $
+        sized @(Between 0 0) @[Int] [] `shouldBe` Just (trustedSized [])
+      it "(Between 3 3) on a 4-items list should be Nothing" $
+        sized @(Between 3 3) @[Int] [1, 2, 3, 4] `shouldBe` Nothing
+      it "(Between 2 4) on three items list should be equivalent to the wrapped list" $
+        sized @(Between 2 4) @[Int] [1, 2, 3] `shouldBe` Just (trustedSized [1, 2, 3])
+      it "(Between 0 4) on empty list should be equivalent to the wrapped list" $
+        sized @(Between 0 4) @[Int] [] `shouldBe` Just (trustedSized [])
+      it "(Between 1 3) on a 4-items list should be Nothing" $
+        sized @(Between 1 3) @[Int] [1, 2, 3, 4] `shouldBe` Nothing
+      it "(Between 5 7) on a 4-items list should be Nothing" $
+        sized @(Between 5 7) @[Int] [1, 2, 3, 4] `shouldBe` Nothing
+  describe "approximate" $ do
+    describe "Exactly" $ do
+      it "(Exactly 3) approximates to (Between 3 3)" $
+        approximate (trustedSized @[Int] @(Exactly 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(Between 3 3) [1, 2, 3]
+      it "(Exactly 3) approximates to (Between 0 3)" $
+        approximate (trustedSized @[Int] @(Exactly 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(Between 0 3) [1, 2, 3]
+      it "(Exactly 3) approximates to (Between 3 5)" $
+        approximate (trustedSized @[Int] @(Exactly 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(Between 3 5) [1, 2, 3]
+      it "(Exactly 3) approximates to (AtLeast 3)" $
+        approximate (trustedSized @[Int] @(Exactly 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(AtLeast 3) [1, 2, 3]
+      it "(Exactly 3) approximates to (AtMost 3)" $
+        approximate (trustedSized @[Int] @(Exactly 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(AtMost 3) [1, 2, 3]
+      it "(Exactly 3) approximates to (AtLeast 1)" $
+        approximate (trustedSized @[Int] @(Exactly 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(AtLeast 1) [1, 2, 3]
+      it "(Exactly 3) approximates to (AtMost 5)" $
+        approximate (trustedSized @[Int] @(Exactly 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(AtMost 5) [1, 2, 3]
+    describe "AtLeast" $ do
+      it "(AtLeast 3) approximates to (AtLeast 2)" $
+        approximate (trustedSized @[Int] @(AtLeast 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(AtLeast 2) [1, 2, 3]
+    describe "AtMost" $ do
+      it "(AtMost 3) approximates to (AtMost 4)" $
+        approximate (trustedSized @[Int] @(AtMost 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(AtMost 4) [1, 2, 3]
+    describe "Between" $ do
+      it "(Between 3 3) approximates to (Between 3 3)" $
+        approximate (trustedSized @[Int] @(Between 3 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(Between 3 3) [1, 2, 3]
+      it "(Between 3 3) approximates to (Between 0 3)" $
+        approximate (trustedSized @[Int] @(Between 3 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(Between 0 3) [1, 2, 3]
+      it "(Between 3 3) approximates to (Between 3 5)" $
+        approximate (trustedSized @[Int] @(Between 3 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(Between 3 5) [1, 2, 3]
+      it "(Between 3 3) approximates to (AtLeast 3)" $
+        approximate (trustedSized @[Int] @(Between 3 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(AtLeast 3) [1, 2, 3]
+      it "(Between 3 3) approximates to (AtMost 3)" $
+        approximate (trustedSized @[Int] @(Between 3 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(AtMost 3) [1, 2, 3]
+      it "(Between 3 3) approximates to (AtLeast 1)" $
+        approximate (trustedSized @[Int] @(Between 3 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(AtLeast 1) [1, 2, 3]
+      it "(Between 3 3) approximates to (AtMost 5)" $
+        approximate (trustedSized @[Int] @(Between 3 3) [1, 2, 3]) `shouldBe` trustedSized @[Int] @(AtMost 5) [1, 2, 3]
+
+-- it "prepend should use semigroup" $
+--   singleton (Proxy @[Int]) 1 <| [2] `shouldBe` trustedSized [1, 2]
+-- it "append should use semigroup" $
+--   [1] |> singleton (Proxy @[Int]) 2 `shouldBe` trustedSized [1, 2]
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
