docs: def compare_record_iter(a, b, fs_a=None, fs_b=None, options=None):
      """This generator function compares a record, slot by slot, and yields
      differences found as ``DiffInfo`` objects.
  
      args:
  
          ``a=``\ *Record*
              The base object
  
          ``b=``\ *Record*\ \|\ *object*
              The 'other' object, which must be the same type as ``a``, unless
              ``options.duck_type`` is set.
  
          ``fs_a=``\ *FieldSelector\*
              The current diff context, prefixed to any returned ``base`` field
              in yielded ``DiffInfo`` objects.  Defaults to an empty
              FieldSelector.
  
          ``fs_b=``\ *FieldSelector\*
              The ``other`` object context.  This will differ from ``fs_a`` in
              the case of collections, where a value has moved slots.  Defaults
              to an empty FieldSelector.
  
          ``options=``\ *DiffOptions\*
              A constructed ``DiffOptions`` object; a default one is created if
              not passed in.
      """
      if not options:
          options = DiffOptions()
  
      if not options.duck_type and type(a) != type(b) and not (
          a is _nothing or b is _nothing
      ):
          raise TypeError(
              "cannot compare %s with %s" % (type(a).__name__, type(b).__name__)
          )
  
      if fs_a is None:
          fs_a = FieldSelector(tuple())
          fs_b = FieldSelector(tuple())
  
      properties = (
          type(a).properties if a is not _nothing else type(b).properties
      )
      for propname in sorted(properties):
  
          prop = properties[propname]
  
          if options.is_filtered(prop, fs_a + propname):
              continue
  
          propval_a = options.normalize_object_slot(
              getattr(a, propname, _nothing), prop, a,
          )
          propval_b = options.normalize_object_slot(
              getattr(b, propname, _nothing), prop, b,
          )
  
          if propval_a is _nothing and propval_b is _nothing:
              # don't yield NO_CHANGE for fields missing on both sides
              continue
  
          one_side_nothing = (propval_a is _nothing) != (propval_b is _nothing)
          types_match = type(propval_a) == type(propval_b)
          comparable = (
              isinstance(propval_a, COMPARABLE) or
              isinstance(propval_b, COMPARABLE)
          )
          prop_fs_a = fs_a + [propname]
          prop_fs_b = fs_b + [propname]
  
          if comparable and (
              types_match or options.duck_type or (
                  options.ignore_empty_slots and one_side_nothing
              )
          ):
              if one_side_nothing:
                  diff_types_found = set()
  
              for diff in _diff_iter(
                  propval_a, propval_b, prop_fs_a,
                  prop_fs_b, options,
              ):
                  if one_side_nothing:
                      if diff.diff_type != DiffTypes.NO_CHANGE:
                          diff_types_found.add(diff.diff_type)
                  else:
                      yield diff
  
              if one_side_nothing:
                  net_diff = None
                  if diff_types_found:
                      assert(len(diff_types_found) == 1)
                      net_diff = tuple(diff_types_found)[0]
                  elif options.unchanged:
                      net_diff = DiffTypes.NO_CHANGE
                  if net_diff:
                      yield DiffInfo(
                          diff_type=net_diff,
                          base=prop_fs_a,
                          other=prop_fs_b,
                      )
  
          elif one_side_nothing:
              yield DiffInfo(
                  diff_type=(
                      DiffTypes.ADDED if propval_a is _nothing else
                      DiffTypes.REMOVED
                  ),
                  base=fs_a + [propname],
                  other=fs_b + [propname],
              )
  
          elif not options.items_equal(propval_a, propval_b):
              yield DiffInfo(
                  diff_type=DiffTypes.MODIFIED,
                  base=fs_a + [propname],
                  other=fs_b + [propname],
              )
  
          elif options.unchanged:
              yield DiffInfo(
                  diff_type=DiffTypes.NO_CHANGE,
                  base=fs_a + [propname],
                  other=fs_b + [propname],
              )