diff --git a/prometheus.cabal b/prometheus.cabal
--- a/prometheus.cabal
+++ b/prometheus.cabal
@@ -1,5 +1,5 @@
 name:                prometheus
-version:             0.1.0.0
+version:             0.1.0.1
 synopsis:            Prometheus Haskell Client
 description:         Idiomatic Haskell client for Prometheus.io monitoring.
 homepage:            http://github.com/LukeHoersten/prometheus#readme
@@ -16,22 +16,25 @@
   hs-source-dirs:      src
   default-language:    Haskell2010
 
-  exposed-modules: System.Metrics.Prometheus.Counter
-                 , System.Metrics.Prometheus.Encode
-                 , System.Metrics.Prometheus.Gauge
-                 , System.Metrics.Prometheus.Histogram
+  exposed-modules: System.Metrics.Prometheus.Encode
+                 , System.Metrics.Prometheus.Encode.Histogram
+                 , System.Metrics.Prometheus.Encode.MetricId
                  , System.Metrics.Prometheus.Metric
+                 , System.Metrics.Prometheus.Metric.Counter
+                 , System.Metrics.Prometheus.Metric.Gauge
+                 , System.Metrics.Prometheus.Metric.Histogram
+                 , System.Metrics.Prometheus.Metric.Summary
                  , System.Metrics.Prometheus.MetricId
                  , System.Metrics.Prometheus.Registry
-                 , System.Metrics.Prometheus.Sample
-                 , System.Metrics.Prometheus.Summary
+                 , System.Metrics.Prometheus.RegistryT
 
   build-depends: atomic-primops >= 0.8  && < 0.9
                , base           >= 4.7  && < 5.0
                , bytestring     >= 0.10 && < 0.11
                , containers     >= 0.5  && < 0.6
+               , mtl            >= 2.2  && < 2.3
                , text           >= 1.2  && < 1.3
-
+               , transformers   >= 0.4  && < 0.5
 
 source-repository head
   type:     git
diff --git a/src/System/Metrics/Prometheus/Counter.hs b/src/System/Metrics/Prometheus/Counter.hs
deleted file mode 100644
--- a/src/System/Metrics/Prometheus/Counter.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-module System.Metrics.Prometheus.Counter where
-
-
-import           Data.Atomics.Counter (AtomicCounter, incrCounter_, newCounter,
-                                       readCounter)
-
-
-newtype Counter = Counter { unCounter :: AtomicCounter }
-
-
-new :: IO Counter
-new = Counter <$> newCounter 0
-
-
-add :: Int -> Counter -> IO ()
-add by = incrCounter_ by . unCounter
-
-
-inc :: Counter -> IO ()
-inc = add 1
-
-
-view :: Counter -> IO Int
-view = readCounter . unCounter
diff --git a/src/System/Metrics/Prometheus/Encode.hs b/src/System/Metrics/Prometheus/Encode.hs
--- a/src/System/Metrics/Prometheus/Encode.hs
+++ b/src/System/Metrics/Prometheus/Encode.hs
@@ -5,22 +5,24 @@
        , serializeMetrics
        ) where
 
-import           Data.ByteString.Builder            (Builder, byteString, char8,
-                                                     doubleDec, intDec,
-                                                     toLazyByteString)
-import           Data.ByteString.Lazy               (ByteString)
-import           Data.List                          (intersperse)
-import qualified Data.Map                           as Map
-import           Data.Monoid                        ((<>))
-import           Data.Text                          (Text, replace)
-import           Data.Text.Encoding                 (encodeUtf8)
+import           Data.ByteString.Builder                    (Builder, doubleDec,
+                                                             intDec,
+                                                             toLazyByteString)
+import           Data.ByteString.Lazy                       (ByteString)
+import           Data.List                                  (intersperse)
+import qualified Data.Map                                   as Map
+import           Data.Monoid                                ((<>))
 
