diff --git a/som.cabal b/som.cabal
--- a/som.cabal
+++ b/som.cabal
@@ -1,5 +1,5 @@
 name:           som
-version:        4.0
+version:        4.1
 synopsis:       Self-Organising Maps
 description:    A Kohonen Self-organising Map (SOM) maps input patterns 
                 onto a regular grid (usually two-dimensional) where each
diff --git a/src/Data/Datamining/Clustering/SOM.hs b/src/Data/Datamining/Clustering/SOM.hs
--- a/src/Data/Datamining/Clustering/SOM.hs
+++ b/src/Data/Datamining/Clustering/SOM.hs
@@ -16,7 +16,7 @@
 --
 -- In layman's terms, a SOM can be useful when you you want to discover
 -- the underlying structure of some data. A tutorial is available at
--- <https://github.com/mhwombat/som/wiki>
+-- <https://github.com/mhwombat/som/wiki>.
 --
 -- References:
 --
@@ -28,23 +28,20 @@
 {-# LANGUAGE UnicodeSyntax #-}
 module Data.Datamining.Clustering.SOM
   (
+    -- * Construction
     SOM,
     defaultSOM,
     customSOM,
     gaussian,
     decayingGaussian,
-    toGridMap
+    -- * Deconstruction
+    toGridMap,
+    -- * Advanced control
+    trainNeighbourhood,
+    incrementCounter
   ) where
 
 import Data.Datamining.Clustering.SOMInternal (SOM, defaultSOM,
-  customSOM, gaussian, decayingGaussian, toGridMap)
-
-{- $Vector
-If you wish to use a SOM with raw numeric vectors, use @no-warn-orphans@
-and add the following to your code:
-
-> instance (Floating a, Fractional a, Ord a, Eq a) ⇒ Pattern [a] a where
->   difference = euclideanDistanceSquared
->   makeSimilar = adjustVector
--}
+  customSOM, gaussian, decayingGaussian, toGridMap, trainNeighbourhood,
+  incrementCounter)
 
diff --git a/src/Data/Datamining/Clustering/SOMInternal.hs b/src/Data/Datamining/Clustering/SOMInternal.hs
--- a/src/Data/Datamining/Clustering/SOMInternal.hs
+++ b/src/Data/Datamining/Clustering/SOMInternal.hs
@@ -16,14 +16,20 @@
 
 module Data.Datamining.Clustering.SOMInternal
   (
+    -- * Construction
     SOM(..),
     defaultSOM,
     customSOM,
     gaussian,
     decayingGaussian,
-    toGridMap
+    -- * Deconstruction
+    toGridMap,
+    -- * Advanced control
+    trainNeighbourhood,
+    incrementCounter
   ) where
 
