diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,20 @@
 *~
 *#
 .stack-work/
+cabal-dev
+*.chi
+*.chs.h
+*.dyn_o
+*.dyn_hi
+.hpc
+.hsenv
+.cabal-sandbox/
+cabal.sandbox.config
+*.prof
+*.aux
+*.hp
+*.eventlog
+cabal.project.local
+cabal.project.local~
+.HTF/
+.ghc.environment.*
diff --git a/.travis.yml b/.travis.yml
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,105 +1,151 @@
-# This file has been generated -- see https://github.com/hvr/multi-ghc-travis
+# This Travis job script has been generated by a script via
+#
+#   runghc make_travis_yml_2.hs '-o' '.travis.yml' '--ghc-head' '--irc-channel=irc.freenode.org#haskell-lens' '--no-no-tests-no-bench' '--no-unconstrained' 'cabal.project'
+#
+# For more information, see https://github.com/hvr/multi-ghc-travis
+#
 language: c
 sudo: false
 
+git:
+  submodules: false  # whether to recursively clone submodules
+
+notifications:
+  irc:
+    channels:
+      - "irc.freenode.org#haskell-lens"
+    skip_join: true
+    template:
+      - "\x0313linear\x03/\x0306%{branch}\x03 \x0314%{commit}\x03 %{build_url} %{message}"
+
 cache:
   directories:
-    - $HOME/.cabsnap
     - $HOME/.cabal/packages
+    - $HOME/.cabal/store
 
 before_cache:
   - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
-  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
+  # remove files that are regenerated by 'cabal update'
+  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
+  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
+  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
+  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
+  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
 
+  - rm -rfv $HOME/.cabal/packages/head.hackage
+
 matrix:
   include:
-    - env: CABALVER=1.24 GHCVER=7.4.2
-      compiler: ": #GHC 7.4.2"
-      addons: {apt: {packages: [cabal-install-1.24,ghc-7.4.2], sources: [hvr-ghc]}}
-    - env: CABALVER=1.24 GHCVER=7.6.3
-      compiler: ": #GHC 7.6.3"
-      addons: {apt: {packages: [cabal-install-1.24,ghc-7.6.3], sources: [hvr-ghc]}}
-    - env: CABALVER=1.24 GHCVER=7.8.4
-      compiler: ": #GHC 7.8.4"
-      addons: {apt: {packages: [cabal-install-1.24,ghc-7.8.4], sources: [hvr-ghc]}}
-    - env: CABALVER=1.24 GHCVER=7.10.3
-      compiler: ": #GHC 7.10.3"
-      addons: {apt: {packages: [cabal-install-1.24,ghc-7.10.3], sources: [hvr-ghc]}}
-    - env: CABALVER=1.24 GHCVER=8.0.2
-      compiler: ": #GHC 8.0.2"
-      addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2], sources: [hvr-ghc]}}
-    - env: CABALVER=2.0 GHCVER=8.2.1
-      compiler: ": #GHC 8.2.1"
-      addons: {apt: {packages: [cabal-install-2.0,ghc-8.2.1], sources: [hvr-ghc]}}
-    - env: CABALVER=head GHCVER=head
-      compiler: ": #GHC head"
-      addons: {apt: {packages: [cabal-install-head,ghc-head], sources: [hvr-ghc]}}
+    - compiler: "ghc-8.6.1"
+      env: GHCHEAD=true
+      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.6.1], sources: [hvr-ghc]}}
+    - compiler: "ghc-8.4.3"
+    # env: TEST=--disable-tests BENCH=--disable-benchmarks
+      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.3], sources: [hvr-ghc]}}
+    - compiler: "ghc-8.2.2"
+    # env: TEST=--disable-tests BENCH=--disable-benchmarks
+      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.2], sources: [hvr-ghc]}}
+    - compiler: "ghc-8.0.2"
+    # env: TEST=--disable-tests BENCH=--disable-benchmarks
+      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.2], sources: [hvr-ghc]}}
+    - compiler: "ghc-7.10.3"
+    # env: TEST=--disable-tests BENCH=--disable-benchmarks
+      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.10.3], sources: [hvr-ghc]}}
+    - compiler: "ghc-7.8.4"
+    # env: TEST=--disable-tests BENCH=--disable-benchmarks
+      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.4], sources: [hvr-ghc]}}
+    - compiler: "ghc-7.6.3"
+    # env: TEST=--disable-tests BENCH=--disable-benchmarks
+      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.6.3], sources: [hvr-ghc]}}
+    - compiler: "ghc-7.4.2"
+    # env: TEST=--disable-tests BENCH=--disable-benchmarks
+      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.4.2], sources: [hvr-ghc]}}
+    - compiler: "ghc-head"
+      env: GHCHEAD=true
+      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-head], sources: [hvr-ghc]}}
 
   allow_failures:
