diff --git a/som.cabal b/som.cabal
--- a/som.cabal
+++ b/som.cabal
@@ -1,5 +1,5 @@
 name:           som
-version:        7.0.1
+version:        7.2.0
 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/DSOMInternal.hs b/src/Data/Datamining/Clustering/DSOMInternal.hs
--- a/src/Data/Datamining/Clustering/DSOMInternal.hs
+++ b/src/Data/Datamining/Clustering/DSOMInternal.hs
@@ -114,8 +114,8 @@
       G.Index (GM.BaseGrid gm p) ~ G.Index (gm p)) =>
      DSOM gm t p -> p -> DSOM gm (G.Index (gm p)) p
 justTrain s p = trainNeighbourhood s bmu p
-  where ds = GM.toList . GM.map (p `difference`) . sGridMap $ s
-        bmu = fst . minimumBy (comparing snd) $ ds
+  where ds = GM.toList . GM.map (p `difference`) $ sGridMap s
+        bmu = fst $ minimumBy (comparing snd) ds
 
 instance
   (GM.GridMap gm p, k ~ G.Index (GM.BaseGrid gm p), Pattern p,
@@ -126,11 +126,11 @@
   toList = GM.toList . sGridMap
   numModels = G.tileCount . sGridMap
   models = GM.elems . sGridMap
-  differences s p = GM.toList . GM.map (p `difference`) . sGridMap $ s
+  differences s p = GM.toList . GM.map (p `difference`) $ sGridMap s
   trainBatch s = foldl' justTrain s
   reportAndTrain s p = (bmu, ds, s')
     where ds = differences s p
-          bmu = fst . minimumBy (comparing snd) $ ds
+          bmu = fst $ minimumBy (comparing snd) ds
           s' = trainNeighbourhood s bmu p
 
 
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
@@ -44,13 +44,9 @@
     -- * Deconstruction
     toGridMap,
     -- * Advanced control
-    trainNeighbourhood,
-    incrementCounter,
-    counter,
-    setCounter
+    trainNeighbourhood
   ) where
 
 import Data.Datamining.Clustering.SOMInternal (SOM(..),
-  DecayingGaussian(..), toGridMap, trainNeighbourhood, incrementCounter,
-  counter, setCounter)
+  DecayingGaussian(..), toGridMap, trainNeighbourhood)
 
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
@@ -26,9 +26,20 @@
 import GHC.Generics (Generic)
 import Prelude hiding (lookup)
 
+-- | A function used to adjust the models in a classifier.
 class LearningFunction f where
   type LearningRate f
-  rate :: f -> Int -> Int -> (LearningRate f)
+  -- | @'rate' f t d@ returns the learning rate for a node.
+  --   The parameter @f@ is the learning function.
+  --   The parameter @t@ indicates how many patterns (or pattern
+  --   batches) have previously been presented to the classifier.
+  --   Typically this is used to make the learning rate decay over time.
+  --   The parameter @d@ 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.
+  rate :: f -> LearningRate f -> LearningRate f -> LearningRate f
 
 -- | A typical learning function for classifiers.
 --   @'DecayingGaussian' r0 rf w0 wf tf@ returns a bell curve-shaped
@@ -47,25 +58,23 @@
 --
 --   where << means "is much smaller than" (not the Haskell @<<@
 --   operator!)
-data DecayingGaussian a = DecayingGaussian a a a a Int
+data DecayingGaussian a = DecayingGaussian a a a a a
   deriving (Eq, Show, Generic)
 
-instance (Floating a, Fractional a, Num a) =>
-         LearningFunction (DecayingGaussian a) where
+instance (Floating a, Fractional a, Num a)
+    => LearningFunction (DecayingGaussian a) where
   type LearningRate (DecayingGaussian a) = a
-  rate (DecayingGaussian r0 rf w0 wf tf) t d = r * exp (-(d'*d')/(w*w))
-    where a = t'/tf'
+  rate (DecayingGaussian r0 rf w0 wf tf) t d = r * exp (-(d*d)/(w*w))
+    where a = t/tf
           r = r0 * ((rf/r0)**a)
           w = w0 * ((wf/w0)**a)
-          t' = fromIntegral t
-          tf' = fromIntegral tf
-          d' = fromIntegral d
 
 -- | A learning function that only updates the BMU and has a constant
 --   learning rate.
 data StepFunction a = StepFunction a deriving (Eq, Show, Generic)
 
-instance (Fractional a) => LearningFunction (StepFunction a) where
+instance (Fractional a, Eq a)
+  => LearningFunction (StepFunction a) where
   type LearningRate (StepFunction a) = a
   rate (StepFunction r) _ d = if d == 0 then r else 0.0
 