+import qualified Data.Foldable as F (Foldable, foldr)
 import Data.List (foldl', minimumBy)
 import Data.Ord (comparing)
 import qualified Math.Geometry.Grid as G (Grid(..))
@@ -32,6 +38,19 @@
 import Data.Datamining.Clustering.Classifier(Classifier(..))
 import Prelude hiding (lookup)
 
+-- | A Self-Organising Map (SOM).
+--
+--   Although @SOM@ implements @GridMap@, most users will only need the
+--   interface provided by @Classifier@. If you chose to use the 
+--   @GridMap@ functions, please note:
+--
+--   1. The functions @adjust@, and @adjustWithKey@ do not increment the
+--      counter. You can do so manually with @incrementCounter@.
+--
+--   2. The functions @map@ and @mapWithKey@ are not implemented (they
+--      just return an @error@). It would be problematic to implement
+--      them because the input SOM and the output SOM would have to have
+--      the same @Metric@ type.
 data SOM gm k p = SOM
   {
     sGridMap ∷ gm p,
@@ -39,10 +58,33 @@
     sCounter ∷ Int
   }
 
+instance (F.Foldable gm) ⇒ F.Foldable (SOM gm k) where
+  foldr f x g = F.foldr f x (sGridMap g)
+
+instance (G.Grid (gm p)) ⇒ G.Grid (SOM gm k p) where
+  type Index (SOM gm k p) = G.Index (gm p)
+  indices = G.indices . sGridMap
+  distance = G.distance . sGridMap
+  neighbours = G.neighbours . sGridMap
+  contains = G.contains . sGridMap
+  viewpoint = G.viewpoint . sGridMap
+  tileCount = G.tileCount . sGridMap
+  null = G.null . sGridMap
+  nonNull = G.nonNull . sGridMap
+
+instance (F.Foldable gm, GM.GridMap gm p, G.Grid (GM.BaseGrid gm p)) ⇒ GM.GridMap (SOM gm k) p where
+  type BaseGrid (SOM gm k) p = GM.BaseGrid gm p
+  toGrid = GM.toGrid . sGridMap
+  toMap = GM.toMap . sGridMap
+  mapWithKey = error "Not implemented"
+  adjustWithKey f k s = s { sGridMap=gm' }
+    where gm = sGridMap s
+          gm' = GM.adjustWithKey f k gm
+
 currentLearningFunction ∷ SOM gm k p → (Int → Metric p)
 currentLearningFunction s = (sLearningFunction s) (sCounter s)
 
--- | Extract the grid and current models from the SOM.
+-- | Extracts the grid and current models from the SOM.
 toGridMap ∷ GM.GridMap gm p ⇒ SOM gm k p → gm p
 toGridMap = sGridMap
 
@@ -52,22 +94,29 @@
 adjustNode g f target bmu k = makeSimilar target (f d)
   where d = G.distance g bmu k
 
-trainWithBMU
+-- | Trains the specified node and the neighbourood around it to better
+--   match a target.
+--   Most users should use @train@, which automatically determines
+--   the BMU and trains it and its neighbourhood.
+trainNeighbourhood
   ∷ (Pattern p, G.Grid (gm p), GM.GridMap gm p,
       G.Index (GM.BaseGrid gm p) ~ G.Index (gm p)) ⇒
      SOM gm k p → G.Index (gm p) → p → SOM gm k p
-trainWithBMU s bmu target = s { sGridMap=gm' }
+trainNeighbourhood s bmu target = s { sGridMap=gm' }
   where gm = sGridMap s
         gm' = GM.mapWithKey (adjustNode gm f target bmu) gm
         f = currentLearningFunction s
 
+incrementCounter :: SOM gm k p → SOM gm k p
+incrementCounter s = s { sCounter=sCounter s + 1}
+
 justTrain
   ∷ (Ord (Metric p), Pattern p, G.Grid (gm p),
       GM.GridMap gm (Metric p), GM.GridMap gm p,
       G.Index (GM.BaseGrid gm (Metric p)) ~ G.Index (gm p),
       G.Index (GM.BaseGrid gm p) ~ G.Index (gm p)) ⇒
      SOM gm k p → p → SOM gm k p
-justTrain s p = trainWithBMU s bmu p
+justTrain s p = trainNeighbourhood s bmu p
   where ds = GM.toList . GM.map (p `difference`) . sGridMap $ s
         bmu = fst . minimumBy (comparing snd) $ ds
 
@@ -80,35 +129,33 @@
   numModels = G.tileCount . sGridMap
   models = GM.elems . sGridMap
   differences s p = GM.toList . GM.map (p `difference`) . sGridMap $ s
-  trainBatch s ps = (foldl' justTrain s ps) {sCounter=sCounter s + 1}
-  reportAndTrain s p = (bmu, ds, s'')
+  trainBatch s = incrementCounter . foldl' justTrain s
+  reportAndTrain s p = (bmu, ds, incrementCounter s')
     where ds = differences s p
           bmu = fst . minimumBy (comparing snd) $ ds
-          s' = trainWithBMU s bmu p
-          s'' = s' { sCounter=sCounter s + 1}
+          s' = trainNeighbourhood s bmu p
 
 
--- Creates a classifier with a default (bell-shaped) learning function.
+-- | Creates a classifier with a default (bell-shaped) learning
+--   function. Usage is @'defaultSOM' gm r w t@, where:
+--
+--   [@gm@] The geometry and initial models for this classifier.
+--   A reasonable choice here is @'lazyGridMap' g ps@, where @g@ is a
+--   @'HexHexGrid'@, and @ps@ is a set of random patterns.
+--
+--   [@r@] The learning rate to be applied to the BMU (Best Matching Unit)
+--   at "time" zero. The BMU is the model which best matches the
+--   current target pattern.
+--
+--   [@w@] The width of the bell curve at "time" zero.
+--
+--   [@t@] Controls how rapidly the learning rate decays. After this
+--   time, any learning done by the classifier will be negligible.
+--   We recommend setting this parameter to the number of patterns
+--   (or pattern batches) that will be presented to the classifier. An
+--   estimate is fine.
 defaultSOM 
-  ∷ Floating (Metric p) ⇒
-  -- | The geometry and initial models for this classifier.
-  --   A reasonable choice here is 'lazyGridMap g ps', where 'g' is a
-  --   @'Math.Geometry.Grid.HexHexGrid'@, and 'ps' is a set of
-  --   random patterns.
-  gm p →
-  -- | The learning rate to be applied to the BMU (Best Matching Unit)
-  --   at "time" zero. The BMU is the model which best matches the
-  --   current target pattern.
-  Metric p →
-  -- | The width of the bell curve at "time" zero.
-  Metric p →
-  -- | After this time, any learning done by the classifier will be
-  --   negligible. Recommend setting this parameter to the number of
-  --   patterns (or pattern batches) that will be presented to the
-  --   classifier. An estimate is fine.
-  Int →
-  -- | The result
-  SOM gm k p
+  ∷ Floating (Metric p) ⇒ gm p → Metric p → Metric p → Int → SOM gm k p
 defaultSOM gm r w t = 
   SOM { 
         sGridMap=gm, 
@@ -116,26 +163,24 @@
         sCounter=0
       }
 
--- Creates a classifier with a custom learning function.
-customSOM ∷ 
-  -- | The geometry and initial models for this classifier.
-  --   A reasonable choice here is 'lazyGridMap g ps', where 'g' is a
-  --   @'Math.Geometry.Grid.HexHexGrid'@, and 'ps' is a set of
-  --   random patterns.
-  gm p →
-  -- | A function used to adjust the models in the classifier.
-  --   This function will be invoked with two parameters.
-  --   The first parameter will indicate how many patterns (or pattern 
-  --   batches) have previously been presented to this classifier. 
-  --   Typically this is used to make the learning rate decay over time.
-  --   The second parameter to the function is the grid distance from
-  --   the node being updated to the BMU (Best Matching Unit).
-  --   The output is the learning rate for that node (the amount by
-  --   which the node's model should be updated to match the target).
-  --   The learning rate should be between zero and one.
-  (Int → Int → Metric p) →
-  -- | The result
-  SOM gm k p
+-- | Creates a classifier with a custom learning function. 
+--   Usage is @'customSOM' gm g@, where:
+--
+--   [@gm@] The geometry and initial models for this classifier.
+--   A reasonable choice here is @'lazyGridMap' g ps@, where @g@ is a
+--   @'HexHexGrid'@, and @ps@ is a set of random patterns.
+--
+--   [@f@] A function used to adjust the models in the classifier.
+--   This function will be invoked with two parameters.
+--   The first parameter will indicate how many patterns (or pattern 
+--   batches) have previously been presented to this classifier. 
+--   Typically this is used to make the learning rate decay over time.
+--   The second parameter to the function is the grid distance from
+--   the node being updated to the BMU (Best Matching Unit).
+--   The output is the learning rate for that node (the amount by
+--   which the node's model should be updated to match the target).
+--   The learning rate should be between zero and one.
+customSOM ∷ gm p → (Int → Int → Metric p) → SOM gm k p
 customSOM gm f = 
   SOM {
         sGridMap=gm,
@@ -158,11 +203,11 @@
   where d' = fromIntegral d
 
 -- | Configures a typical learning function for classifiers.
---   @'decayingGaussian r w0 tMax' returns a bell curve-shaped function.
---   At time zero, the maximum learning rate (applied to the BMU) is 
---   @r@, and the neighbourhood width is @w@. Over time the bell curve
---   shrinks and the learning rate tapers off, until at time @tMax@,
---   the learning rate is negligible.
+--   @'decayingGaussian' r w0 tMax@ returns a bell curve-shaped
+--   function. At time zero, the maximum learning rate (applied to the 
+--   BMU) is @r@, and the neighbourhood width is @w@. Over time the bell
+--   curve shrinks and the learning rate tapers off, until at time
+--   @tMax@, the learning rate is negligible.
 decayingGaussian
   ∷ Floating a ⇒ a → a → Int → (Int → Int → a)
 decayingGaussian r w0 tMax = 