-    - env: CABALVER=head GHCVER=head
+    - compiler: "ghc-head"
+    - compiler: "ghc-8.6.1"
 
 before_install:
- - unset CC
- - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
+  - HC=${CC}
+  - HCPKG=${HC/ghc/ghc-pkg}
+  - unset CC
+  - ROOTDIR=$(pwd)
+  - mkdir -p $HOME/.local/bin
+  - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"
+  - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))
+  - echo $HCNUMVER
 
 install:
- - cabal --version
- - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
- - if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ];
-   then
-     zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz >
-          $HOME/.cabal/packages/hackage.haskell.org/00-index.tar;
-   fi
- - travis_retry cabal update -v
- - sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
- - cabal install --only-dependencies --enable-tests --enable-benchmarks --dry -v > installplan.txt
- - sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt
+  - cabal --version
+  - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
+  - BENCH=${BENCH---enable-benchmarks}
+  - TEST=${TEST---enable-tests}
+  - HADDOCK=${HADDOCK-true}
+  - UNCONSTRAINED=${UNCONSTRAINED-true}
+  - NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}
+  - GHCHEAD=${GHCHEAD-false}
+  - travis_retry cabal update -v
+  - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"
+  - rm -fv cabal.project cabal.project.local
+  # Overlay Hackage Package Index for GHC HEAD: https://github.com/hvr/head.hackage
+  - |
+    if $GHCHEAD; then
+      sed -i 's/-- allow-newer: .*/allow-newer: *:base/' ${HOME}/.cabal/config
+      for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" ${HOME}/.cabal/config; done
 
-# check whether current requested install-plan matches cached package-db snapshot
- - if diff -u installplan.txt $HOME/.cabsnap/installplan.txt;
-   then
-     echo "cabal build-cache HIT";
-     rm -rfv .ghc;
-     cp -a $HOME/.cabsnap/ghc $HOME/.ghc;
-     cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/;
-   else
-     echo "cabal build-cache MISS";
-     rm -rf $HOME/.cabsnap;
-     mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin;
-     cabal install -j --only-dependencies --enable-tests --enable-benchmarks;
-   fi
+      echo 'repository head.hackage'                                                        >> ${HOME}/.cabal/config
+      echo '   url: http://head.hackage.haskell.org/'                                       >> ${HOME}/.cabal/config
+      echo '   secure: True'                                                                >> ${HOME}/.cabal/config
+      echo '   root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> ${HOME}/.cabal/config
+      echo '              2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> ${HOME}/.cabal/config
+      echo '              8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> ${HOME}/.cabal/config
+      echo '   key-threshold: 3'                                                            >> ${HOME}/.cabal.config
 
-# snapshot package-db on cache miss
- - if [ ! -d $HOME/.cabsnap ];
-   then
-      echo "snapshotting package-db to build-cache";
-      mkdir $HOME/.cabsnap;
-      cp -a $HOME/.ghc $HOME/.cabsnap/ghc;
-      cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
-   fi
+      grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
 
+      cabal new-update head.hackage -v
+    fi
+  - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
+  - "printf 'packages: \".\"\\n' > cabal.project"
+  - touch cabal.project.local
+  - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
+  - cat cabal.project || true
+  - cat cabal.project.local || true
+  - if [ -f "./configure.ac" ]; then
+      (cd "." && autoreconf -i);
+    fi
+  - rm -f cabal.project.freeze
+  - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all
+  - rm -rf .ghc.environment.* "."/dist
+  - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
+
 # Here starts the actual work to be performed for the package under test;
 # any command which exits with a non-zero exit code causes the build to fail.
 script:
- - cabal configure -v2 --enable-tests --enable-benchmarks  # -v2 provides useful information for debugging
- - cabal build # this builds all libraries and executables (including tests/benchmarks)
- - cabal test
- - cabal sdist   # tests that a source-distribution can be generated
- - export SRC_TGZ=$(cabal info . | awk '{print $2 ".tar.gz";exit}') ;
-   cd dist/;
-   if [ -f "$SRC_TGZ" ]; then
-      cabal install "$SRC_TGZ";
-   else
-      echo "expected '$SRC_TGZ' not found";
-      exit 1;
-   fi
+  # test that source-distributions can be generated
+  - (cd "." && cabal sdist)
+  - mv "."/dist/linear-*.tar.gz ${DISTDIR}/
+  - cd ${DISTDIR} || false
+  - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
+  - "printf 'packages: linear-*/*.cabal\\n' > cabal.project"
+  - touch cabal.project.local
+  - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
+  - cat cabal.project || true
+  - cat cabal.project.local || true
 
