diff --git a/som.cabal b/som.cabal
--- a/som.cabal
+++ b/som.cabal
@@ -1,5 +1,5 @@
 name:           som
-version:        2.0
+version:        3.0
 synopsis:       Self-Organising Maps
 description:    A Kohonen Self-organising Map (SOM) maps input patterns 
                 onto a regular grid (usually two-dimensional) where each
@@ -11,6 +11,13 @@
                 .
                 In layman's terms, a SOM can be useful when you you want
                 to discover the underlying structure of some data.
+                .
+                The userguide is available at 
+                <https://github.com/mhwombat/som/wiki>.
+                .
+                NOTE: Version 3.0 changed the order of parameters
+                for many functions. This makes it easier for the user
+                to write mapping and folding operations.
 category:       Math
 
 cabal-version:  >=1.8
@@ -28,7 +35,7 @@
                    base-unicode-symbols ==0.2.*,
                    binary == 0.5.*,
                    containers ==0.4.2.* || ==0.5.*,
-                   grid ==3.*,
+                   grid ==4.*,
                    MonadRandom ==0.1.*
   ghc-options:     -Wall
   exposed-modules: Data.Datamining.Clustering.SOM,
@@ -41,7 +48,7 @@
                    QuickCheck == 2.5.*,
                    test-framework == 0.8.*,
                    som,
-                   grid ==3.*,
+                   grid ==4.*,
                    base-unicode-symbols ==0.2.*,
                    MonadRandom ==0.1.*,
                    random ==1.0.*
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
@@ -1,7 +1,7 @@
 ------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Datamining.Clustering.SOM
--- Copyright   :  (c) Amy de Buitléir 2012
+-- Copyright   :  (c) Amy de Buitléir 2012-2013
 -- License     :  BSD-style
 -- Maintainer  :  amy@nualeargais.ie
 -- Stability   :  experimental
@@ -36,8 +36,7 @@
     trainBatch,
     classify,
     classifyAndTrain,
-    diffs,
-    differences, -- TO BE REMOVED
+    diff,
     diffAndTrain,
     -- * Numeric vectors as patterns
     -- ** Normalised vectors
@@ -54,7 +53,7 @@
   ) where
 
 import Data.Datamining.Clustering.SOMInternal (adjustVector, classify, 
-  classifyAndTrain, diffs, differences, diffAndTrain, 
+  classifyAndTrain, diff, diffAndTrain, 
   euclideanDistanceSquared, normalise, NormalisedVector, scale,
   ScaledVector, train, trainBatch, Pattern(..))
 
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
@@ -1,7 +1,7 @@
 ------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Datamining.Clustering.SOMInternal
--- Copyright   :  (c) Amy de Buitléir 2012
+-- Copyright   :  (c) Amy de Buitléir 2012-2013
 -- License     :  BSD-style
 -- Maintainer  :  amy@nualeargais.ie
 -- Stability   :  experimental
@@ -10,9 +10,12 @@
 -- A module containing private @SOM@ internals. Most developers should
 -- use @SOM@ instead. This module is subject to change without notice.
 --
+-- NOTE: Version 3.0 changed the order of parameters for many functions.
+-- This makes it easier for the user to write mapping and folding
+-- operations.
+--
 ------------------------------------------------------------------------
