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],
            )
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.