diff --git a/.travis.yml b/.travis.yml
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,13 +1,14 @@
 env:
- - GHCVER=7.4.2 CABALVER=1.16
- - GHCVER=7.6.3 CABALVER=1.16
+ - GHCVER=7.4.2 CABALVER=1.18
+ - GHCVER=7.6.3 CABALVER=1.18
  - GHCVER=7.8.4 CABALVER=1.18
  - GHCVER=7.10.1 CABALVER=1.22
+ - GHCVER=8.0.1 CABALVER=1.24
  - GHCVER=head CABALVER=1.22
 
 matrix:
   allow_failures:
-   - env: GHCVER=head CABALVER=1.22
+   - env: GHCVER=head CABALVER=1.24
 
 before_install:
  - travis_retry sudo add-apt-repository -y ppa:hvr/ghc
diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
--- a/CHANGELOG.markdown
+++ b/CHANGELOG.markdown
@@ -1,3 +1,8 @@
+1.20.5
+------
+* GHC 8 compatibility
+* Fixed the `perspective` calculation.
+
 1.20.4
 ------
 * Compatibility with `base-orphans` 0.5
diff --git a/linear.cabal b/linear.cabal
--- a/linear.cabal
+++ b/linear.cabal
@@ -1,6 +1,6 @@
 name:          linear
 category:      Math, Algebra
-version:       1.20.4
+version:       1.20.5
 license:       BSD3
 cabal-version: >= 1.8
 license-file:  LICENSE
@@ -13,7 +13,7 @@
 synopsis:      Linear Algebra
 description:   Types and combinators for linear algebra on free vector spaces
 build-type:    Custom
-tested-with:   GHC == 7.4.2, GHC == 7.6.1, GHC == 7.8.4, GHC == 7.10.1
+tested-with:   GHC == 7.4.2, GHC == 7.6.1, GHC == 7.8.4, GHC == 7.10.1, GHC==8.0.1
 extra-source-files:
   .gitignore
   .travis.yml
@@ -45,7 +45,7 @@
     adjunctions          >= 4     && < 5,
     base                 >= 4.5   && < 5,
     base-orphans         >= 0.5   && < 1,
-    binary               >= 0.5   && < 0.8,
+    binary               >= 0.5   && < 0.9,
     bytes                >= 0.15  && < 1,
     cereal               >= 0.4.1.1 && < 0.6,
     containers           >= 0.4   && < 0.6,
@@ -58,7 +58,7 @@
     semigroups           >= 0.9   && < 1,
     semigroupoids        >= 3     && < 6,
     tagged               >= 0.4.4 && < 1,
-    transformers         >= 0.2   && < 0.5,
+    transformers         >= 0.2   && < 0.6,
     transformers-compat  >= 0.4   && < 1,
     unordered-containers >= 0.2.3 && < 0.3,
     vector               >= 0.10  && < 0.12,
@@ -99,6 +99,10 @@
   ghc-options: -Wall -fwarn-tabs -O2 -fdicts-cheap -funbox-strict-fields
   hs-source-dirs: src
 
+  -- hack around the buggy unused matches check for class associated types in ghc 8 rc1
+  if impl(ghc >= 8)
+    ghc-options: -fno-warn-unused-matches
+
 -- Verify the results of the examples
 test-suite doctests
   type:           exitcode-stdio-1.0
@@ -108,7 +112,7 @@
   build-depends:
     base,
     directory >= 1.0 && < 1.3,
-    doctest   >= 0.8 && < 0.11,
+    doctest   >= 0.8 && < 0.12,
     filepath  >= 1.3 && < 1.5,
     lens,
     simple-reflect >= 0.3.1
@@ -127,3 +131,5 @@
     test-framework-hunit >= 0.3,
     HUnit >= 1.2.5,
     linear
+
+
diff --git a/src/Linear/Algebra.hs b/src/Linear/Algebra.hs
--- a/src/Linear/Algebra.hs
+++ b/src/Linear/Algebra.hs
@@ -1,6 +1,10 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
+#if __GLASGOW_HASKELL__ >= 800
+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-} -- they are mathematically required, not redundant, damn it.
+#endif
 -----------------------------------------------------------------------------
 -- |
 -- License     :  BSD-style (see the file LICENSE)