-import           System.Metrics.Prometheus.MetricId (MetricId (..))
-import           System.Metrics.Prometheus.Sample   (CounterSample (..),
-                                                     GaugeSample (..),
-                                                     MetricSample (..),
-                                                     RegistrySample (..),
-                                                     metricSample)
+import           System.Metrics.Prometheus.Encode.Histogram (encodeHistogram)
+import           System.Metrics.Prometheus.Encode.MetricId  (encodeHeader,
+                                                             encodeMetricId,
+                                                             newline, space)
+import           System.Metrics.Prometheus.Metric           (MetricSample (..),
+                                                             metricSample)
+import           System.Metrics.Prometheus.Metric.Counter   (CounterSample (..))
+import           System.Metrics.Prometheus.Metric.Gauge     (GaugeSample (..))
+import           System.Metrics.Prometheus.MetricId         (MetricId (..))
+import           System.Metrics.Prometheus.Registry         (RegistrySample (..))
 
 
 serializeMetrics :: RegistrySample -> ByteString
@@ -35,21 +37,9 @@
 encodeMetric :: MetricId -> MetricSample -> Builder
 encodeMetric mid sample
     =  encodeHeader mid sample <> newline
-    <> metricSample (encodeCounter mid) (encodeGauge mid) encodeHistogram encodeSummary sample
-
-
-encodeHeader :: MetricId -> MetricSample -> Builder
-encodeHeader mid sample
-    =  "# HELP " <> encodeName mid <> space <> "help" <> newline
-    <> "# TYPE " <> encodeName mid <> space <> encodeSampleType sample
-
-
--- escape :: Text -> Text
--- escape = replace "\\" "\\\" . replace "\"" ("\\" <> "\"")
-
-
-encodeHistogram = undefined
-encodeSummary = undefined
+    <> metricSample (encodeCounter mid) (encodeGauge mid) (encodeHistogram mid) (encodeSummary mid) sample
+  where
+    encodeSummary = undefined
 
 
 encodeCounter :: MetricId -> CounterSample -> Builder
@@ -58,56 +48,3 @@
 
 encodeGauge :: MetricId -> GaugeSample -> Builder
 encodeGauge mid gauge = encodeMetricId mid <> space <> doubleDec (unGaugeSample gauge)