@@ -90,52 +99,63 @@
 --      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 f gm k p = SOM
+data SOM f t gm k p = SOM
   {
-    sGridMap :: gm p,
-    sLearningFunction :: f,
-    sCounter :: Int
+    -- | Maps patterns to tiles in a regular grid.
+    --   In the context of a SOM, the tiles are called "nodes"
+    gridMap :: gm p,
+    -- | The function used to update the nodes.
+    learningFunction :: f,
+    -- | A counter used as a "time" parameter.
+    --   If you create the SOM with a counter value @0@, and don't
+    --   directly modify it, then the counter will represent the number
+    --   of patterns that this SOM has classified.
+    counter :: t
   } deriving (Eq, Show, Generic)
 
-instance (F.Foldable gm) => F.Foldable (SOM f gm k) where
-  foldr f x g = F.foldr f x (sGridMap g)
+instance (F.Foldable gm) => F.Foldable (SOM f t gm k) where
+  foldr f x g = F.foldr f x (gridMap g)
 
-instance (G.Grid (gm p)) => G.Grid (SOM f gm k p) where
-  type Index (SOM f gm k p) = G.Index (gm p)
-  type Direction (SOM f gm k p) = G.Direction (gm p)
-  indices = G.indices . sGridMap
-  distance = G.distance . sGridMap
-  neighbours = G.neighbours . sGridMap
-  contains = G.contains . sGridMap
-  viewpoint = G.viewpoint . sGridMap
-  directionTo = G.directionTo . sGridMap
-  tileCount = G.tileCount . sGridMap
-  null = G.null . sGridMap
-  nonNull = G.nonNull . sGridMap
+instance (G.Grid (gm p)) => G.Grid (SOM f t gm k p) where
+  type Index (SOM f t gm k p) = G.Index (gm p)
+  type Direction (SOM f t gm k p) = G.Direction (gm p)
+  indices = G.indices . gridMap
+  distance = G.distance . gridMap
+  neighbours = G.neighbours . gridMap
+  contains = G.contains . gridMap
+  viewpoint = G.viewpoint . gridMap
+  directionTo = G.directionTo . gridMap
+  tileCount = G.tileCount . gridMap
+  null = G.null . gridMap
+  nonNull = G.nonNull . gridMap
 
-instance (F.Foldable gm, GM.GridMap gm p, G.Grid (GM.BaseGrid gm p)) => GM.GridMap (SOM f gm k) p where
-  type BaseGrid (SOM f gm k) p = GM.BaseGrid gm p
-  toGrid = GM.toGrid . sGridMap
-  toMap = GM.toMap . sGridMap
+instance (F.Foldable gm, GM.GridMap gm p, G.Grid (GM.BaseGrid gm p))
+    => GM.GridMap (SOM f t gm k) p where
+  type BaseGrid (SOM f t gm k) p = GM.BaseGrid gm p
+  toGrid = GM.toGrid . gridMap
+  toMap = GM.toMap . gridMap
   mapWithKey = error "Not implemented"
