Kaz Kylheku
3/17/2016 7:22:00 PM
On 2016-03-17, Alan Bawden <alan@csail.mit.edu> wrote:
> Jeff Barnett <jbb@notatt.com> writes:
>> ..
>> So we get the concept that (function (rational) rational) is a subtype of
>> (function (integer) number) because any function with the first type can be
>> called when the expectation is using a function of the second type.
>
> And yet in SBCL:
>
> * (subtypep '(function (rational) rational) '(function (integer) number))
>
> nil
> t
> * (subtypep '(function (integer) rational) '(function (rational) number))
>
> t
> t
This looks like it is based on the concept that if a set of functions
accepts a number, then the set of functions which requires that number
to specifically be an integer and not just any number is a subset of
that set.
This is an instance in which subtyping by subsetting appears to be at
odds with subtyping by substitutability.
Integer-accepting functions are a special case of number-accepting
functions, belonging to that set. Yet, a number-accepting function (member
of the broad set) can be substituted whenever an integer-accepting
function is required (member of the subset).
By contrast, though circles are a subset of ellipses, an
ellipse cannot be substituted wherever a circle is required.
Same with "animal inheritance". If specifically a bear is required, an
animal will not do.
Can we model a function as a record, together with an evaluate?
That is to say suppose that (function (integer integer) rational)
is regarded similarly to be a (record rational integer integer).
The first slot is the return value (output slot), the remaining ones are
arguments (input slots). This record also has an associated operation,
which is not represented as type.
A function call takes place by stuffing values into input slots. The
record is then operated upon by a generic operator which invokes
the associated procedure. The procedure examines the input slots,
performs some calculation, and deposits a value into the output slot.
record of this type).
So why is it that we have to treat the output and input slots specially?
Both are subject to stuffing and retrieval.
But that's the thing: the caller stuffs the input slots and accesses
the output slot. Whereas the callee does the opposite.
Stuffing and accessing are different uses. If a slot is restricted
to rational, when you're stuffing it, you cannot stuff in any old number;
it has to be an integer or ratio. But you can access the slot under the
expectation of obtaining a number; your expectation isn't violated
by obtaining an integer or ratio, since the are numbers.
So restricting the output slot doesn't matter from the caller's
perspective, but restricting the input slots does.
From the callee's perspective, restricting the input slots
doesn't matter, but restricting the output slot does.
If a function that previously returned number is restricted to return
integer, that doesn't affect any outsiders; they are still getting
a number from the "output slot". That function's code is affected;
it can no longer return a ratio or real as it was doing before.
From the function's own point of view, its own new type is not
substitutable for the old type for the purposes of returning a value.
From the point of view of callers, it is perfectly substitutable.