-
-
-encodeSampleType :: MetricSample -> Builder
-encodeSampleType = byteString . metricSample (const "counter")
-    (const "gauge") (const "histogram") (const "summary")
-
-
-encodeMetricId :: MetricId -> Builder
-encodeMetricId mid = encodeName mid <> encodeLabels mid
-
-
-encodeName :: MetricId -> Builder
-encodeName = text . name
-
-
-encodeLabels :: MetricId -> Builder
-encodeLabels mid | Map.null (labels mid) = space
-                 | otherwise =
-                   openBracket
-                   <> (mconcat . intersperse comma . map encodeLabel . Map.toList $ labels mid)
-                   <> closeBracket
-
-
-encodeLabel :: (Text, Text) -> Builder
-encodeLabel (key, value) = text key <> equals <> text value
-
-
-text :: Text -> Builder
-text = byteString . encodeUtf8
-
-
-space :: Builder
-space = char8 ' '
-
-
-newline :: Builder
-newline = char8 '\n'
-
-
-openBracket :: Builder
-openBracket = char8 '{'
-
-
-closeBracket :: Builder
-closeBracket = char8 '}'
-
-
-comma :: Builder
-comma = char8 ','
-
-
-equals :: Builder
-equals = char8 '='
diff --git a/src/System/Metrics/Prometheus/Encode/Histogram.hs b/src/System/Metrics/Prometheus/Encode/Histogram.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Metrics/Prometheus/Encode/Histogram.hs
@@ -0,0 +1,40 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module System.Metrics.Prometheus.Encode.Histogram where
+
+
+import           Data.ByteString.Builder                    (Builder, doubleDec,
+                                                             intDec)
+import qualified Data.Map                                   as Map
+import           Data.Monoid                                ((<>))
+import           Data.Text.Lazy                             (toStrict)
+import           Data.Text.Lazy.Builder                     (toLazyText)
+import           Data.Text.Lazy.Builder.RealFloat           (FPFormat (Generic),
+                                                             formatRealFloat)
+
+import           System.Metrics.Prometheus.Encode.MetricId  (encodeLabels,
+                                                             encodeName, space)
+import           System.Metrics.Prometheus.Metric.Histogram (Count, HistogramSample (..),
+                                                             UpperBound)
+import           System.Metrics.Prometheus.MetricId         (MetricId (..),
+                                                             addLabel)
+
+
+encodeHistogram :: MetricId -> HistogramSample -> Builder
+encodeHistogram mid histogram
+    =  encodeHistogramBuckets mid histogram
+    <> encodeName (name mid) <> "_sum" <> space <> doubleDec (histSum histogram)
+    <> encodeName (name mid) <> "_count" <> space <> intDec (histCount histogram)
+
+
+encodeHistogramBuckets :: MetricId -> HistogramSample -> Builder
+encodeHistogramBuckets mid = mconcat . map snd . Map.toList
+    . Map.mapWithKey (encodeHistogramBucket mid) . histBuckets
+
+
+encodeHistogramBucket :: MetricId -> UpperBound -> Count -> Builder
+encodeHistogramBucket mid upperBound count
+    =  encodeName (name mid) <> "_bucket" <> encodeLabels labels' <> space <> intDec count
+  where
+    upperBoundValue = toStrict . toLazyText $ formatRealFloat Generic Nothing upperBound
+    labels' = addLabel "le" upperBoundValue $ labels mid
diff --git a/src/System/Metrics/Prometheus/Encode/MetricId.hs b/src/System/Metrics/Prometheus/Encode/MetricId.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Metrics/Prometheus/Encode/MetricId.hs
@@ -0,0 +1,77 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module System.Metrics.Prometheus.Encode.MetricId where
+
+import           Data.ByteString.Builder            (Builder, byteString, char8)
+import           Data.List                          (intersperse)
+import qualified Data.Map                           as Map
+import           Data.Monoid                        ((<>))
+import           Data.Text                          (Text)
+import           Data.Text.Encoding                 (encodeUtf8)
+
+import           System.Metrics.Prometheus.Metric   (MetricSample (..),
+                                                     metricSample)
+import           System.Metrics.Prometheus.MetricId (Labels (..), MetricId (..),
+                                                     Name (..))
+
+
+encodeHeader :: MetricId -> MetricSample -> Builder
+encodeHeader mid sample
+    =  "# HELP " <> nm <> space <> "help" <> newline
+    <> "# TYPE " <> nm <> space <> encodeSampleType sample
+  where nm = encodeName (name mid)
+
+
+encodeSampleType :: MetricSample -> Builder
+encodeSampleType = byteString . metricSample (const "counter")
+    (const "gauge") (const "histogram") (const "summary")
+
+
+encodeMetricId :: MetricId -> Builder
+encodeMetricId mid = encodeName (name mid) <> encodeLabels (labels mid)
+
+
+encodeName :: Name -> Builder
+encodeName = text . unName
+
+
+encodeLabels :: Labels -> Builder
+encodeLabels ls
+    | Map.null ls' = space
+    | otherwise =
+             openBracket
+          <> (mconcat . intersperse comma . map encodeLabel $ Map.toList ls')
+          <> closeBracket
+  where ls' = unLabels ls
+
+
+encodeLabel :: (Text, Text) -> Builder
+encodeLabel (key, value) = text key <> equals <> text value
+
+
+text :: Text -> Builder
+text = byteString . encodeUtf8
+
+
+space :: Builder
+space = char8 ' '
+
+
+newline :: Builder
+newline = char8 '\n'
+
+
+openBracket :: Builder
+openBracket = char8 '{'
+
+
+closeBracket :: Builder
+closeBracket = char8 '}'
+
+
+comma :: Builder
+comma = char8 ','
+
+
+equals :: Builder
+equals = char8 '='
diff --git a/src/System/Metrics/Prometheus/Gauge.hs b/src/System/Metrics/Prometheus/Gauge.hs
deleted file mode 100644
--- a/src/System/Metrics/Prometheus/Gauge.hs
+++ /dev/null
@@ -1,35 +0,0 @@
-module System.Metrics.Prometheus.Gauge where
-
-import           Data.IORef (IORef, atomicModifyIORef', atomicWriteIORef,
-                             newIORef, readIORef)
-
-
-newtype Gauge = Gauge { unGauge :: IORef Double }
-
-
-new :: IO Gauge
-new = Gauge <$> newIORef 0
-
-add :: Double -> Gauge -> IO ()
-add x = flip atomicModifyIORef' f . unGauge
-  where f v = (v + x, ())
-
-
-sub :: Double -> Gauge -> IO ()
-sub x = flip atomicModifyIORef' f . unGauge
-  where f v = (v - x, ())
-
-inc :: Gauge -> IO ()
-inc = add 1
-
-
-dec :: Gauge -> IO ()
-dec = sub 1
-
-
-set :: Double -> Gauge -> IO ()
-set x = flip atomicWriteIORef x . unGauge
-
-
-get :: Gauge -> IO Double
-get = readIORef . unGauge
diff --git a/src/System/Metrics/Prometheus/Histogram.hs b/src/System/Metrics/Prometheus/Histogram.hs
deleted file mode 100644
--- a/src/System/Metrics/Prometheus/Histogram.hs
+++ /dev/null
@@ -1,1 +0,0 @@
-module System.Metrics.Prometheus.Histogram where
diff --git a/src/System/Metrics/Prometheus/Metric.hs b/src/System/Metrics/Prometheus/Metric.hs
--- a/src/System/Metrics/Prometheus/Metric.hs
+++ b/src/System/Metrics/Prometheus/Metric.hs
@@ -1,12 +1,31 @@
 module System.Metrics.Prometheus.Metric where
 
-import qualified System.Metrics.Prometheus.Counter as C
-import qualified System.Metrics.Prometheus.Gauge   as G
--- import qualified System.Metrics.Prometheus.Histogram as H
--- import qualified System.Metrics.Prometheus.Summary   as S
+import           System.Metrics.Prometheus.Metric.Counter   (Counter,
+                                                             CounterSample)
+import           System.Metrics.Prometheus.Metric.Gauge     (Gauge, GaugeSample)
+import           System.Metrics.Prometheus.Metric.Histogram (Histogram,
+                                                             HistogramSample)
+import           System.Metrics.Prometheus.Metric.Summary   (SummarySample)
 
+
 data Metric
-    = Counter C.Counter
-    | Gauge G.Gauge
+    = CounterMetric Counter
+    | GaugeMetric Gauge
     -- | Summary S.Summary
-    -- | Histogram H.Histogram
+    | HistogramMetric Histogram
+
+
+data MetricSample
+    = CounterMetricSample CounterSample
+    | GaugeMetricSample GaugeSample
+    | HistogramMetricSample HistogramSample
+    | SummaryMetricSample SummarySample
+
+
+metricSample :: (CounterSample -> a) -> (GaugeSample -> a)
+             -> (HistogramSample -> a) -> (SummarySample -> a)
+             -> MetricSample -> a
+metricSample f _ _ _ (CounterMetricSample s)   = f s
+metricSample _ f _ _ (GaugeMetricSample s)     = f s
+metricSample _ _ f _ (HistogramMetricSample s) = f s
+metricSample _ _ _ f (SummaryMetricSample s)   = f s
diff --git a/src/System/Metrics/Prometheus/Metric/Counter.hs b/src/System/Metrics/Prometheus/Metric/Counter.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Metrics/Prometheus/Metric/Counter.hs
@@ -0,0 +1,25 @@
+module System.Metrics.Prometheus.Metric.Counter where
+
+
+import           Data.Atomics.Counter (AtomicCounter, incrCounter_, newCounter,
+                                       readCounter)
+
+
+newtype Counter = Counter { unCounter :: AtomicCounter }
+newtype CounterSample = CounterSample { unCounterSample :: Int }
+
+
+new :: IO Counter
+new = Counter <$> newCounter 0
+
+
+add :: Int -> Counter -> IO ()
+add by = incrCounter_ by . unCounter
+
+
+inc :: Counter -> IO ()
+inc = add 1
+
+
+sample :: Counter -> IO CounterSample
+sample = fmap CounterSample . readCounter . unCounter
diff --git a/src/System/Metrics/Prometheus/Metric/Gauge.hs b/src/System/Metrics/Prometheus/Metric/Gauge.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Metrics/Prometheus/Metric/Gauge.hs
@@ -0,0 +1,38 @@
+module System.Metrics.Prometheus.Metric.Gauge where
+
+import           Data.IORef (IORef, atomicModifyIORef', atomicWriteIORef,
+                             newIORef, readIORef)
+
+
+newtype Gauge = Gauge { unGauge :: IORef Double }
+newtype GaugeSample = GaugeSample { unGaugeSample :: Double }
+
+
+new :: IO Gauge
+new = Gauge <$> newIORef 0
+
+
+add :: Double -> Gauge -> IO ()
+add x = flip atomicModifyIORef' f . unGauge
+  where f v = (v + x, ())
+
+
+sub :: Double -> Gauge -> IO ()
+sub x = flip atomicModifyIORef' f . unGauge
+  where f v = (v - x, ())
+
+
+inc :: Gauge -> IO ()
+inc = add 1
+
+
+dec :: Gauge -> IO ()
+dec = sub 1
+
+
+set :: Double -> Gauge -> IO ()
+set x = flip atomicWriteIORef x . unGauge
+
+
+sample :: Gauge -> IO GaugeSample
+sample = fmap GaugeSample . readIORef . unGauge
diff --git a/src/System/Metrics/Prometheus/Metric/Histogram.hs b/src/System/Metrics/Prometheus/Metric/Histogram.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Metrics/Prometheus/Metric/Histogram.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE TupleSections #-}
+
+module System.Metrics.Prometheus.Metric.Histogram where
+
+
+import           Data.IORef (IORef, atomicModifyIORef', newIORef, readIORef)
+import           Data.Map   (Map)
+import qualified Data.Map   as Map
+
+
+newtype Histogram = Histogram { unHistogram :: IORef HistogramSample }
+
+
+type UpperBound = Double -- Inclusive upper bounds
+type Count = Int
+type Buckets = Map UpperBound Count
+
+
+data HistogramSample =
+    HistogramSample
+    { histBuckets :: Buckets
+    , histSum     :: Double
+    , histCount   :: Count
+    }
+
+
+new :: [UpperBound] -> IO Histogram
+new buckets = Histogram <$> newIORef empty
+  where empty = HistogramSample (Map.fromList $ map (, 0) (read "Infinity" : buckets)) 0.0 0
+
+
+put :: Double -> Histogram -> IO ()
+put x ioref = atomicModifyIORef' (unHistogram ioref) update
+    where update histData = (hist' histData, ())
+          hist' histData =
+              histData { histBuckets = updateBuckets x $ histBuckets histData
+                       , histSum = histSum histData + x
+                       , histCount = histCount histData + 1
+                       }
+
+
+updateBuckets :: Double -> Buckets -> Buckets
+updateBuckets x = Map.mapWithKey updateBucket
+  where updateBucket key val
+            | x <= key  = val + 1
+            | otherwise = val
+
+
+sample :: Histogram -> IO HistogramSample
+sample = readIORef . unHistogram
diff --git a/src/System/Metrics/Prometheus/Metric/Summary.hs b/src/System/Metrics/Prometheus/Metric/Summary.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Metrics/Prometheus/Metric/Summary.hs
@@ -0,0 +1,10 @@
+module System.Metrics.Prometheus.Metric.Summary where
+
+import           Data.Map (Map)
+
+data SummarySample =
+    SummarySample
+    { sumQuantiles :: Map Double Int
+    , sumSum       :: Int
+    , sumCount     :: Int
+    }
diff --git a/src/System/Metrics/Prometheus/MetricId.hs b/src/System/Metrics/Prometheus/MetricId.hs
--- a/src/System/Metrics/Prometheus/MetricId.hs
+++ b/src/System/Metrics/Prometheus/MetricId.hs
@@ -1,11 +1,20 @@
 module System.Metrics.Prometheus.MetricId where
 
 import           Data.Map  (Map)
+import qualified Data.Map  as Map
 import           Data.Text (Text)
 
 
+newtype Name = Name { unName :: Text } deriving (Show, Eq, Ord)
+newtype Labels = Labels { unLabels :: Map Text Text } deriving (Show, Eq, Ord)
+
+
 data MetricId =
     MetricId
-    { name   :: Text
-    , labels :: Map Text Text
-    } deriving (Eq, Ord)
+    { name   :: Name
+    , labels :: Labels
+    } deriving (Eq, Ord, Show)
+
+
+addLabel :: Text -> Text -> Labels -> Labels
+addLabel key val = Labels . Map.insert key val . unLabels
diff --git a/src/System/Metrics/Prometheus/Registry.hs b/src/System/Metrics/Prometheus/Registry.hs
--- a/src/System/Metrics/Prometheus/Registry.hs
+++ b/src/System/Metrics/Prometheus/Registry.hs
@@ -1,29 +1,67 @@
-module System.Metrics.Prometheus.Registry where
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings  #-}
 
-import           Data.Map                           (Map)
-import qualified Data.Map                           as Map
+module System.Metrics.Prometheus.Registry where
 
-import           System.Metrics.Prometheus.Counter  (Counter)
-import qualified System.Metrics.Prometheus.Counter  as Counter
-import           System.Metrics.Prometheus.Gauge    (Gauge)
-import qualified System.Metrics.Prometheus.Gauge    as Gauge
-import           System.Metrics.Prometheus.Metric   (Metric)
-import qualified System.Metrics.Prometheus.Metric   as Metric
-import           System.Metrics.Prometheus.MetricId (MetricId)
+import           Control.Exception                          (Exception, throw)
+import           Data.Map                                   (Map)
+import qualified Data.Map                                   as Map
+import           Data.Typeable                              (Typeable)
 
+import           System.Metrics.Prometheus.Metric           (Metric (..),
+                                                             MetricSample (..))
+import           System.Metrics.Prometheus.Metric.Counter   (Counter)
+import qualified System.Metrics.Prometheus.Metric.Counter   as Counter
+import           System.Metrics.Prometheus.Metric.Gauge     (Gauge)
+import qualified System.Metrics.Prometheus.Metric.Gauge     as Gauge
+import           System.Metrics.Prometheus.Metric.Histogram (Histogram)
+import qualified System.Metrics.Prometheus.Metric.Histogram as Histogram
+import           System.Metrics.Prometheus.MetricId         (Labels,
+                                                             MetricId (MetricId),
+                                                             Name)
 
 newtype Registry = Registry { unRegistry :: Map MetricId Metric }
+newtype RegistrySample = RegistrySample { unRegistrySample :: Map MetricId MetricSample }
 
+newtype KeyError = KeyError MetricId deriving (Show, Typeable)
+instance Exception KeyError
 
-registerCounter :: MetricId -> Registry -> IO (Counter, Registry)
-registerCounter mid registry = do
+
+new :: Registry
+new = Registry Map.empty
+
+
+registerCounter :: Name -> Labels -> Registry -> IO (Counter, Registry)
+registerCounter name labels registry = do
     counter <- Counter.new
-    return (counter, Registry $ Map.insertWithKey collision mid (Metric.Counter counter) (unRegistry registry))
-  where collision k n o = error "oh shit"
+    return (counter, Registry $ Map.insertWithKey collision mid (CounterMetric counter) (unRegistry registry))
+  where
+      mid = MetricId name labels
+      collision k _ _ = throw (KeyError k)
 
 
-registerGauge :: MetricId -> Registry -> IO (Gauge, Registry)
-registerGauge mid registry = do
+registerGauge :: Name -> Labels -> Registry -> IO (Gauge, Registry)
+registerGauge name labels registry = do
     gauge <- Gauge.new
-    return (gauge, Registry $ Map.insertWithKey collision mid (Metric.Gauge gauge) (unRegistry registry))
-  where collision k n o = error "oh shit"
+    return (gauge, Registry $ Map.insertWithKey collision mid (GaugeMetric gauge) (unRegistry registry))
+  where
+      mid = MetricId name labels
+      collision k _ _ = throw (KeyError k)
+
+
+registerHistogram :: Name -> Labels -> [Histogram.UpperBound] -> Registry -> IO (Histogram, Registry)
+registerHistogram name labels buckets registry = do
+    histogram <- Histogram.new buckets
+    return (histogram, Registry $ Map.insertWithKey collision mid (HistogramMetric histogram) (unRegistry registry))
+  where
+      mid = MetricId name labels
+      collision k _ _ = throw (KeyError k)
+
+
+sample :: Registry -> IO RegistrySample
+sample = fmap RegistrySample . mapM sampleMetric . unRegistry
+  where
+    sampleMetric :: Metric -> IO MetricSample
+    sampleMetric (CounterMetric count) = CounterMetricSample <$> Counter.sample count
+    sampleMetric (GaugeMetric gauge) = GaugeMetricSample <$> Gauge.sample gauge
+    sampleMetric (HistogramMetric histogram) = HistogramMetricSample <$> Histogram.sample histogram
diff --git a/src/System/Metrics/Prometheus/RegistryT.hs b/src/System/Metrics/Prometheus/RegistryT.hs
new file mode 100644
--- /dev/null
+++ b/src/System/Metrics/Prometheus/RegistryT.hs
@@ -0,0 +1,57 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+module System.Metrics.Prometheus.RegistryT where
+
+import           Control.Monad.IO.Class                     (MonadIO)
+import           Control.Monad.State.Class                  (MonadState, get)
+import           Control.Monad.Trans                        (lift)
+import           Control.Monad.Trans.Class                  (MonadTrans)
+import           Control.Monad.Trans.State.Strict           (StateT (..),
+                                                             evalStateT,
+                                                             execStateT)
+
+import           System.Metrics.Prometheus.Metric.Counter   (Counter)
+import           System.Metrics.Prometheus.Metric.Gauge     (Gauge)
+import           System.Metrics.Prometheus.Metric.Histogram (Histogram)
+import qualified System.Metrics.Prometheus.Metric.Histogram as Histogram
+import           System.Metrics.Prometheus.MetricId         (Labels, Name)
+import           System.Metrics.Prometheus.Registry         (Registry (..), new)
+import qualified System.Metrics.Prometheus.Registry         as R
+
+
+newtype RegistryT m a =
+    RegistryT { unRegistryT :: StateT Registry m a }
+    deriving ( Monad, MonadTrans, Applicative, Functor
+             , MonadState Registry, MonadIO)
+
+
+evalRegistryT :: Monad m => RegistryT m a -> m a
+evalRegistryT registry = evalStateT (unRegistryT registry) new
+
+
+execRegistryT :: Monad m => RegistryT m a -> m Registry
+execRegistryT registry = execStateT (unRegistryT registry) new
+
+
+runRegistryT :: Monad m => RegistryT m a -> m (a, Registry)
+runRegistryT registry = runStateT (unRegistryT registry) new
+
+
+withRegistry :: (Registry -> m (a, Registry)) -> RegistryT m a
+withRegistry = RegistryT . StateT
+
+
+registerCounter :: Name -> Labels -> RegistryT IO Counter
+registerCounter = (.) withRegistry . R.registerCounter
+
+
+registerGauge :: Name -> Labels -> RegistryT IO Gauge
+registerGauge = (.) withRegistry . R.registerGauge
+
+
+registerHistogram :: Name -> Labels -> [Histogram.UpperBound] -> RegistryT IO Histogram
+registerHistogram = (.) (withRegistry .) . R.registerHistogram
+
+
+sample :: RegistryT IO R.RegistrySample
+sample = get >>= (lift . R.sample)
diff --git a/src/System/Metrics/Prometheus/Sample.hs b/src/System/Metrics/Prometheus/Sample.hs
deleted file mode 100644
--- a/src/System/Metrics/Prometheus/Sample.hs
+++ /dev/null
@@ -1,60 +0,0 @@
-module System.Metrics.Prometheus.Sample where
-
-
-import           Data.Map                           (Map)
-
-import qualified System.Metrics.Prometheus.Counter  as Counter
-import qualified System.Metrics.Prometheus.Gauge    as Gauge
-import           System.Metrics.Prometheus.Metric   (Metric)
-import qualified System.Metrics.Prometheus.Metric   as Metric
-import           System.Metrics.Prometheus.MetricId (MetricId)
-import           System.Metrics.Prometheus.Registry (Registry, unRegistry)
-
-
-newtype CounterSample = CounterSample { unCounterSample :: Int }
-
-newtype GaugeSample = GaugeSample { unGaugeSample :: Double }
-
-
-data HistogramSample =
-    HistogramSample
-    { histBuckets :: Map Double Int
-    , histSum     :: Int
-    , histCount   :: Int
-    }
-
-
-data SummarySample =
-    SummarySample
-    { sumQuantiles :: Map Double Int
-    , sumSum       :: Int
-    , sumCount     :: Int
-    }
-
-
-data MetricSample
-    = Counter CounterSample
-    | Gauge GaugeSample
-    | Histogram HistogramSample
-    | Summary SummarySample
-
-
-metricSample :: (CounterSample -> a) -> (GaugeSample -> a)
-             -> (HistogramSample -> a) -> (SummarySample -> a)
-             -> MetricSample -> a
-metricSample f _ _ _ (Counter s)   = f s
-metricSample _ f _ _ (Gauge s)     = f s
-metricSample _ _ f _ (Histogram s) = f s
-metricSample _ _ _ f (Summary s)   = f s
-
-
-newtype RegistrySample = RegistrySample { unRegistrySample :: Map MetricId MetricSample }
-
-
-sample :: Registry -> IO RegistrySample
-sample = fmap RegistrySample . mapM sampleMetric . unRegistry
-
-
-sampleMetric :: Metric -> IO MetricSample
-sampleMetric (Metric.Counter count) = Counter . CounterSample <$> Counter.view count
-sampleMetric (Metric.Gauge gauge) = Gauge . GaugeSample <$> Gauge.get gauge
diff --git a/src/System/Metrics/Prometheus/Summary.hs b/src/System/Metrics/Prometheus/Summary.hs
deleted file mode 100644
--- a/src/System/Metrics/Prometheus/Summary.hs
+++ /dev/null
@@ -1,1 +0,0 @@
-module System.Metrics.Prometheus.Summary where
