merge(...) lazily collapses all arguments, which must be python Mapping objects of some kind,
      into a single mapping from left-to-right. The mapping that is returned is a lazy persistent
      object that does not request the value of a key from any of the maps provided until they are
      requested of it; in this fashion it preserves the laziness of immutable map objects that are
      passed to it. Arguments may be mappings or lists/tuples of mappings.

    If all of the arguments passed to merge are pimms itables with the same row_count, then an
    itable object is returned instead of a lazy map.

    The following options are accepted:
    * choose (default None) specifies a function that chooses from which map, of those maps given
      to merge, the value should be drawn when keys overlap. The function is always passed two
      arguments: the key for which the conflict occurs and a list of maps containing that key; it
      should return the value to which the key should be mapped. The default uses the first map.

def merge(*args, **kwargs):
    '''
    merge(...) lazily collapses all arguments, which must be python Mapping objects of some kind,
      into a single mapping from left-to-right. The mapping that is returned is a lazy persistent
      object that does not request the value of a key from any of the maps provided until they are
      requested of it; in this fashion it preserves the laziness of immutable map objects that are
      passed to it. Arguments may be mappings or lists/tuples of mappings.

    If all of the arguments passed to merge are pimms itables with the same row_count, then an
    itable object is returned instead of a lazy map.

    The following options are accepted:
    * choose (default None) specifies a function that chooses from which map, of those maps given
      to merge, the value should be drawn when keys overlap. The function is always passed two
      arguments: the key for which the conflict occurs and a list of maps containing that key; it
      should return the value to which the key should be mapped. The default uses the first map.
    '''
    from .table import (is_itable, ITable)
    # figure out the choose-fn
    choose_fn = None
    if 'choose' in kwargs:
        choose_fn = kwargs['choose']
    if len(kwargs) > 1 or (len(kwargs) > 0 and 'choose' not in kwargs):
        raise ValueError('Unidentified options given to merge: %s' (kwargs.keys(),))
    # collect the maps...
    maps = flatten_maps(*args)
    if len(maps) == 0: return ps.m()
    elif len(maps) == 1: return maps[0]
    coll = collect(maps)
    if choose_fn is None: choose_fn = _choose_last
    def curry_choice(k, args): return lambda:choose_fn(k, args)
    resmap = lazy_map({k:curry_choice(k, v) for (k,v) in six.iteritems(coll)})
    # if they're all itables of the same size, return an itable
    if is_itable(maps[0]):
        n = maps[0].row_count
        if all(is_itable(m) and m.row_count == n for m in maps):
            return ITable(resmap, n)
    # otherwise return the lazy map
    return resmap