diff --git a/src/Data/Validator.hs b/src/Data/Validator.hs
--- a/src/Data/Validator.hs
+++ b/src/Data/Validator.hs
@@ -1,22 +1,21 @@
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE QuasiQuotes #-}
 module Data.Validator
-    ( -- * core monad and runners
-      ValidationM, ValidationT(..)
+    ( -- * Core monad and runners
+      ValidationM, ValidationT, ValidationRule, ValidationRuleT
     , runValidator, runValidatorT
-      -- * combinators
-    , (+>>)
-      -- * checks
+      -- * Combinators
+    , (>=>), (<=<)
+      -- * Checks
     , minLength, maxLength, lengthBetween, notEmpty
     , largerThan, smallerThan, valueBetween
     , matchesRegex
     , conformsPred, conformsPredM
-      -- * helper classes and types
+      -- * Helper classes and types
     , HasLength(..), Stringable(..)
     , Int64
-      -- * reexports
+      -- * Regular expression helpers
     , re, mkRegexQQ, Regex
     )
 where
@@ -43,90 +42,100 @@
       deriving (Monad, Functor, Applicative, Alternative, MonadPlus, MonadTrans)
 
 -- | Run a validation on a type 'a'
-runValidator :: (a -> ValidationM e a) -> a -> Either e a
+runValidator :: ValidationRule e a -> a -> Either e a
 runValidator a b = runIdentity $ runValidatorT a b
+{-# INLINE runValidator #-}
 
 -- | Run a validation on a type 'a'
-runValidatorT :: Monad m => (a -> ValidationT e m a) -> a -> m (Either e a)
+runValidatorT :: Monad m => ValidationRuleT e m a -> a -> m (Either e a)
 runValidatorT validationSteps input =
     runEitherT $ unValidationT (validationSteps input)
+{-# INLINE runValidatorT #-}
 
+-- | A validation rule. Combine using @('>=>')@ or @('<=<')@
+type ValidationRule e a = ValidationRuleT e Identity a
+
+-- | A validation rule. Combine using @('>=>')@ or @('<=<')@
+type ValidationRuleT e m a = a -> ValidationT e m a
+
 -- | All types that have a length, eg. 'String', '[a]', 'Vector a', etc.
 class HasLength a where
     getLength :: a -> Int64
 
 instance HasLength [a] where
     getLength = fromIntegral . length
+    {-# INLINE getLength #-}
 
 instance HasLength T.Text where
     getLength = fromIntegral . T.length
+    {-# INLINE getLength #-}
 
 instance HasLength TL.Text where
     getLength = TL.length
+    {-# INLINE getLength #-}
 
 instance HasLength BS.ByteString where
     getLength = fromIntegral . BS.length
+    {-# INLINE getLength #-}
 
 instance HasLength BSL.ByteString where
     getLength = BSL.length
+    {-# INLINE getLength #-}
 
 -- | Mark a custom check as failed
 checkFailed :: Monad m => e -> ValidationT e m a
 checkFailed = ValidationT . left
 {-# INLINE checkFailed #-}
 
--- | Combine two checks
-(+>>) :: Monad m => (a -> ValidationT e m a) -> (a -> ValidationT e m a) -> a -> ValidationT e m a
-(+>>) m1 m2 a =
-    m1 a >>= m2
-{-# INLINE (+>>) #-}
-
 -- | Check that the value is at least N elements long
-minLength :: (Monad m, HasLength a) => Int64 -> e -> a -> ValidationT e m a
-minLength lowerBound e obj =
-    largerThan lowerBound e (getLength obj) >> return obj
+minLength :: (Monad m, HasLength a) => Int64 -> e -> ValidationRuleT e m a
+minLength lowerBound e obj = largerThan lowerBound e (getLength obj) >> return obj
+{-# INLINE minLength #-}
 
 -- | Check that the value is at maxium N elements long
-maxLength :: (Monad m, HasLength a) => Int64 -> e -> a -> ValidationT e m a
+maxLength :: (Monad m, HasLength a) => Int64 -> e -> ValidationRuleT e m a
 maxLength upperBound e obj =
     smallerThan upperBound e (getLength obj) >> return obj
+{-# INLINE maxLength #-}
 
 -- | Check that the value's length is between N and M
-lengthBetween :: (Monad m, HasLength a) => Int64 -> Int64 -> e -> a -> ValidationT e m a
-lengthBetween lowerBound upperBound e obj =
-    valueBetween lowerBound upperBound e (getLength obj) >> return obj
+lengthBetween :: (Monad m, HasLength a) => Int64 -> Int64 -> e -> ValidationRuleT e m a
+lengthBetween lowerBound upperBound e obj = valueBetween lowerBound upperBound e (getLength obj) >> return obj
+{-# INLINE lengthBetween #-}
 
 -- | Specialized minLength with N = 1
-notEmpty :: (Monad m, HasLength a) => e -> a -> ValidationT e m a
+notEmpty :: (Monad m, HasLength a) => e -> ValidationRuleT e m a
 notEmpty = minLength 1
 {-# INLINE notEmpty #-}
 
 -- | Check that a value is larger than N
-largerThan :: (Monad m, Ord a) => a -> e -> a -> ValidationT e m a
+largerThan :: (Monad m, Ord a) => a -> e -> ValidationRuleT e m a
 largerThan lowerBound = conformsPred (>= lowerBound)
+{-# INLINE largerThan #-}
 
 -- | Check that a value is smaller than N
-smallerThan :: (Monad m, Ord a) => a -> e -> a -> ValidationT e m a
+smallerThan :: (Monad m, Ord a) => a -> e -> ValidationRuleT e m a
 smallerThan upperBound = conformsPred (<= upperBound)
+{-# INLINE smallerThan #-}
 
 -- | Check that a value is between M and N
-valueBetween :: (Monad m, Ord a) => a -> a -> e -> a -> ValidationT e m a
-valueBetween lowerBound upperBound e obj =
-    (largerThan lowerBound e +>> smallerThan upperBound e) obj
+valueBetween :: (Monad m, Ord a) => a -> a -> e -> ValidationRuleT e m a
+valueBetween lowerBound upperBound e = largerThan lowerBound e >=> smallerThan upperBound e
+{-# INLINE valueBetween #-}
 
 -- | Check that a value conforms a predicate
-conformsPred :: Monad m => (a -> Bool) -> e -> a -> ValidationT e m a
-conformsPred predicate e obj =
-    do unless (predicate obj) $ checkFailed e
-       return obj
+conformsPred :: Monad m => (a -> Bool) -> e -> ValidationRuleT e m a
+conformsPred predicate e obj = unless (predicate obj) (checkFailed e) >> return obj
+{-# INLINE conformsPred #-}
 
 -- | Check that a value conforms a predicate
-conformsPredM :: Monad m => (a -> m Bool) -> e -> a -> ValidationT e m a
+conformsPredM :: Monad m => (a -> m Bool) -> e -> ValidationRuleT e m a
 conformsPredM predicate e obj =
     do res <- lift $ predicate obj
-       unless res $ checkFailed e
-       return obj
+       unless res (checkFailed e) >> return obj
+{-# INLINE conformsPredM #-}
 
 -- | Checks that a value matches a regular expression
-matchesRegex :: (Stringable a, Monad m) => Regex -> e -> a -> ValidationT e m a
-matchesRegex r = conformsPred (\obj -> obj =~ r)
+matchesRegex :: (Stringable a, Monad m) => Regex -> e -> ValidationRuleT e m a
+matchesRegex r = conformsPred (=~ r)
+{-# INLINE matchesRegex #-}
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Main where
+
+import Data.Validator
+
+import Test.Hspec
+import Test.QuickCheck
+
+errMsg :: String
+errMsg = "oops"
+
+main :: IO ()
+main =
+    hspec $
+    do describe "conformsPred" $
+        do it "should result in error when value does not conform pred" $
+            property $ \(i :: Int) ->
+                i <= 5 ==> runValidator (conformsPred (>5) errMsg) i == Left errMsg
+           it "should result in error when value does conform pred" $
+            property $ \(i :: Int) ->
+                i > 5 ==> runValidator (conformsPred (>5) errMsg) i == Right i
+       describe "minLength" $
+        do it "should result in error when string is to small" $
+            property $ \(str :: String) (i :: Int64) ->
+                fromIntegral (length str) < i ==>
+                runValidator (minLength i errMsg) str == Left errMsg
+           it "should allow strings with correct size to pass" $
+            property $ \(str :: String) (i :: Int64) ->
+                            fromIntegral (length str) >= i ==>
+                            runValidator (minLength i errMsg) str == Right str
+       describe "maxLength" $
+        do it "should result in error when string is to large" $
+            property $ \(str :: String) (i :: Int64) ->
+                fromIntegral (length str) > i ==>
+                runValidator (maxLength i errMsg) str == Left errMsg
+           it "should allow strings with correct size to pass" $
+            property $ \(str :: String) (i :: Int64) ->
+                fromIntegral (length str) <= i ==>
+                runValidator (maxLength i errMsg) str == Right str
diff --git a/validate-input.cabal b/validate-input.cabal
--- a/validate-input.cabal
+++ b/validate-input.cabal
@@ -1,7 +1,8 @@
 name:                validate-input
-version:             0.1.0.0
+version:             0.2.0.0
 synopsis:            Input validation combinator library
-description:         A small Haskell combinator library that provides a simple way of validating user provided data structures.
+description:         A small Haskell combinator library that provides a simple way of
+                     validating user provided data structures.
 homepage:            https://github.com/agrafix/validate-input
 bug-reports:         https://github.com/agrafix/validate-input/issues
 license:             MIT
@@ -22,13 +23,25 @@
 library
   exposed-modules:     Data.Validator
   build-depends:
-                      base >=4.6 && <5,
-                      bytestring >=0.10,
-                      either >=4.3,
-                      mtl >=2.1,
-                      pcre-heavy >=0.2,
-                      stringable >=0.1,
-                      text >=1.2
+                       base >=4.6 && <5,
+                       bytestring >=0.10,
+                       either >=4.3,
+                       mtl >=2.1,
+                       pcre-heavy >=0.2,
+                       stringable >=0.1,
+                       text >=1.2
   hs-source-dirs:      src
   default-language:    Haskell2010
   ghc-options:         -auto-all -Wall -fno-warn-orphans
+
+test-suite validate-input-tests
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  build-depends:
+                       base >=4.6 && <5,
+                       hspec >=2.1,
+                       QuickCheck >=2.7,
+                       validate-input
+  ghc-options:         -auto-all -Wall -fno-warn-orphans
+  default-language:    Haskell2010