-notifications:
-  irc:
-    channels:
-      - "irc.freenode.org#haskell-lens"
-    skip_join: true
-    template:
-      - "\x0313linear\x0f/\x0306%{branch}\x0f \x0314%{commit}\x0f %{message} \x0302\x1f%{build_url}\x0f"
+  # build & run tests, build benchmarks
+  - cabal new-build -w ${HC} ${TEST} ${BENCH} all
+  - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi
 
+  # cabal check
+  - (cd linear-* && cabal check)
+
+  # haddock
+  - rm -rf ./dist-newstyle
+  - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi
+
+# REGENDATA ["-o",".travis.yml","--ghc-head","--irc-channel=irc.freenode.org#haskell-lens","--no-no-tests-no-bench","--no-unconstrained","cabal.project"]
 # EOF
diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
--- a/CHANGELOG.markdown
+++ b/CHANGELOG.markdown
@@ -1,3 +1,13 @@
+1.20.8 [2018.07.03]
+-------------------
+* Add instances of the `Field` classes from `lens`.
+* Add `Epsilon` instance for `Complex`.
+* Use specialized implementations of the `null` and `length` methods in
+  `Foldable` instances.
+* Add `Hashable1` instances for data types in `linear`. Also add a
+  `Hashable` instance for `V`.
+* Fix a bug in which `Quaternion`s were incorrectly exponentiated.
+
 1.20.7
 ------
 * Support `semigroupoids-5.2.1` and `doctest-0.12`
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.7
+version:       1.20.8
 license:       BSD3
 cabal-version: >= 1.8
 license-file:  LICENSE
@@ -13,7 +13,14 @@
 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.3, GHC == 7.8.4, GHC == 7.10.3, GHC==8.0.2, GHC==8.2.1
+tested-with:   GHC == 7.4.2
+             , GHC == 7.6.3
+             , GHC == 7.8.4
+             , GHC == 7.10.3
+             , GHC == 8.0.2
+             , GHC == 8.2.2
+             , GHC == 8.4.3
+             , GHC == 8.6.1
 extra-source-files:
   .gitignore
   .travis.yml
@@ -55,15 +62,15 @@
     binary               >= 0.5   && < 0.9,
     bytes                >= 0.15  && < 1,
     cereal               >= 0.4.1.1 && < 0.6,
-    containers           >= 0.4   && < 0.6,
+    containers           >= 0.4   && < 0.7,
     deepseq              >= 1.1   && < 1.5,
     distributive         >= 0.2.2 && < 1,
     ghc-prim,
     hashable             >= 1.1   && < 1.3,
-    lens                 >= 4     && < 5,
+    lens                 >= 4.15.2 && < 5,
     reflection           >= 1.3.2 && < 3,
     semigroups           >= 0.9   && < 1,
-    semigroupoids        >= 5.2.1 && < 5.3,
+    semigroupoids        >= 5.2.1 && < 6,
     tagged               >= 0.4.4 && < 1,
     transformers         >= 0.2   && < 0.6,
     transformers-compat  >= 0.4   && < 1,
@@ -106,6 +113,9 @@
   ghc-options: -Wall -fwarn-tabs -O2 -fdicts-cheap -funbox-strict-fields
   hs-source-dirs: src
 
+  if impl(ghc >= 7.10)
+    ghc-options: -fno-warn-trustworthy-safe
+
   -- 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
@@ -118,7 +128,7 @@
   hs-source-dirs: tests
   build-depends:
     base,
-    doctest        >= 0.11.1 && < 0.13,
+    doctest        >= 0.11.1 && < 0.17,
     lens,
     linear,
     simple-reflect >= 0.3.1
