 
         @._generate_more(key, chain, max - 1, callback)
 
-
+# Markov storage implementation that uses redis hash keys to store the model.
 class RedisMarkovStorage
 
+  # Prefix used to isolate stored markov transitions from other keys in the database.
   keyprefix = "markov:"
 
+  # Create a storage module that uses the provided Redis connection.
   constructor: (@client) ->
 
+  # Uniformly and unambiguously convert an array of Strings and nulls into a valid
+  # Redis key. Uses a length-prefixed encoding.
+  #
+  # _encode([null, null, "a"]) = "markov:001a"
+  # _encode(["a", "bb", "ccc"]) = "markov:1a2b3c"
   _encode: (key) ->
     encoded = for part in key
       if part then "#{part.length}#{part}" else "0"
     keyprefix + encoded.join('')
 
+  # Record a transition within the model. "transition.from" is an array of Strings and
+  # nulls marking the prior state and "transition.to" is the observed next state, which
+  # may be an end-of-chain sentinel.
   increment: (transition) ->
     @client.hincrby(@._encode(transition.from), transition.to, 1)
 
+  # Retrieve an object containing the possible next hops from a prior state and their
+  # relative frequencies. Invokes "callback" with the object.
   get: (prior, callback) ->
     @client.hgetall @._encode(prior), (err, hash) ->
       throw err if err

    for i in [0..words.length - @ply - 1]
      { from: words.slice(i, i + @ply), to: words[i + @ply] or sentinel }

  learn: (phrase) ->
    @storage.increment(t) for t in @._transitions(phrase)

  generate: (seed, max, callback) ->
    words = @._words(seed)

    key = words.slice(words.length - @ply, words.length)
    if key.length < @ply
      key.unshift null for i in [1..@ply - key.length]

    chain = []
    chain.push words...

    @._generate_more key, chain, max, callback

  _generate_more: (key, chain, max, callback) ->
    @storage.get key, (choices) =>
      next = @._chooseWeighted choices

  client = Redis.createClient(info.port, info.hostname)
  storage = new RedisMarkovStorage(client)

  # Read markov-specific configuration from the environment.
  ply = process.env.HUBOT_MARKOV_PLY or 1
  max = process.env.HUBOT_MARKOV_MAX or 50