-  adjustWithKey f k s = s { sGridMap=gm' }
-    where gm = sGridMap s
+  adjustWithKey f k s = s { gridMap=gm' }
+    where gm = gridMap s
           gm' = GM.adjustWithKey f k gm
 
 currentLearningFunction
-  :: (LearningFunction f, LearningRate f ~ Metric p)
-    => SOM f gm k p -> (Int -> Metric p)
-currentLearningFunction s = (rate . sLearningFunction $ s) (sCounter s)
+  :: (LearningFunction f, Metric p ~ LearningRate f,
+    Num (LearningRate f), Integral t)
+      => SOM f t gm k p -> (LearningRate f -> Metric p)
+currentLearningFunction s
+  = rate (learningFunction s) (fromIntegral $ counter s)
 
 -- | Extracts the grid and current models from the SOM.
-toGridMap :: GM.GridMap gm p => SOM f gm k p -> gm p
-toGridMap = sGridMap
+--   A synonym for @'gridMap'@.
+toGridMap :: GM.GridMap gm p => SOM f t gm k p -> gm p
+toGridMap = gridMap
 
 adjustNode
-  :: (Pattern p, G.Grid g, k ~ G.Index g) =>
-     g -> (Int -> Metric p) -> p -> k -> k -> p -> p
+  :: (Pattern p, G.Grid g, k ~ G.Index g, Num t) =>
+     g -> (t -> Metric p) -> p -> k -> k -> p -> p
 adjustNode g f target bmu k = makeSimilar target (f d)
-  where d = G.distance g bmu k
+  where d = fromIntegral $ G.distance g bmu k
 
 -- | Trains the specified node and the neighbourood around it to better
 --   match a target.
@@ -144,100 +164,40 @@
 trainNeighbourhood
   :: (Pattern p, G.Grid (gm p), GM.GridMap gm p,
       G.Index (GM.BaseGrid gm p) ~ G.Index (gm p), LearningFunction f,
-      LearningRate f ~ Metric p) =>
-     SOM f gm k p -> G.Index (gm p) -> p -> SOM f gm k p
-trainNeighbourhood s bmu target = s { sGridMap=gm' }
-  where gm = sGridMap s
+      Metric p ~ LearningRate f, Num (LearningRate f), Integral t) =>
+     SOM f t gm k p -> G.Index (gm p) -> p -> SOM f t gm k p
+trainNeighbourhood s bmu target = s { gridMap=gm' }
+  where gm = gridMap s
         gm' = GM.mapWithKey (adjustNode gm f target bmu) gm
         f = currentLearningFunction s
 
-incrementCounter :: SOM f gm k p -> SOM f gm k p
-incrementCounter s = setCounter (sCounter s + 1) s
-
-counter :: SOM f gm k p -> Int
-counter = sCounter
-
-setCounter :: Int -> SOM f gm k p -> SOM f gm k p
-setCounter k s = s { sCounter = k }
+incrementCounter :: Num t => SOM f t gm k p -> SOM f t gm k p
+incrementCounter s = s { counter=counter 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), LearningFunction f,
-      LearningRate f ~ Metric p) =>
-     SOM f gm k p -> p -> SOM f gm k p
+      Metric p ~ LearningRate f, Num (LearningRate f), Integral t) =>
+     SOM f t gm k p -> p -> SOM f t gm k p
 justTrain s p = trainNeighbourhood s bmu p
-  where ds = GM.toList . GM.map (p `difference`) . sGridMap $ s
-        bmu = fst . minimumBy (comparing snd) $ ds
+  where ds = GM.toList . GM.map (p `difference`) $ gridMap s
+        bmu = fst $ minimumBy (comparing snd) ds
 
 instance
   (GM.GridMap gm p, k ~ G.Index (GM.BaseGrid gm p), Pattern p,
   G.Grid (gm p), GM.GridMap gm (Metric p), k ~ G.Index (gm p),
   k ~ G.Index (GM.BaseGrid gm (Metric p)), Ord (Metric p),
-  LearningFunction f, LearningRate f ~ Metric p) =>
-    Classifier (SOM f gm) k p where
-  toList = GM.toList . sGridMap
-  numModels = G.tileCount . sGridMap
-  models = GM.elems . sGridMap
-  differences s p = GM.toList . GM.map (p `difference`) . sGridMap $ s
+  LearningFunction f, Metric p ~ LearningRate f, Num (LearningRate f),
+  Integral t)
+    => Classifier (SOM f t gm) k p where
+  toList = GM.toList . gridMap
+  numModels = G.tileCount . gridMap
+  models = GM.elems . gridMap
+  differences s p = GM.toList . GM.map (p `difference`) $ gridMap 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
+          bmu = fst $ minimumBy (comparing snd) ds
           s' = trainNeighbourhood s bmu p
-
-
--- -- | Creates a classifier with a default (bell-shaped) learning
--- --   function. Usage is @'defaultSOM' gm r0 rf w0 wf tf@, 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.
--- --
--- --   [@r0@] See description in @'decayingGaussian2'@.
--- --
--- --   [@rf@] See description in @'decayingGaussian2'@.
--- --
--- --   [@w0@] See description in @'decayingGaussian2'@.
--- --
--- --   [@wf@] See description in @'decayingGaussian2'@.
--- --
--- --   [@tf@] See description in @'decayingGaussian2'@.
--- defaultSOM
---   :: Floating (Metric p) => gm p -> Metric p -> Metric p -> Metric p ->
---      Metric p -> Int -> SOM f gm k p
--- defaultSOM gm r0 rf w0 wf tf =
---   SOM {
---         sGridMap=gm,
---         sLearningFunction=DecayingGaussian r0 rf w0 wf tf,
---         sCounter=0
---       }
-
--- -- | 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
---   :: (LearningFunction f, LearningRate f ~ Metric p)
---     => gm p -> f -> SOM f gm k p
--- customSOM gm f =
---   SOM {
---         sGridMap=gm,
---         sLearningFunction=f,
---         sCounter=0
---       }
-
