diff --git a/Text/StringConvert.hs b/Text/StringConvert.hs
--- a/Text/StringConvert.hs
+++ b/Text/StringConvert.hs
@@ -3,18 +3,13 @@
 -- strict), and String, are supported.
 --
 -- To hook custom string types into the conversion mechanism, implement both
--- @FromString@ and @ToString@ for your type, and optionally provide special
--- cases for conversions to and from some other string-like type by
--- implementing @StringConvert@ directly.
-{-#LANGUAGE MultiParamTypeClasses #-}
+-- @FromString@ and @ToString@ for your type.
 {-#LANGUAGE FlexibleInstances #-}
-{-#LANGUAGE OverlappingInstances #-}
 module Text.StringConvert
 ( FromString
 , fromString
 , ToString
 , toString
-, StringConvert
 , s
 )
 where
@@ -64,21 +59,22 @@
 instance ToString LUTF8.ByteString where
     toString = LUTF8.toString
 
--- | Defines conversions between two given stringish types.
-class StringConvert a b where
-    s :: a -> b
-
-instance (ToString a, FromString b) => StringConvert a b where
-    s = fromString . toString
+-- if the conversion is a no-op, rewrite it as id
+{-# RULES "s/same-type" s = id #-}
 
-instance StringConvert Text.Text LText.Text where
-    s = LText.fromStrict
+-- conversions between strict and lazy texts can be done directly
+{-# RULES "s/LText->Text" s = LText.toStrict #-}
+{-# RULES "s/Text->LText" s = LText.fromStrict #-}
 
-instance StringConvert LText.Text Text.Text where
-    s = LText.toStrict
+-- conversions between strict and lazy bytestrings can be done directly
+{-# RULES "s/LBS->BS" s = LBS.toStrict #-}
+{-# RULES "s/BS->LBS" s = LBS.fromStrict #-}
 
-instance StringConvert UTF8.ByteString LUTF8.ByteString where
-    s = LBS.fromStrict
+-- conversions to and from string can be done through the ToString / FromString
+-- classes
+{-# RULES "s/->String" s = toString #-}
+{-# RULES "s/String->" s = fromString #-}
 
-instance StringConvert LUTF8.ByteString UTF8.ByteString where
-    s = LBS.toStrict
+{-# NOINLINE s #-}
+s :: (ToString a, FromString b) => a -> b
+s = fromString . toString
diff --git a/string-convert.cabal b/string-convert.cabal
--- a/string-convert.cabal
+++ b/string-convert.cabal
@@ -1,28 +1,42 @@
--- Initial string-convert.cabal generated by cabal init.  For further 
+-- Initial string-convert.cabal generated by cabal init.  For further
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                string-convert
-version:             2.0.1
-synopsis:            Provide universal string conversions between any two string-like types
--- description:         
+version:             3.0.1
+synopsis:            Universal string conversions
+description:         Provides functions and typeclasses for transparent
+                     conversions between various string types.
 homepage:            https://bitbucket.org/tdammers/string-convert
 license:             BSD3
 license-file:        LICENSE
 author:              Tobias Dammers
 maintainer:          tdammers@gmail.com
--- copyright:           
+copyright:           2016 Tobias Dammers
 category:            Text
 build-type:          Simple
--- extra-source-files:  
+-- extra-source-files:
 cabal-version:       >=1.10
 
 library
   exposed-modules:     Text.StringConvert
-  -- other-modules:       
-  -- other-extensions:    
+  -- other-modules:
+  -- other-extensions:
   build-depends: base >=4.5 && <5.0
                , bytestring
                , text
                , utf8-string
-  -- hs-source-dirs:      
+  -- hs-source-dirs:
   default-language:    Haskell2010
+test-suite tests
+  build-depends: base >= 4.5 && <5.0
+               , string-convert
+               , tasty
+               , tasty-hunit
+               , bytestring
+               , text
+               , utf8-string
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  hs-source-dirs: test
+  default-language: Haskell2010
+
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,23 @@
+{-#LANGUAGE OverloadedStrings #-}
+module Main where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+import Text.StringConvert
+import qualified Data.Text as Text
+import qualified Data.Text.Lazy as LText
+
+main = defaultMain tests
+
+tests = testGroup "Tests"
+    [ testCase "string->string" $ do
+        let input = "Hello, world!" :: String
+            expected = "Hello, world!" :: String
+            actual = s input :: String
+        assertEqual "" expected actual
+    , testCase "text->ltext" $ do
+        let input = "Hello, world!" :: Text.Text
+            expected = "Hello, world!" :: LText.Text
+            actual = s input :: LText.Text
+        assertEqual "" expected actual
+    ]