diff --git a/src/Linear/Conjugate.hs b/src/Linear/Conjugate.hs
--- a/src/Linear/Conjugate.hs
+++ b/src/Linear/Conjugate.hs
@@ -1,5 +1,9 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE DefaultSignatures #-}
+#if __GLASGOW_HASKELL__ >= 800
+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
+#endif
+
 -----------------------------------------------------------------------------
 -- |
 -- Copyright   :  (C) 2012-2015 Edward Kmett
diff --git a/src/Linear/Matrix.hs b/src/Linear/Matrix.hs
--- a/src/Linear/Matrix.hs
+++ b/src/Linear/Matrix.hs
@@ -343,7 +343,7 @@
 --
 -- >>> inv22 $ V2 (V2 1 2) (V2 3 4)
 -- V2 (V2 (-2.0) 1.0) (V2 1.5 (-0.5))
-inv22 :: Floating a => M22 a -> M22 a
+inv22 :: Fractional a => M22 a -> M22 a
 inv22 m@(V2 (V2 a b) (V2 c d)) = (1 / det) *!! V2 (V2 d (-b)) (V2 (-c) a)
   where det = det22 m
 {-# INLINE inv22 #-}
@@ -352,7 +352,7 @@
 --
 -- >>> inv33 $ V3 (V3 1 2 4) (V3 4 2 2) (V3 1 1 1)
 -- V3 (V3 0.0 0.5 (-1.0)) (V3 (-0.5) (-0.75) 3.5) (V3 0.5 0.25 (-1.5))
-inv33 :: Floating a => M33 a -> M33 a
+inv33 :: Fractional a => M33 a -> M33 a
 inv33 m@(V3 (V3 a b c)
             (V3 d e f)
             (V3 g h i))
diff --git a/src/Linear/Plucker.hs b/src/Linear/Plucker.hs
--- a/src/Linear/Plucker.hs
+++ b/src/Linear/Plucker.hs
@@ -12,6 +12,10 @@
 #ifndef MIN_VERSION_vector
 #define MIN_VERSION_vector(x,y,z) 1
 #endif
+
+#ifndef MIN_VERSION_transformers
+#define MIN_VERSION_transformers(x,y,z) 1
+#endif
 -----------------------------------------------------------------------------
 -- |
 -- Copyright   :  (C) 2012-2015 Edward Kmett
@@ -412,7 +416,7 @@
 -- | Valid Plücker coordinates @p@ will have @'squaredError' p '==' 0@
 --
 -- That said, floating point makes a mockery of this claim, so you may want to use 'nearZero'.
-squaredError :: (Eq a, Num a) => Plucker a -> a
+squaredError :: Num a => Plucker a -> a
 squaredError v = v >< v
 {-# INLINE squaredError #-}
 
@@ -452,7 +456,7 @@
 
 -- | Check how two lines pass each other. @passes l1 l2@ describes
 -- @l2@ when looking down @l1@.
-passes :: (Epsilon a, Num a, Ord a) => Plucker a -> Plucker a -> LinePass
+passes :: (Epsilon a, Ord a) => Plucker a -> Plucker a -> LinePass
 passes a b
   | nearZero s = Coplanar
   | s > 0 = Counterclockwise
@@ -590,7 +594,33 @@
   put = serializeWith Cereal.put
   get = deserializeWith Cereal.get
 
+
+#if (MIN_VERSION_transformers(0,5,0)) || !(MIN_VERSION_transformers(0,4,0))
+instance Eq1 Plucker where
+  liftEq k (Plucker a1 b1 c1 d1 e1 f1)
+           (Plucker a2 b2 c2 d2 e2 f2)
+         = k a1 a2 && k b1 b2 && k c1 c2 && k d1 d2 && k e1 e2 && k f1 f2
+instance Ord1 Plucker where
+  liftCompare k (Plucker a1 b1 c1 d1 e1 f1)
+                (Plucker a2 b2 c2 d2 e2 f2)
+            = k a1 a2 `mappend` k b1 b2 `mappend` k c1 c2 `mappend` k d1 d2 `mappend` k e1 e2 `mappend` k f1 f2
+instance Read1 Plucker where
+  liftReadsPrec k _ z = readParen (z > 10) $ \r ->
+     [ (Plucker a b c d e f, r7)
+     | ("Plucker",r1) <- lex r
+     , (a,r2) <- k 11 r1
+     , (b,r3) <- k 11 r2
+     , (c,r4) <- k 11 r3
+     , (d,r5) <- k 11 r4
+     , (e,r6) <- k 11 r5
+     , (f,r7) <- k 11 r6
+     ]
+instance Show1 Plucker where
+  liftShowsPrec k _ z (Plucker a b c d e f) = showParen (z > 10) $
+     showString "Plucker " . k 11 a . showChar ' ' . k 11 b . showChar ' ' . k 11 c . showChar ' ' . k 11 d . showChar ' ' . k 11 e . showChar ' ' . k 11 f
+#else
 instance Eq1 Plucker where eq1 = (==)
 instance Ord1 Plucker where compare1 = compare
 instance Show1 Plucker where showsPrec1 = showsPrec
 instance Read1 Plucker where readsPrec1 = readsPrec
+#endif
diff --git a/src/Linear/Projection.hs b/src/Linear/Projection.hs
--- a/src/Linear/Projection.hs
+++ b/src/Linear/Projection.hs
@@ -71,8 +71,8 @@
         y = 1 / tanHalfFovy
         fpn = far + near
         fmn = far - near
-        oon = 1/near
-        oof = 1/far
+        oon = 0.5/near
+        oof = 0.5/far
         -- z = 1 / (near/fpn - far/fpn) -- would be better by .5 bits
         z = -fpn/fmn
         w = 1/(oof-oon) -- 13 bits error reduced to 0.17
@@ -100,8 +100,8 @@
         b = tanHalfFovy
         c = oon - oof
         d = oon + oof
-        oon = 1/near
-        oof = 1/far
+        oon = 0.5/near
+        oof = 0.5/far
 
 
 -- | Build a perspective matrix per the classic @glFrustum@ arguments.
diff --git a/src/Linear/Quaternion.hs b/src/Linear/Quaternion.hs
--- a/src/Linear/Quaternion.hs
+++ b/src/Linear/Quaternion.hs
@@ -624,7 +624,18 @@
   put = serializeWith Cereal.put
   get = deserializeWith Cereal.get
 
+#if (MIN_VERSION_transformers(0,5,0)) || !(MIN_VERSION_transformers(0,4,0))
+instance Eq1 Quaternion where
+  liftEq f (Quaternion a b) (Quaternion c d) = f a c && liftEq f b d
+instance Ord1 Quaternion where
+  liftCompare f (Quaternion a b) (Quaternion c d) = f a c `mappend` liftCompare f b d
+instance Show1 Quaternion where
+  liftShowsPrec f g d (Quaternion a b) = showsBinaryWith f (liftShowsPrec f g) "Quaternion" d a b
+instance Read1 Quaternion where
+  liftReadsPrec f g = readsData $ readsBinaryWith f (liftReadsPrec f g) "Quaternion" Quaternion
+#else
 instance Eq1 Quaternion where eq1 = (==)
 instance Ord1 Quaternion where compare1 = compare
 instance Show1 Quaternion where showsPrec1 = showsPrec
 instance Read1 Quaternion where readsPrec1 = readsPrec
+#endif
diff --git a/src/Linear/V.hs b/src/Linear/V.hs
--- a/src/Linear/V.hs
+++ b/src/Linear/V.hs
@@ -20,6 +20,10 @@
 #define MIN_VERSION_reflection(x,y,z) 1
 #endif
 
+#ifndef MIN_VERSION_transformers
+#define MIN_VERSION_transformers(x,y,z) 1
+#endif
+
 -----------------------------------------------------------------------------
 -- |
 -- Copyright   :  (C) 2012-2015 Edward Kmett
@@ -93,6 +97,12 @@
 import Linear.Epsilon
 import Linear.Metric
 import Linear.Vector
+#if (MIN_VERSION_transformers(0,5,0)) || !(MIN_VERSION_transformers(0,4,0))
+import Prelude as P
+#if __GLASGOW_HASKELL__ < 710
+import Data.Monoid
+#endif
+#endif
 
 
 #ifdef HLINT
@@ -462,10 +472,36 @@
   put = serializeWith Cereal.put
   get = deserializeWith Cereal.get
 
+#if (MIN_VERSION_transformers(0,5,0)) || !(MIN_VERSION_transformers(0,4,0))
+instance Eq1 (V n) where
+  liftEq f0 (V as0) (V bs0) = go f0 (V.toList as0) (V.toList bs0) where
+    go _ [] [] = True
+    go f (a:as) (b:bs) = f a b && go f as bs
+    go _ _ _ = False
+
+instance Ord1 (V n) where
+  liftCompare f0 (V as0) (V bs0) = go f0 (V.toList as0) (V.toList bs0) where
+    go f (a:as) (b:bs) = f a b `mappend` go f as bs
+    go _ [] [] = EQ
+    go _ _  [] = GT
+    go _ [] _  = LT
+
+instance Show1 (V n) where
+  liftShowsPrec _ g d (V as) = showParen (d > 10) $ showString "V " . g (V.toList as)
+
+instance Dim n => Read1 (V n) where
+  liftReadsPrec _ g d = readParen (d > 10) $ \r ->
+    [ (V (V.fromList as), r2)
+    | ("V",r1) <- lex r
+    , (as, r2) <- g r1
+    , P.length as == reflectDim (Proxy :: Proxy n)
+    ]
+#else
 instance Dim n => Eq1 (V n) where eq1 = (==)
 instance Dim n => Ord1 (V n) where compare1 = compare
 instance Dim n => Show1 (V n) where showsPrec1 = showsPrec
 instance Dim n => Read1 (V n) where readsPrec1 = readsPrec
+#endif
 
 
 data instance U.Vector    (V n a) =  V_VN {-# UNPACK #-} !Int !(U.Vector    a)
diff --git a/src/Linear/V0.hs b/src/Linear/V0.hs
--- a/src/Linear/V0.hs
+++ b/src/Linear/V0.hs
@@ -10,13 +10,18 @@
 #endif
 
 #ifndef MIN_VERSION_hashable
-#define MIN_VERSION_hashable
+#define MIN_VERSION_hashable(x,y,z) 1
 #endif
 
 #ifndef MIN_VERSION_vector
 #define MIN_VERSION_vector(x,y,z) 1
 #endif
 
+#ifndef MIN_VERSION_transformers
+#define MIN_VERSION_transformers(x,y,z) 1
+#endif
+
+
 -----------------------------------------------------------------------------
 -- |
 -- Copyright   :  (C) 2012-2015 Edward Kmett
@@ -220,11 +225,11 @@
   hashWithSalt s V0 = s
   {-# INLINE hashWithSalt #-}
 
-instance Epsilon a => Epsilon (V0 a) where
+instance Epsilon (V0 a) where
   nearZero _ = True
   {-# INLINE nearZero #-}
 
-instance Storable a => Storable (V0 a) where
+instance Storable (V0 a) where
   sizeOf _ = 0
   {-# INLINE sizeOf #-}
   alignment _ = 1
@@ -315,7 +320,18 @@
 instance NFData (V0 a) where
   rnf V0 = ()
 
+#if (MIN_VERSION_transformers(0,5,0)) || !(MIN_VERSION_transformers(0,4,0))
+instance Eq1 V0   where
+  liftEq _ _ _ = True
+instance Ord1 V0  where
+  liftCompare _ _ _ = EQ
+instance Show1 V0 where
+  liftShowsPrec _ _ = showsPrec
+instance Read1 V0 where
+  liftReadsPrec _ _ = readsPrec
+#else
 instance Eq1 V0   where eq1 = (==)
 instance Ord1 V0  where compare1 = compare
 instance Show1 V0 where showsPrec1 = showsPrec
 instance Read1 V0 where readsPrec1 = readsPrec
+#endif
diff --git a/src/Linear/V1.hs b/src/Linear/V1.hs
--- a/src/Linear/V1.hs
+++ b/src/Linear/V1.hs
@@ -21,6 +21,11 @@
 #ifndef MIN_VERSION_vector
 #define MIN_VERSION_vector(x,y,z) 1
 #endif
+
+#ifndef MIN_VERSION_transformers
+#define MIN_VERSION_transformers(x,y,z) 1
+#endif
+
 -----------------------------------------------------------------------------
 -- |
 -- Copyright   :  (C) 2012-2015 Edward Kmett
@@ -348,7 +353,18 @@
   put = serializeWith Cereal.put
   get = deserializeWith Cereal.get
 
+#if (MIN_VERSION_transformers(0,5,0)) || !(MIN_VERSION_transformers(0,4,0))
+instance Eq1 V1 where
+  liftEq f (V1 a) (V1 b) = f a b
+instance Ord1 V1 where
+  liftCompare f (V1 a) (V1 b) = f a b
+instance Show1 V1 where
+  liftShowsPrec f _ d (V1 a) = showParen (d >= 10) $ showString "V1 " . f d a
+instance Read1 V1 where
+  liftReadsPrec f _ = readsData $ readsUnaryWith f "V1" V1
+#else
 instance Eq1 V1 where eq1 = (==)
 instance Ord1 V1 where compare1 = compare
 instance Show1 V1 where showsPrec1 = showsPrec
 instance Read1 V1 where readsPrec1 = readsPrec
+#endif
diff --git a/src/Linear/V2.hs b/src/Linear/V2.hs
--- a/src/Linear/V2.hs
+++ b/src/Linear/V2.hs
@@ -14,6 +14,12 @@
 #ifndef MIN_VERSION_vector
 #define MIN_VERSION_vector(x,y,z) 1
 #endif
+
+#ifndef MIN_VERSION_transformers
+#define MIN_VERSION_transformers(x,y,z) 1
+#endif
+
+
 -----------------------------------------------------------------------------
 -- |
 -- Copyright   :  (C) 2012-2015 Edward Kmett
@@ -33,6 +39,7 @@
   , ex, ey
   , perp
   , angle
+  , crossZ
   ) where
 
 import Control.Applicative
@@ -385,6 +392,14 @@
 angle :: Floating a => a -> V2 a
 angle a = V2 (cos a) (sin a)
 
+-- | The Z-component of the cross product of two vectors in the XY-plane.
+--
+-- >>> crossZ (V2 1 0) (V2 0 1)
+-- 1
+crossZ :: Num a => V2 a -> V2 a -> a
+crossZ (V2 x1 y1) (V2 x2 y2) = x1*y2 - y1*x2
+{-# INLINE crossZ #-}
+
 instance Bounded a => Bounded (V2 a) where
   minBound = pure minBound
   {-# INLINE minBound #-}
@@ -410,7 +425,18 @@
   put = serializeWith Cereal.put
   get = deserializeWith Cereal.get
 
+#if (MIN_VERSION_transformers(0,5,0)) || !(MIN_VERSION_transformers(0,4,0))
+instance Eq1 V2 where
+  liftEq f (V2 a b) (V2 c d) = f a c && f b d
+instance Ord1 V2 where
+  liftCompare f (V2 a b) (V2 c d) = f a c `mappend` f b d
+instance Read1 V2 where
+  liftReadsPrec f _ = readsData $ readsBinaryWith f f "V2" V2
+instance Show1 V2 where
+  liftShowsPrec f _ d (V2 a b) = showsBinaryWith f f "V2" d a b
+#else
 instance Eq1 V2 where eq1 = (==)
 instance Ord1 V2 where compare1 = compare
 instance Show1 V2 where showsPrec1 = showsPrec
 instance Read1 V2 where readsPrec1 = readsPrec
+#endif
diff --git a/src/Linear/V3.hs b/src/Linear/V3.hs
--- a/src/Linear/V3.hs
+++ b/src/Linear/V3.hs
@@ -14,6 +14,10 @@
 #define MIN_VERSION_vector(x,y,z) 1
 #endif
 
+#ifndef MIN_VERSION_transformers
+#define MIN_VERSION_transformers(x,y,z) 1
+#endif
+
 -----------------------------------------------------------------------------
 -- |
 -- Copyright   :  (C) 2012-2015 Edward Kmett
@@ -434,7 +438,25 @@
   put = serializeWith Cereal.put
   get = deserializeWith Cereal.get
 
+#if (MIN_VERSION_transformers(0,5,0)) || !(MIN_VERSION_transformers(0,4,0))
+instance Eq1 V3 where
+  liftEq k (V3 a b c) (V3 d e f) = k a d && k b e && k c f
+instance Ord1 V3 where
+  liftCompare k (V3 a b c) (V3 d e f) = k a d `mappend` k b e `mappend` k c f
+instance Read1 V3 where
+  liftReadsPrec k _ d = readParen (d > 10) $ \r ->
+     [ (V3 a b c, r4)
+     | ("V3",r1) <- lex r
+     , (a,r2) <- k 11 r1
+     , (b,r3) <- k 11 r2
+     , (c,r4) <- k 11 r3
+     ]
+instance Show1 V3 where
+  liftShowsPrec f _ d (V3 a b c) = showParen (d > 10) $
+     showString "V3 " . f 11 a . showChar ' ' . f 11 b . showChar ' ' . f 11 c
+#else
 instance Eq1 V3 where eq1 = (==)
 instance Ord1 V3 where compare1 = compare
 instance Show1 V3 where showsPrec1 = showsPrec
 instance Read1 V3 where readsPrec1 = readsPrec
+#endif
diff --git a/src/Linear/V4.hs b/src/Linear/V4.hs
--- a/src/Linear/V4.hs
+++ b/src/Linear/V4.hs
@@ -13,6 +13,10 @@
 #ifndef MIN_VERSION_vector
 #define MIN_VERSION_vector(x,y,z) 1
 #endif
+
+#ifndef MIN_VERSION_transformers
+#define MIN_VERSION_transformers(x,y,z) 1
+#endif
 -----------------------------------------------------------------------------
 -- |
 -- Copyright   :  (C) 2012-2015 Edward Kmett
@@ -421,12 +425,14 @@
     where ptr' = castPtr ptr
   {-# INLINE peek #-}
 
--- | Convert a 3-dimensional affine vector into a 4-dimensional homogeneous vector.
+-- | Convert a 3-dimensional affine vector into a 4-dimensional homogeneous vector,
+-- i.e. sets the @w@ coordinate to 0.
 vector :: Num a => V3 a -> V4 a
 vector (V3 a b c) = V4 a b c 0
 {-# INLINE vector #-}
 
--- | Convert a 3-dimensional affine point into a 4-dimensional homogeneous vector.
+-- | Convert a 3-dimensional affine point into a 4-dimensional homogeneous vector,
+-- i.e. sets the @w@ coordinate to 1.
 point :: Num a => V3 a -> V4 a
 point (V3 a b c) = V4 a b c 1
 {-# INLINE point #-}
@@ -568,7 +574,26 @@
   put = serializeWith Cereal.put
   get = deserializeWith Cereal.get
 
+#if (MIN_VERSION_transformers(0,5,0)) || !(MIN_VERSION_transformers(0,4,0))
+instance Eq1 V4 where
+  liftEq k (V4 a b c d) (V4 e f g h) = k a e && k b f && k c g && k d h
+instance Ord1 V4 where
+  liftCompare k (V4 a b c d) (V4 e f g h) = k a e `mappend` k b f `mappend` k c g `mappend` k d h
+instance Read1 V4 where
+  liftReadsPrec k _ z = readParen (z > 10) $ \r ->
+     [ (V4 a b c d, r5)
+     | ("V4",r1) <- lex r
+     , (a,r2) <- k 11 r1
+     , (b,r3) <- k 11 r2
+     , (c,r4) <- k 11 r3
+     , (d,r5) <- k 11 r4
+     ]
+instance Show1 V4 where
+  liftShowsPrec f _ z (V4 a b c d) = showParen (z > 10) $
+     showString "V4 " . f 11 a . showChar ' ' . f 11 b . showChar ' ' . f 11 c . showChar ' ' . f 11 d
+#else
 instance Eq1 V4 where eq1 = (==)
 instance Ord1 V4 where compare1 = compare
 instance Show1 V4 where showsPrec1 = showsPrec
 instance Read1 V4 where readsPrec1 = readsPrec
+#endif