-{-# LANGUAGE UnicodeSyntax, MultiParamTypeClasses, FlexibleInstances, 
-    FunctionalDependencies #-}
+{-# LANGUAGE UnicodeSyntax, TypeFamilies, FlexibleContexts #-}
 
 module Data.Datamining.Clustering.SOMInternal
   (
@@ -20,8 +23,7 @@
     adjustVector,
     classify,
     classifyAndTrain,
-    differences, -- TO BE REMOVED
-    diffs,
+    diff,
     diffAndTrain,
     euclideanDistanceSquared,
     magnitudeSquared,
@@ -38,16 +40,18 @@
 import Data.Eq.Unicode ((≡))
 import Data.List (foldl', minimumBy)
 import Data.Ord (comparing)
-import Math.Geometry.Grid (distance, Grid)
-import Math.Geometry.GridMap (GridMap, mapWithKey, toList)
+import Math.Geometry.GridMap (GridMap, Index, BaseGrid, distance, 
+  mapWithKey, toList)
+import Math.Geometry.Grid (Grid)
 import qualified Math.Geometry.GridMap as GM (map)
 
 -- | A pattern to be learned or classified by a self-organising map.
-class Pattern p v | p → v where
+class Pattern p where
+  type Metric p
   -- | Compares two patterns and returns a /non-negative/ number 
   --   representing how different the patterns are. A result of @0@ 
   --   indicates that the patterns are identical.
-  difference ∷ p → p → v
+  difference ∷ p → p → Metric p
   -- | @'makeSimilar' target amount pattern@ returns a modified copy of
   --   @pattern@ that is more similar to @target@ than @pattern@ is. The 
   --   magnitude of the adjustment is controlled by the @amount@ 
@@ -55,79 +59,107 @@
   --   values for @amount@ permit greater adjustments. If @amount@=1,
   --   the result should be identical to the @target@. If @amount@=0,
   --   the result should be the unmodified @pattern@.
-  makeSimilar ∷ p → v → p → p
+  makeSimilar ∷ p → Metric p → p → p
 
+-- | @'diff' c pattern@ returns the positions of all nodes in 
+--   @c@, paired with the difference between @pattern@ and the node's 
+--   pattern.
+diff 
+  ∷ (GridMap gm p, Pattern p, GridMap gm m,
+    Metric p ~ m, BaseGrid gm p ~ BaseGrid gm m) ⇒ 
+    gm p → p → gm m
+diff c pattern = GM.map (pattern `difference`) c
+
 -- | @classify c pattern@ returns the position of the node in @c@ 
 --   whose pattern best matches the input @pattern@.
-classify ∷ (Ord v, Pattern p v) ⇒ GridMap g k p → p → k
+classify
+  ∷ (GridMap gm p, Pattern p, GridMap gm m,
+      Metric p ~ m, Ord m, k ~ Index (BaseGrid gm p), 
+      BaseGrid gm m ~ BaseGrid gm p) ⇒ 
+    gm p → p → k
 classify c pattern = 
-  fst $ minimumBy (comparing snd) $ toList $ differences pattern c
-
--- | @pattern \`'differences'\` c@ returns the positions of all nodes in 
---   @c@, paired with the difference between @pattern@ and the node's 
---   pattern. This function has been replaced with @'diffs'@, which
---   swaps the parameter order to be consistent with @'classify'@.
-{-# DEPRECATED differences "Use diffs instead" #-}
-differences ∷ Pattern p v ⇒ p → GridMap g k p → GridMap g k v
-differences pattern = GM.map (pattern `difference`)
-
--- | @'diffs' c pattern@ returns the positions of all nodes in 
---   @c@, paired with the difference between @pattern@ and the node's 
---   pattern.
-diffs ∷ Pattern p v ⇒ GridMap g k p → p → GridMap g k v
-diffs c pattern = GM.map (pattern `difference`) c
+  fst $ minimumBy (comparing snd) $ toList $ diff c pattern
 
 -- | If @f d@ is a function that returns the learning rate to apply to a
 --   node based on its distance @d@from the node that best matches the
---   input pattern, then @'train' f c pattern@ returns a modified copy
+--   input pattern, then @'train' c f pattern@ returns a modified copy
 --   of the classifier @c@ that has partially learned the @target@.
-train ∷ (Ord v, Pattern p v, Grid g s k) ⇒
-  (Int → v) → GridMap g k p → p → GridMap g k p
-train f c pattern = snd $ classifyAndTrain f c pattern
+train
+  ∷ (Ord m, GridMap gm p, GridMap gm m,
+      GridMap gm (Int, p), GridMap gm (m, p), Grid (gm p),
+      Pattern p, Metric p ~ m, Index (BaseGrid gm p) ~ Index (gm p),
+      BaseGrid gm m ~ BaseGrid gm p) ⇒
+    gm p → (Int → m) → p → gm p
+train c f pattern = snd $ classifyAndTrain c f pattern
 
 -- | Same as @train@, but applied to multiple patterns.
-trainBatch ∷ (Ord v, Grid g s k, Pattern p v) ⇒
-  (Int → v) → GridMap g k p → [p] → GridMap g k p
-trainBatch f = foldl' (train f)
+trainBatch
+  ∷ (Ord m, GridMap gm p, GridMap gm m,
+      GridMap gm (Int, p), GridMap gm (m, p), Grid (gm p),
+      Pattern p, Metric p ~ m, Index (BaseGrid gm p) ~ Index (gm p),
+      BaseGrid gm m ~ BaseGrid gm p) ⇒
+    gm p → (Int → m) → [p] → gm p
+trainBatch c f ps = foldl' (\som → train som f) c ps
 
 -- | If @f@ is a function that returns the learning rate to apply to a
 --   node based on its distance from the node that best matches the 
---   @target@, then @'classifyAndTrain' f c target@ returns a tuple 
+--   @target@, then @'classifyAndTrain' c f target@ returns a tuple 
 --   containing the position of the node in @c@ whose pattern best 
 --   matches the input @target@, and a modified copy of the classifier 
 --   @c@ that has partially learned the @target@.
---   Invoking @classifyAndTrain f c p@ may be faster than invoking
---   @(p `classify` c, train f c p)@, but they should give identical
+--   Invoking @classifyAndTrain c f p@ may be faster than invoking
+--   @(p `classify` c, train c f p)@, but they should give identical
 --   results.
-classifyAndTrain ∷ (Eq k, Ord v, Pattern p v, Grid g s k) ⇒ 
-  (Int → v) → GridMap g k p → p → (k, GridMap g k p)
-classifyAndTrain f c pattern = (bmu, c')
-  where bmu = classify c pattern
-        dMap = mapWithKey (\k p → (distance c k bmu, p)) c
-        lrMap = GM.map (\(d,p) → (f d, p)) dMap
-        c' = GM.map (adjustNode pattern) lrMap
+classifyAndTrain
+  ∷ (Ord m, GridMap gm p, GridMap gm m,
+      GridMap gm (Int, p), GridMap gm (m, p), Grid (gm p),
+      Pattern p, Metric p ~ m, Index (BaseGrid gm p) ~ Index (gm p),
+      BaseGrid gm m ~ BaseGrid gm p) ⇒
+     gm p → (Int → m) → p → (Index (gm p), gm p)
+classifyAndTrain c f pattern = (bmu, c')
+  where (bmu, _, c') = reportAndTrain c f pattern
 
 -- | If @f@ is a function that returns the learning rate to apply to a
 --   node based on its distance from the node that best matches the 
---   @target@, then @'diffAndTrain' f c target@ returns a tuple 
+--   @target@, then @'diffAndTrain' c f target@ returns a tuple 
 --   containing:
 --   1. The positions of all nodes in @c@, paired with the difference
 --      between @pattern@ and the node's pattern
 --   2. A modified copy of the classifier @c@ that has partially
 --      learned the @target@.
---   Invoking @diffAndTrain f c p@ may be faster than invoking
---   @(p `differences` c, train f c p)@, but they should give identical
+--   Invoking @diffAndTrain c f p@ may be faster than invoking
+--   @(p `diff` c, train c f p)@, but they should give identical
 --   results.
-diffAndTrain ∷ (Eq k, Ord v, Pattern p v, Grid g s k) ⇒ 
-  (Int → v) → GridMap g k p → p → (GridMap g k v, GridMap g k p)
-diffAndTrain f c pattern = (ds, c')
-  where ds = pattern `differences` c
+diffAndTrain
+  ∷ (Ord m, GridMap gm p, GridMap gm m,
+      GridMap gm (Int, p), GridMap gm (m, p), Grid (gm p),
+      Pattern p, Metric p ~ m, Index (BaseGrid gm p) ~ Index (gm p),
+      BaseGrid gm m ~ BaseGrid gm p) ⇒
+     gm p → (Int → m) → p → (gm m, gm p)
+diffAndTrain c f pattern = (ds, c')
+  where (_, ds, c') = reportAndTrain c f pattern
+
+reportAndTrain
+  ∷ (Ord m, GridMap gm p, GridMap gm m,
+      GridMap gm (Int, p), GridMap gm (m, p), Grid (gm p),
+      Pattern p, Metric p ~ m, Index (BaseGrid gm p) ~ Index (gm p),
+      BaseGrid gm m ~ BaseGrid gm p) ⇒
+     gm p → (Int → m) → p → (Index (gm p), gm m, gm p)
+reportAndTrain c f pattern = (bmu, ds, c')
+  where ds = c `diff` pattern
         bmu = fst $ minimumBy (comparing snd) $ toList ds
-        dMap = mapWithKey (\k p → (distance c k bmu, p)) c
+        c' = trainWithBMU c f bmu pattern
+
+trainWithBMU
+  ∷ (GridMap gm p, GridMap gm (Int, p), GridMap gm (m, p),
+      Grid (gm p), Pattern p, Metric p ~ m, k ~ Index (BaseGrid gm p), 
+      k ~ Index (gm p)) ⇒
+    gm p → (Int → m) → k → p → gm p
+trainWithBMU c f bmu pattern = GM.map (adjustNode pattern) lrMap
+  where dMap = mapWithKey (\k p → (distance c k bmu, p)) c
         lrMap = GM.map (\(d,p) → (f d, p)) dMap
-        c' = GM.map (adjustNode pattern) lrMap
 
-adjustNode ∷ (Pattern p v) ⇒ p → (v,p) → p
+adjustNode ∷ (Pattern p) ⇒ p → (Metric p, p) → p
 adjustNode target (r,p) = makeSimilar target r p
 
 --
@@ -171,7 +203,8 @@
   where f x = x*x
 
 instance (Floating a, Fractional a, Ord a, Eq a) ⇒ 
-    Pattern (NormalisedVector a) a where
+    Pattern (NormalisedVector a) where
+  type Metric (NormalisedVector a) = a
   difference (NormalisedVector xs) (NormalisedVector ys) = 
     euclideanDistanceSquared xs ys
   makeSimilar (NormalisedVector xs) r (NormalisedVector ys) = 
@@ -210,7 +243,8 @@
 quantify' = zipWith f
   where f (minX, maxX) x = (min minX x, max maxX x)
 
-instance (Fractional a, Ord a, Eq a) ⇒ Pattern (ScaledVector a) a where
+instance (Fractional a, Ord a, Eq a) ⇒ Pattern (ScaledVector a) where
+  type Metric (ScaledVector a) = a
   difference (ScaledVector xs) (ScaledVector ys) = 
     euclideanDistanceSquared xs ys
   makeSimilar (ScaledVector xs) r (ScaledVector ys) =
