func mergeMaps(map1, map2 map[interface{}]interface{}) map[interface{}]interface{} {
	result := make(map[interface{}]interface{})
	for k, v2 := range map2 {
		if v1, ok := map1[k]; !ok {
			result[k] = v2
		} else {
			map1, ok1 := v1.(map[interface{}]interface{})
			map2, ok2 := v2.(map[interface{}]interface{})
			if ok1 && ok2 {
				result[k] = mergeMaps(map1, map2)
			} else {
				result[k] = v2
			}
		}
	}
	for k, v := range map1 {
		if v2, ok := map2[k]; !ok {
			result[k] = v
		} else {
			map1, ok1 := v.(map[interface{}]interface{})
			map2, ok2 := v2.(map[interface{}]interface{})
			if ok1 && ok2 {
				result[k] = mergeMaps(map1, map2)
			}
		}
	}
	return result
}
mergeMaps takes two maps and merge its keys and values recursively.

In case of conflicts, the function picks value from map2.
package config
import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/howeyc/fsnotify"
	"gopkg.in/yaml.v1"
)
var ErrMismatchConf = errors.New("Your conf is wrong:")
type configuration struct {
	data map[interface{}]interface{}
	sync.RWMutex
}
func (c *configuration) Store(data map[interface{}]interface{}) {
	c.Lock()
	defer c.Unlock()
	c.store(data)
}
func (c *configuration) store(data map[interface{}]interface{}) {
	c.data = data
}
func (c *configuration) Data() map[interface{}]interface{} {
	c.RLock()
	defer c.RUnlock()
	return c.data
}
var configs configuration
func readConfigBytes(data []byte, out interface{}) error {
	return yaml.Unmarshal(data, out)
}
func ReadConfigBytes(data []byte) error {
	var newConfig map[interface{}]interface{}
	err := readConfigBytes(data, &newConfig)
	if err == nil {
		configs.Store(newConfig)
	}
	return err
}
func ReadConfigFile(filePath string) error {
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		return err
	}
	return ReadConfigBytes(data)
}
func ReadAndWatchConfigFile(filePath string) error {
	err := ReadConfigFile(filePath)
	if err != nil {
		return err
	}
	w, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}
	err = w.Watch(filePath)
	if err != nil {
		return err
	}
	go func() {
		for {
			select {
			case e := <-w.Event:
				if e.IsModify() {
					ReadConfigFile(filePath)
				}
			case <-w.Error:  
			}
		}
	}()
	return nil
}
func Bytes() ([]byte, error) {
	return yaml.Marshal(configs.Data())
}
func WriteConfigFile(filePath string, perm os.FileMode) error {
	b, err := Bytes()
	if err != nil {
		return err
	}
	f, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_EXCL, perm)
	if err != nil {
		return err
	}
	defer f.Close()
	n, err := f.Write(b)
	if err != nil {
		return err
	}
	if n != len(b) {
		return io.ErrShortWrite
	}
	return nil
}
func Get(key string) (interface{}, error) {
	keys := strings.Split(key, ":")
	configs.RLock()
	defer configs.RUnlock()
	conf, ok := configs.data[keys[0]]
	if !ok {
		return nil, fmt.Errorf("key %q not found", key)
	}
	for _, k := range keys[1:] {
		_, ok = conf.(map[interface{}]interface{})
		if !ok {
			return nil, ErrMismatchConf
		}
		if conf, ok = conf.(map[interface{}]interface{})[k]; !ok {
			return nil, fmt.Errorf("key %q not found", key)
		}
	}
	if v, ok := conf.(func() interface{}); ok {
		conf = v()
	}
	if v, ok := conf.(string); ok {
		return os.ExpandEnv(v), nil
	}
	return conf, nil
}
func GetString(key string) (string, error) {
	value, err := Get(key)
	if err != nil {
		return "", err
	}
	switch v := value.(type) {
                case int:
                        return strconv.Itoa(v), nil
                case int64:
                        return strconv.FormatInt(v, 10), nil
                default:
                        if v, ok := value.(string); ok {
                                return v, nil
                        }
        }
	return "", &invalidValue{key, "string|int|int64"}
}
func GetInt(key string) (int, error) {
	value, err := Get(key)
	if err != nil {
		return 0, err
	}
	if v, ok := value.(int); ok {
		return v, nil
	} else if v, ok := value.(string); ok {
		if i, err := strconv.ParseInt(v, 10, 64); err == nil {
			return int(i), nil
		}
	}
	return 0, &invalidValue{key, "int"}
}
func GetFloat(key string) (float64, error) {
	value, err := Get(key)
	if err != nil {
		return 0, err
	}
	switch v := value.(type) {
	case float64:
		return v, nil
	case float32:
		return float64(v), nil
	case int:
		return float64(v), nil
	case string:
		if floatVal, err := strconv.ParseFloat(v, 64); err == nil {
			return floatVal, nil
		}
	}
	return 0, &invalidValue{key, "float"}
}
func GetUint(key string) (uint, error) {
	value, err := Get(key)
	if err != nil {
		return 0, err
	}
	if v, ok := value.(int); ok {
		if v < 0 {
			return 0, &invalidValue{key, "uint"}
		}
		return uint(v), nil
	}
	return 0, &invalidValue{key, "uint"}
}
func GetDuration(key string) (time.Duration, error) {
	value, err := Get(key)
	if err != nil {
		return 0, err
	}
	switch v := value.(type) {
	case int:
		return time.Duration(v), nil
	case float64:
		return time.Duration(v), nil
	case string:
		if value, err := time.ParseDuration(value.(string)); err == nil {
			return value, nil
		}
	}
	return 0, &invalidValue{key, "duration"}
}
func GetBool(key string) (bool, error) {
	value, err := Get(key)
	if err != nil {
		return false, err
	}
	if v, ok := value.(bool); ok {
		return v, nil
	}
	return false, &invalidValue{key, "boolean"}
}
func GetList(key string) ([]string, error) {
	value, err := Get(key)
	if err != nil {
		return nil, err
	}
	switch value.(type) {
	case []interface{}:
		v := value.([]interface{})
		result := make([]string, len(v))
		for i, item := range v {
			switch v := item.(type) {
			case int:
				result[i] = strconv.Itoa(v)
			case bool:
				result[i] = strconv.FormatBool(v)
			case float64:
				result[i] = strconv.FormatFloat(v, 'f', -1, 64)
			case string:
				result[i] = v
			default:
				result[i] = fmt.Sprintf("%v", item)
			}
		}
		return result, nil
	case []string:
		return value.([]string), nil
	}
	return nil, &invalidValue{key, "list"}
}