diff --git a/src/Linear/Affine.hs b/src/Linear/Affine.hs
--- a/src/Linear/Affine.hs
+++ b/src/Linear/Affine.hs
@@ -16,6 +16,11 @@
 {-# LANGUAGE StandaloneDeriving #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 #endif
+
+#ifndef MIN_VERSION_hashable
+#define MIN_VERSION_hashable(x,y,z) 1
+#endif
+
 -----------------------------------------------------------------------------
 -- |
 -- License     :  BSD-style (see the file LICENSE)
@@ -45,6 +50,9 @@
 import Data.Functor.Rep as Rep
 import Data.HashMap.Lazy (HashMap)
 import Data.Hashable
+#if (MIN_VERSION_hashable(1,2,5))
+import Data.Hashable.Lifted
+#endif
 import Data.IntMap (IntMap)
 import Data.Ix
 import Data.Map (Map)
@@ -177,6 +185,12 @@
 instance Serialize (f a) => Serialize (Point f a) where
   put (P p) = Cereal.put p
   get = P `liftM` Cereal.get
+
+#if (MIN_VERSION_hashable(1,2,5))
+instance Hashable1 f => Hashable1 (Point f) where
+  liftHashWithSalt h s (P f) = liftHashWithSalt h s f
+  {-# INLINE liftHashWithSalt #-}
+#endif
 
 #if __GLASGOW_HASKELL__ < 708
 instance forall f. Typeable1 f => Typeable1 (Point f) where
diff --git a/src/Linear/Epsilon.hs b/src/Linear/Epsilon.hs
--- a/src/Linear/Epsilon.hs
+++ b/src/Linear/Epsilon.hs
@@ -11,6 +11,7 @@
 module Linear.Epsilon
   ( Epsilon(..)
   ) where
+import Data.Complex (Complex, magnitude)
 import Foreign.C.Types (CFloat, CDouble)
 
 -- | Provides a fairly subjective test to see if a quantity is near zero.
@@ -45,3 +46,6 @@
 -- | @'abs' a '<=' 1e-12@
 instance Epsilon CDouble where
   nearZero a = abs a <= 1e-12
+
+instance (Epsilon a, RealFloat a) => Epsilon (Complex a) where
+  nearZero = nearZero . magnitude
diff --git a/src/Linear/Plucker.hs b/src/Linear/Plucker.hs
--- a/src/Linear/Plucker.hs
+++ b/src/Linear/Plucker.hs
@@ -183,6 +183,10 @@
   foldMap g (Plucker a b c d e f) =
     g a `mappend` g b `mappend` g c `mappend` g d `mappend` g e `mappend` g f
   {-# INLINE foldMap #-}
+#if __GLASGOW_HASKELL__ >= 710
+  null _ = False
+  length _ =  6
+#endif
 
 instance Traversable Plucker where
   traverse g (Plucker a b c d e f) =
@@ -639,3 +643,21 @@
 instance Show1 Plucker where showsPrec1 = showsPrec
 instance Read1 Plucker where readsPrec1 = readsPrec
 #endif
+
+instance Field1 (Plucker a) (Plucker a) a a where
+  _1 f (Plucker x y z u v w) = f x <&> \x' -> Plucker x' y z u v w
+
+instance Field2 (Plucker a) (Plucker a) a a where
+  _2 f (Plucker x y z u v w) = f y <&> \y' -> Plucker x y' z u v w
+
+instance Field3 (Plucker a) (Plucker a) a a where
+  _3 f (Plucker x y z u v w) = f z <&> \z' -> Plucker x y z' u v w
+
+instance Field4 (Plucker a) (Plucker a) a a where
+  _4 f (Plucker x y z u v w) = f u <&> \u' -> Plucker x y z u' v w
+
+instance Field5 (Plucker a) (Plucker a) a a where
+  _5 f (Plucker x y z u v w) = f v <&> \v' -> Plucker x y z u v' w
+
+instance Field6 (Plucker a) (Plucker a) a a where
+  _6 f (Plucker x y z u v w) = f w <&> \w' -> Plucker x y z u v w'
diff --git a/src/Linear/Quaternion.hs b/src/Linear/Quaternion.hs
--- a/src/Linear/Quaternion.hs
+++ b/src/Linear/Quaternion.hs
@@ -13,6 +13,10 @@
 {-# LANGUAGE DataKinds #-}
 #endif
 
+#ifndef 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
@@ -61,6 +65,9 @@
 import Data.Functor.Classes
 import Data.Functor.Rep
 import Data.Hashable
+#if (MIN_VERSION_hashable(1,2,5))
+import Data.Hashable.Lifted
+#endif
 import Data.Serialize as Cereal
 import GHC.Arr (Ix(..))
 import qualified Data.Foldable as F
@@ -202,6 +209,10 @@
   {-# INLINE foldMap #-}
   foldr f z (Quaternion e v) = f e (F.foldr f z v)
   {-# INLINE foldr #-}
+#if __GLASGOW_HASKELL__ >= 710
+  null _ = False
+  length _ = 4
+#endif
 
 instance Traversable Quaternion where
   traverse f (Quaternion e v) = Quaternion <$> f e <*> traverse f v
@@ -258,6 +269,12 @@
   hashWithSalt s (Quaternion a b) = s `hashWithSalt` a `hashWithSalt` b
   {-# INLINE hashWithSalt #-}
 
+#if (MIN_VERSION_hashable(1,2,5))
+instance Hashable1 Quaternion where
+  liftHashWithSalt h s (Quaternion a b) = liftHashWithSalt h (h s a) b
+  {-# INLINE liftHashWithSalt #-}
+#endif
+
 qNaN :: RealFloat a => Quaternion a
 qNaN = Quaternion fNaN (V3 fNaN fNaN fNaN) where fNaN = 0/0
 {-# INLINE qNaN #-}
@@ -358,9 +375,9 @@
 {-# INLINE pow #-}
 
 sqrte2pqiq :: (Floating a, Ord a) => a -> a -> a
-sqrte2pqiq e qiq -- = sqrt (e*e) + qiq
+sqrte2pqiq e qiq -- = sqrt (e*e + qiq)
   | e < - 1.5097698010472593e153 = -(qiq/e) - e
-  | e < 5.582399551122541e57      = sqrt ((e*e) + qiq) -- direct definition
+  | e < 5.582399551122541e57      = sqrt (e*e + qiq) -- direct definition
   | otherwise                     = (qiq/e) + e
 -- {-# SPECIALIZE sqrte2pqiq :: Double -> Double -> Double #-}
 -- {-# SPECIALIZE sqrte2pqiq :: Float -> Float -> Float #-}
@@ -391,11 +408,11 @@
     | ai <- sqrt qiq, exe <- exp e = reimagine (exe * cos ai) (exe * (sin ai / ai)) q
     where qiq = qi q
   {-# INLINE exp #-}
-  log q@(Quaternion e v@(V3 _i j k))
+  log q@(Quaternion e v)
     | qiq == 0 = if e >= 0
-                 then Quaternion (log e) v
-                 else Quaternion (log (negate e)) (V3 pi j k) -- mmm, pi
-    | ai <- sqrt qiq = reimagine (log m) (atan2 m e / ai) q
+                 then Quaternion (log e) v                   -- Using v rather than 0 preserves negative zeros
+                 else Quaternion (negate (log (negate e))) v -- negative scalar: negate quaternion, take log, negate again, preserves negative zeros
+    | ai <- sqrt qiq = reimagine (log m) (acos (e / m) / ai) q
     where qiq = qi q
           m = sqrte2pqiq e qiq
   {-# INLINE log #-}
@@ -416,7 +433,6 @@
   cos q@(Quaternion e v)
     | qiq == 0 = Quaternion (cos e) v
     | ai <- sqrt qiq = reimagine (cos e * cosh ai) (- sin e / ai / sinh ai) q -- 0.15 bits error
-                    -- reimagine (cos e * cosh ai) (- sin e * sinh ai / ai) q -- 13.5 bits worse
     where qiq = qi q
   {-# INLINE cos #-}
 
@@ -655,3 +671,15 @@
 instance Show1 Quaternion where showsPrec1 = showsPrec
 instance Read1 Quaternion where readsPrec1 = readsPrec
 #endif
+
+instance Field1 (Quaternion a) (Quaternion a) a a where
+  _1 f (Quaternion w xyz) = f w <&> \w' -> Quaternion w' xyz
+
+instance Field2 (Quaternion a) (Quaternion a) a a where
+  _2 f (Quaternion w (V3 x y z)) = f x <&> \x' -> Quaternion w (V3 x' y z)
+
+instance Field3 (Quaternion a) (Quaternion a) a a where
+  _3 f (Quaternion w (V3 x y z)) = f y <&> \y' -> Quaternion w (V3 x y' z)
+
+instance Field4 (Quaternion a) (Quaternion a) a a where
+  _4 f (Quaternion w (V3 x y z)) = f z <&> \z' -> Quaternion w (V3 x y z')
diff --git a/src/Linear/V.hs b/src/Linear/V.hs
--- a/src/Linear/V.hs
+++ b/src/Linear/V.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE TypeOperators #-}
 {-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
@@ -19,6 +20,10 @@
 {-# LANGUAGE Trustworthy #-}
 {-# LANGUAGE DeriveGeneric #-}
 
+#ifndef MIN_VERSION_hashable
+#define MIN_VERSION_hashable(x,y,z) 1
+#endif
+
 #ifndef MIN_VERSION_reflection
 #define MIN_VERSION_reflection(x,y,z) 1
 #endif
@@ -78,6 +83,10 @@
 import Data.Functor.Bind
 import Data.Functor.Classes
 import Data.Functor.Rep as Rep
+import Data.Hashable
+#if (MIN_VERSION_hashable(1,2,5))
+import Data.Hashable.Lifted
+#endif
 #if __GLASGOW_HASKELL__ < 708
 import Data.Proxy
 #endif
@@ -359,6 +368,19 @@
   distribute f = V $ V.generate (reflectDim (Proxy :: Proxy n)) $ \i -> fmap (\(V v) -> unsafeIndex v i) f
   {-# INLINE distribute #-}
 
+instance Hashable a => Hashable (V n a) where
+  hashWithSalt s0 (V v) =
+    V.foldl' (\s a -> s `hashWithSalt` a) s0 v
+      `hashWithSalt` V.length v
+
+#if (MIN_VERSION_hashable(1,2,5))
+instance Dim n => Hashable1 (V n) where
+  liftHashWithSalt h s0 (V v) =
+    V.foldl' (\s a -> h s a) s0 v
+      `hashWithSalt` V.length v
+  {-# INLINE liftHashWithSalt #-}
+#endif
+
 instance (Dim n, Storable a) => Storable (V n a) where
   sizeOf _ = reflectDim (Proxy :: Proxy n) * sizeOf (undefined:: a)
   {-# INLINE sizeOf #-}
@@ -445,9 +467,9 @@
 type instance IxValue (V n a) = a
 
 instance Ixed (V n a) where
-  ix i f (V as)
-     | i < 0 || i >= V.length as = pure $ V as
-     | otherwise = f (as ! i) <&> \a -> V $ as V.// [(i, a)]
+  ix i f v@(V as)
+     | i < 0 || i >= V.length as = pure v
+     | otherwise = vLens i f v
   {-# INLINE ix #-}
 
 instance Dim n => MonadZip (V n) where
@@ -583,3 +605,29 @@
   basicUnsafeIndexM (V_VN _ v) i =
     liftM V $ V.generateM d (\j -> G.basicUnsafeIndexM v (d*i+j))
     where d = reflectDim (Proxy :: Proxy n)
+
+vLens :: Int -> Lens' (V n a) a
+vLens i = \f (V v) -> f (v V.! i) <&> \a -> V (v V.// [(i, a)])
+{-# INLINE vLens #-}
+
+#ifdef USE_TYPE_LITS
+instance ( 1 <= n) => Field1  (V n a) (V n a) a a where _1  = vLens  0
+instance ( 2 <= n) => Field2  (V n a) (V n a) a a where _2  = vLens  1
+instance ( 3 <= n) => Field3  (V n a) (V n a) a a where _3  = vLens  2
+instance ( 4 <= n) => Field4  (V n a) (V n a) a a where _4  = vLens  3
+instance ( 5 <= n) => Field5  (V n a) (V n a) a a where _5  = vLens  4
+instance ( 6 <= n) => Field6  (V n a) (V n a) a a where _6  = vLens  5
+instance ( 7 <= n) => Field7  (V n a) (V n a) a a where _7  = vLens  6
+instance ( 8 <= n) => Field8  (V n a) (V n a) a a where _8  = vLens  7
+instance ( 9 <= n) => Field9  (V n a) (V n a) a a where _9  = vLens  8
+instance (10 <= n) => Field10 (V n a) (V n a) a a where _10 = vLens  9
+instance (11 <= n) => Field11 (V n a) (V n a) a a where _11 = vLens 10
+instance (12 <= n) => Field12 (V n a) (V n a) a a where _12 = vLens 11
+instance (13 <= n) => Field13 (V n a) (V n a) a a where _13 = vLens 12
+instance (14 <= n) => Field14 (V n a) (V n a) a a where _14 = vLens 13
+instance (15 <= n) => Field15 (V n a) (V n a) a a where _15 = vLens 14
+instance (16 <= n) => Field16 (V n a) (V n a) a a where _16 = vLens 15
+instance (17 <= n) => Field17 (V n a) (V n a) a a where _17 = vLens 16
+instance (18 <= n) => Field18 (V n a) (V n a) a a where _18 = vLens 17
+instance (19 <= n) => Field19 (V n a) (V n a) a a where _19 = vLens 18
+#endif
diff --git a/src/Linear/V0.hs b/src/Linear/V0.hs
--- a/src/Linear/V0.hs
+++ b/src/Linear/V0.hs
@@ -56,8 +56,13 @@
 import Data.Functor.Classes
 import Data.Functor.Rep
 import Data.Hashable
+#if (MIN_VERSION_hashable(1,2,5))
+import Data.Hashable.Lifted
+#endif
 import Data.Ix
+#if !(MIN_VERSION_base(4,11,0))
 import Data.Semigroup
+#endif
 import Data.Serialize -- cereal
 #if __GLASGOW_HASKELL__ >= 707
 import qualified Data.Vector as V
@@ -132,6 +137,10 @@
 instance Foldable V0 where
   foldMap _ V0 = mempty
   {-# INLINE foldMap #-}
+#if __GLASGOW_HASKELL__ >= 710
+  null _ = True
+  length _ = 0
+#endif
 
 instance Traversable V0 where
   traverse _ V0 = pure V0
@@ -242,6 +251,12 @@
 #endif
   hashWithSalt s V0 = s
   {-# INLINE hashWithSalt #-}
+
+#if (MIN_VERSION_hashable(1,2,5))
+instance Hashable1 V0 where
+  liftHashWithSalt _ s V0 = s
+  {-# INLINE liftHashWithSalt #-}
+#endif
 
 instance Epsilon (V0 a) where
   nearZero _ = True
diff --git a/src/Linear/V1.hs b/src/Linear/V1.hs
--- a/src/Linear/V1.hs
+++ b/src/Linear/V1.hs
@@ -61,6 +61,9 @@
 import Data.Functor.Classes
 import Data.Functor.Rep
 import Data.Hashable
+#if (MIN_VERSION_hashable(1,2,5))
+import Data.Hashable.Lifted
+#endif
 import Data.Semigroup.Foldable
 #if __GLASGOW_HASKELL__ >= 707
 import qualified Data.Vector as V
@@ -108,7 +111,7 @@
 --data V2 a = V2 !a !a deriving (Eq,Ord,Show,Read,Data,Typeable)
 newtype V1 a = V1 a
   deriving (Eq,Ord,Show,Read,Data,Typeable,
-            Functor,Foldable,Traversable,
+            Functor,Traversable,
             Epsilon,Storable,NFData
 #if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 702
            ,Generic
@@ -118,6 +121,13 @@
 #endif
            )
 
+instance Foldable V1 where
+  foldMap f (V1 a) = f a
+#if __GLASGOW_HASKELL__ >= 710
+  null _ = False
+  length _ = 1
+#endif
+
 #if __GLASGOW_HASKELL__ >= 707
 instance Finite V1 where
   type Size V1 = 1
@@ -229,6 +239,12 @@
 #endif
   hashWithSalt s (V1 a) = s `hashWithSalt` a
 
+#if (MIN_VERSION_hashable(1,2,5))
+instance Hashable1 V1 where
+  liftHashWithSalt h s (V1 a) = h s a
+  {-# INLINE liftHashWithSalt #-}
+#endif
+
 instance Metric V1 where
   dot (V1 a) (V1 b) = a * b
   {-# INLINE dot #-}
@@ -381,3 +397,6 @@
 instance Show1 V1 where showsPrec1 = showsPrec
 instance Read1 V1 where readsPrec1 = readsPrec
 #endif
+
+instance Field1 (V1 a) (V1 b) a b where
+  _1 f (V1 x) = V1 <$> f x
diff --git a/src/Linear/V2.hs b/src/Linear/V2.hs
--- a/src/Linear/V2.hs
+++ b/src/Linear/V2.hs
@@ -13,6 +13,10 @@
 {-# LANGUAGE DataKinds #-}
 #endif
 
+#ifndef 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
@@ -59,6 +63,9 @@
 import Data.Functor.Classes
 import Data.Functor.Rep
 import Data.Hashable
+#if (MIN_VERSION_hashable(1,2,5))
+import Data.Hashable.Lifted
+#endif
 import Data.Semigroup
 import Data.Semigroup.Foldable
 import Data.Serialize as Cereal
@@ -129,6 +136,10 @@
 instance Foldable V2 where
   foldMap f (V2 a b) = f a `mappend` f b
   {-# INLINE foldMap #-}
+#if __GLASGOW_HASKELL__ >= 710
+  null _ = False
+  length _ = 2
+#endif
 
 instance Traversable V2 where
   traverse f (V2 a b) = V2 <$> f a <*> f b
@@ -156,6 +167,12 @@
   hashWithSalt s (V2 a b) = s `hashWithSalt` a `hashWithSalt` b
   {-# INLINE hashWithSalt #-}
 
+#if (MIN_VERSION_hashable(1,2,5))
+instance Hashable1 V2 where
+  liftHashWithSalt h s (V2 a b) = s `h` a `h` b
+  {-# INLINE liftHashWithSalt #-}
+#endif
+
 instance Additive V2 where
   zero = pure 0
   {-# INLINE zero #-}
@@ -455,3 +472,9 @@
 instance Show1 V2 where showsPrec1 = showsPrec
 instance Read1 V2 where readsPrec1 = readsPrec
 #endif
+
+instance Field1 (V2 a) (V2 a) a a where
+  _1 f (V2 x y) = f x <&> \x' -> V2 x' y
+
+instance Field2 (V2 a) (V2 a) a a where
+  _2 f (V2 x y) = f y <&> \y' -> V2 x y'
diff --git a/src/Linear/V3.hs b/src/Linear/V3.hs
--- a/src/Linear/V3.hs
+++ b/src/Linear/V3.hs
@@ -13,6 +13,10 @@
 {-# LANGUAGE DataKinds #-}
 #endif
 
+#ifndef 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
@@ -59,7 +63,12 @@
 import Data.Functor.Classes
 import Data.Functor.Rep
 import Data.Hashable
+#if (MIN_VERSION_hashable(1,2,5))
+import Data.Hashable.Lifted
+#endif
+#if !(MIN_VERSION_base(4,11,0))
 import Data.Semigroup
+#endif
 import Data.Semigroup.Foldable
 import Data.Serialize as Cereal -- cereal
 #if __GLASGOW_HASKELL__ >= 707
@@ -99,7 +108,7 @@
 
 #if __GLASGOW_HASKELL__ >= 707
 instance Finite V3 where
-  type Size V3 = 3 
+  type Size V3 = 3
   toV (V3 a b c) = V (V.fromListN 3 [a,b,c])
   fromV (V v) = V3 (v V.! 0) (v V.! 1) (v V.! 2)
 #endif
@@ -113,6 +122,10 @@
 instance Foldable V3 where
   foldMap f (V3 a b c) = f a `mappend` f b `mappend` f c
   {-# INLINE foldMap #-}
+#if __GLASGOW_HASKELL__ >= 710
+  null _ = False
+  length _ = 3
+#endif
 
 instance Traversable V3 where
   traverse f (V3 a b c) = V3 <$> f a <*> f b <*> f c
@@ -226,6 +239,12 @@
   hashWithSalt s (V3 a b c) = s `hashWithSalt` a `hashWithSalt` b `hashWithSalt` c
   {-# INLINE hashWithSalt #-}
 
+#if (MIN_VERSION_hashable(1,2,5))
+instance Hashable1 V3 where
+  liftHashWithSalt h s (V3 a b c) = s `h` a `h` b `h` c
+  {-# INLINE liftHashWithSalt #-}
+#endif
+
 instance Metric V3 where
   dot (V3 a b c) (V3 d e f) = a * d + b * e + c * f
   {-# INLINABLE dot #-}
@@ -475,3 +494,12 @@
 instance Show1 V3 where showsPrec1 = showsPrec
 instance Read1 V3 where readsPrec1 = readsPrec
 #endif
+
+instance Field1 (V3 a) (V3 a) a a where
+  _1 f (V3 x y z) = f x <&> \x' -> V3 x' y z
+
+instance Field2 (V3 a) (V3 a) a a where
+  _2 f (V3 x y z) = f y <&> \y' -> V3 x y' z
+
+instance Field3 (V3 a) (V3 a) a a where
+  _3 f (V3 x y z) = f z <&> \z' -> V3 x y z'
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 @@
 {-# LANGUAGE DataKinds #-}
 #endif
 
+#ifndef 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
@@ -65,7 +69,12 @@
 import Data.Functor.Classes
 import Data.Functor.Rep
 import Data.Hashable
+#if (MIN_VERSION_hashable(1,2,5))
+import Data.Hashable.Lifted
+#endif
+#if !(MIN_VERSION_base(4,11,0))
 import Data.Semigroup
+#endif
 import Data.Semigroup.Foldable
 import Data.Serialize as Cereal
 #if __GLASGOW_HASKELL__ >= 707
@@ -120,6 +129,10 @@
 instance Foldable V4 where
   foldMap f (V4 a b c d) = f a `mappend` f b `mappend` f c `mappend` f d
   {-# INLINE foldMap #-}
+#if __GLASGOW_HASKELL__ >= 710
+  null _ = False
+  length _ = 4
+#endif
 
 instance Traversable V4 where
   traverse f (V4 a b c d) = V4 <$> f a <*> f b <*> f c <*> f d
@@ -246,6 +259,12 @@
   hashWithSalt s (V4 a b c d) = s `hashWithSalt` a `hashWithSalt` b `hashWithSalt` c `hashWithSalt` d
   {-# INLINE hashWithSalt #-}
 
+#if (MIN_VERSION_hashable(1,2,5))
+instance Hashable1 V4 where
+  liftHashWithSalt h s (V4 a b c d) = s `h` a `h` b `h` c `h` d
+  {-# INLINE liftHashWithSalt #-}
+#endif
+
 -- | A space that distinguishes orthogonal basis vectors '_x', '_y', '_z', '_w'. (It may have more.)
 class R3 t => R4 t where
   -- |
@@ -613,3 +632,15 @@
 instance Show1 V4 where showsPrec1 = showsPrec
 instance Read1 V4 where readsPrec1 = readsPrec
 #endif
+
+instance Field1 (V4 a) (V4 a) a a where
+  _1 f (V4 x y z w) = f x <&> \x' -> V4 x' y z w
+
+instance Field2 (V4 a) (V4 a) a a where
+  _2 f (V4 x y z w) = f y <&> \y' -> V4 x y' z w
+
+instance Field3 (V4 a) (V4 a) a a where
+  _3 f (V4 x y z w) = f z <&> \z' -> V4 x y z' w
+
+instance Field4 (V4 a) (V4 a) a a where
+  _4 f (V4 x y z w) = f w <&> \w' -> V4 x y z w'
diff --git a/tests/Binary.hs b/tests/Binary.hs
--- a/tests/Binary.hs
+++ b/tests/Binary.hs
@@ -1,5 +1,9 @@
+{-# LANGUAGE CPP #-}
 module Binary (tests) where
+
+#if !(MIN_VERSION_base(4,8,0))
 import Control.Applicative
+#endif
 import Data.Binary.Put
 import Data.Binary.Get
 import Linear
