[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.lisp

On the usefulness of ftype

Didier Verna

11/9/2015 3:58:00 PM


Hey there,

until a couple of minutes ago, I assumed that ftype declarations were
just a more concise way of declaring argument and return value
types. That is, instead of writing

(defun plus (a b)
(declare (type fixnum a b))
(the fixnum (+ a b)))

you could just
(declaim (ftype (function (fixnum fixnum) fixnum) plus))
(defun plus (a b) (+ a b))


But apparently, this is wrong. According to CLtL2 section 4.5, ftype
relates to function /calls/ and not function /definitions/. Indeed,
let's do this (in SBCL):

(declaim (optimize (safety 3) (debug 3) (speed 0)))
(declaim (ftype (function (fixnum fixnum) fixnum) plus))
(defun plus (a b) (+ a b))

Disassembly of PLUS will still show the use of GENERIC-+, but with
additional checks on the type of the arguments and the return value. My
first reaction was that this is weird: if ftype is to specify how the
function is used, as opposed to as it is implemented, it shouldn't
affect the way the function is implemented! In other words, the checks
should wrap the caller instead of being incorporated into the callee.

On top of that, at least in SBCL, changing the declamation later on
doesn't affect the function, unless you re-issue the defun. Which sort
of explains why the checks are embedded in the function: it's simpler to
do it this way rather than wrapping every caller.

So, in a safety context as above, I can't find a usefulness to ftype,
apart from the fact that it's slightly more concise than a PLUS with
type declarations for parameters and THE return value. It generates the
same machine code. I think it would have been useful for, say, defining
a generic PLUS somewhere, and then declaring that you're using it as
fixnum -> fixnum -> fixnum in file1, and then float -> float -> float in
file2. But that doesn't work.


Now, the same experiment with

(declaim (optimize (safety 0) (debug 0) (speed 3)))

shows that PLUS would only be fully optimized if you provided parameter
type declarations and THE return value within the function. Not with an
ftype declaration. So ftype seems to be even less useful here.

Thoughts ?

--
My new Jazz CD entitled "Roots and Leaves" is out!
Check it out: http://didierverna.com/records/roots-and-...

Lisp, Jazz, Aïkido: http://www.didier...
9 Answers

Barry Margolin

11/9/2015 5:11:00 PM

0

In article <m1bnb3tbaf.fsf@scofield.lrde.epita.fr>,
Didier Verna <didier@lrde.epita.fr> wrote:

> Hey there,
>
> until a couple of minutes ago, I assumed that ftype declarations were
> just a more concise way of declaring argument and return value
> types. That is, instead of writing
>
> (defun plus (a b)
> (declare (type fixnum a b))
> (the fixnum (+ a b)))
>
> you could just
> (declaim (ftype (function (fixnum fixnum) fixnum) plus))
> (defun plus (a b) (+ a b))
>
>
> But apparently, this is wrong. According to CLtL2 section 4.5, ftype
> relates to function /calls/ and not function /definitions/. Indeed,
> let's do this (in SBCL):
>
> (declaim (optimize (safety 3) (debug 3) (speed 0)))
> (declaim (ftype (function (fixnum fixnum) fixnum) plus))
> (defun plus (a b) (+ a b))
>
> Disassembly of PLUS will still show the use of GENERIC-+, but with
> additional checks on the type of the arguments and the return value. My
> first reaction was that this is weird: if ftype is to specify how the
> function is used, as opposed to as it is implemented, it shouldn't
> affect the way the function is implemented! In other words, the checks
> should wrap the caller instead of being incorporated into the callee.

What difference does it make? Either way, you get the same safety checks.

>
> On top of that, at least in SBCL, changing the declamation later on
> doesn't affect the function, unless you re-issue the defun. Which sort
> of explains why the checks are embedded in the function: it's simpler to
> do it this way rather than wrapping every caller.
>
> So, in a safety context as above, I can't find a usefulness to ftype,
> apart from the fact that it's slightly more concise than a PLUS with
> type declarations for parameters and THE return value. It generates the
> same machine code. I think it would have been useful for, say, defining
> a generic PLUS somewhere, and then declaring that you're using it as
> fixnum -> fixnum -> fixnum in file1, and then float -> float -> float in
> file2. But that doesn't work.
>
>
> Now, the same experiment with
>
> (declaim (optimize (safety 0) (debug 0) (speed 3)))
>
> shows that PLUS would only be fully optimized if you provided parameter
> type declarations and THE return value within the function. Not with an
> ftype declaration. So ftype seems to be even less useful here.
>
> Thoughts ?

Sounds like SBCL is trying to be more fail-safe. High safety settings
apply to all callers, even those defined before the optimize
declamation, but speed settings don't.

--
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***

Didier Verna

11/9/2015 8:21:00 PM

0

Barry Margolin <barmar@alum.mit.edu> wrote:

>> Disassembly of PLUS will still show the use of GENERIC-+, but with
>> additional checks on the type of the arguments and the return value. My
>> first reaction was that this is weird: if ftype is to specify how the
>> function is used, as opposed to as it is implemented, it shouldn't
>> affect the way the function is implemented! In other words, the checks
>> should wrap the caller instead of being incorporated into the callee.
>
> What difference does it make? Either way, you get the same safety checks.

The difference is, or rather, would have been, if it were possible to
have different ftype declarations at different places, yet related to
the same function implemented in a single way.

--
My new Jazz CD entitled "Roots and Leaves" is out!
Check it out: http://didierverna.com/records/roots-and-...

Lisp, Jazz, Aïkido: http://www.didier...

Marco Antoniotti

11/10/2015 9:24:00 AM

0

On Monday, November 9, 2015 at 9:21:25 PM UTC+1, Didier Verna wrote:
> Barry Margolin <barmar@alum.mit.edu> wrote:
>
> >> Disassembly of PLUS will still show the use of GENERIC-+, but with
> >> additional checks on the type of the arguments and the return value. My
> >> first reaction was that this is weird: if ftype is to specify how the
> >> function is used, as opposed to as it is implemented, it shouldn't
> >> affect the way the function is implemented! In other words, the checks
> >> should wrap the caller instead of being incorporated into the callee.
> >
> > What difference does it make? Either way, you get the same safety checks.
>
> The difference is, or rather, would have been, if it were possible to
> have different ftype declarations at different places, yet related to
> the same function implemented in a single way.
>

What do you mean by this? Do you have an example at hand?

MA

Pascal J. Bourguignon

11/10/2015 12:06:00 PM

0

Marco Antoniotti <marcoxa@gmail.com> writes:

> On Monday, November 9, 2015 at 9:21:25 PM UTC+1, Didier Verna wrote:
>> Barry Margolin <barmar@alum.mit.edu> wrote:
>>
>> >> Disassembly of PLUS will still show the use of GENERIC-+, but with
>> >> additional checks on the type of the arguments and the return value. My
>> >> first reaction was that this is weird: if ftype is to specify how the
>> >> function is used, as opposed to as it is implemented, it shouldn't
>> >> affect the way the function is implemented! In other words, the checks
>> >> should wrap the caller instead of being incorporated into the callee.
>> >
>> > What difference does it make? Either way, you get the same safety checks.
>>
>> The difference is, or rather, would have been, if it were possible to
>> have different ftype declarations at different places, yet related to
>> the same function implemented in a single way.
>>
>
> What do you mean by this? Do you have an example at hand?

You could make a local declaration:

(defun fs (seq)
(etypecase seq
(list (nth seq 0))
(vector (aref seq 0))))

(locally
(declaim (ftype (function (list) t)))
(fs '(1 2 3)))

(locally
(declaim (ftype (function (vector) t)))
(fs #(1 2 3)))


The call in the first locally could actually call a list-specific
entry-point in the FS function, while the second locally could actually
call a vector-specific entry-point in the FS function, both without
doing any type checking on the parameter.


For global declaimation, I guess we could assume ftype declaimation
shadows in the current compilation-unit any other ftype declaimation done
in other compilation-units (independently on whether it was in the same
compilation unit where the concerned function was defined), since it
applies to call sites.

--
__Pascal Bourguignon__ http://www.informat...
â??The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.� -- Carl Bass CEO Autodesk

Didier Verna

11/10/2015 12:18:00 PM

0

Marco Antoniotti wrote:

>> The difference is, or rather, would have been, if it were possible to
>> have different ftype declarations at different places, yet related to
>> the same function implemented in a single way.
>>
>
> What do you mean by this? Do you have an example at hand?

(declare (ftype (function (fixnum fixnum) fixnum) plus))

means that every call to (plus a b) is actually a call to
(the fixnum (plus (the fixnum a) (the fixnum b)))

and /not/ equivalent to (defun plus (a b)
(declare (fixnum a b))
(the fixnum (+ a b)))


thus, it could have made sense to define a generic PLUS function, and
then do:
(declare (ftype (function (fixnum fixnum) fixnum) plus)) ;; in some file
(declare (ftype (function (float float) float) plus)) ;; somewhere else

like a contract of sorts.

--
My new Jazz CD entitled "Roots and Leaves" is out!
Check it out: http://didierverna.com/records/roots-and-...

Lisp, Jazz, Aïkido: http://www.didier...

Didier Verna

11/10/2015 12:22:00 PM

0

"Pascal J. Bourguignon" <pjb@informatimago.com> wrote:

> Marco Antoniotti <marcoxa@gmail.com> writes:
>>
>> What do you mean by this? Do you have an example at hand?
>
> You could make a local declaration:

Yes Pascal, your example is actually clearer than mine.

--
My new Jazz CD entitled "Roots and Leaves" is out!
Check it out: http://didierverna.com/records/roots-and-...

Lisp, Jazz, Aïkido: http://www.didier...

Helmut Eller

11/10/2015 1:37:00 PM

0

On Mon, Nov 09 2015, Didier Verna wrote:

> (defun plus (a b)
> (declare (type fixnum a b))
> (the fixnum (+ a b)))

This is equivalent to

(setf (fdefnition plus)
(lambda (a b) (declare ...) ...))

and does not restrict PLUS in any way. As PLUS has no particular
restrictions, the compiler can't be sure that PLUS will be re-defined
with a different lambda and it can't assume anything about
argument/return types at call-sites of PLUS. In this example the
compiler has to check arguments "in" the lambda not at the call-site.
(In SBCL's argument checking is often done in the XEP which is not shown
if you just look at the output of (dissasemble 'plus)).

> you could just
> (declaim (ftype (function (fixnum fixnum) fixnum) plus))
> (defun plus (a b) (+ a b))

This is different with (declaim (ftype ...) plus) which tells the
compiler that only lambdas with the specified ftype are valid as values
for PLUS. So the compiler can make assumptions about the types at
call-sites. (SBCL has no calling convention to bypass type-checks in
the callee so it will likely perform the checks at the call-site and
redundantly in the XEP.)

> On top of that, at least in SBCL, changing the declamation later on
> doesn't affect the function, unless you re-issue the defun. Which sort
> of explains why the checks are embedded in the function: it's simpler to
> do it this way rather than wrapping every caller.

The second declaration will not "replace" the first; it adds more
restrictions to PLUS.

Helmut

Marco Antoniotti

11/10/2015 7:05:00 PM

0

On Tuesday, November 10, 2015 at 1:17:40 PM UTC+1, Didier Verna wrote:
> Marco Antoniotti wrote:
>
> >> The difference is, or rather, would have been, if it were possible to
> >> have different ftype declarations at different places, yet related to
> >> the same function implemented in a single way.
> >>
> >
> > What do you mean by this? Do you have an example at hand?
>
> (declare (ftype (function (fixnum fixnum) fixnum) plus))
>
> means that every call to (plus a b) is actually a call to
> (the fixnum (plus (the fixnum a) (the fixnum b)))
>
> and /not/ equivalent to (defun plus (a b)
> (declare (fixnum a b))
> (the fixnum (+ a b)))
>
>
> thus, it could have made sense to define a generic PLUS function, and
> then do:
> (declare (ftype (function (fixnum fixnum) fixnum) plus)) ;; in some file
> (declare (ftype (function (float float) float) plus)) ;; somewhere else
>
> like a contract of sorts.
>

Got it.

As an aside, It is unfortunate that there is no way of declaring the type of the return value within a definition.

If I remember correctly CMUCL/SBCL allow a VALUES declaration in a DECLARE form, but outside a FUNCTION type specifier, to signify the value of the form containing the declaration.

MA

Waldek Hebisch

11/11/2015 9:57:00 PM

0

Didier Verna <didier@lrde.epita.fr> wrote:
>
> According to CLtL2 section 4.5, ftype
> relates to function /calls/ and not function /definitions/.
<snip>
> (declaim (optimize (safety 0) (debug 0) (speed 3)))
>
> shows that PLUS would only be fully optimized if you provided parameter
> type declarations and THE return value within the function. Not with an
> ftype declaration. So ftype seems to be even less useful here.

As you wrote, ftype is about call sites. I do not have first
hand experience with ftype in sbcl, but trivial declaration of
function type in FUNCALL, like:

(FUNCALL (the (function (t t) t) f) a1 a2)

in the past gave me about 3 times improvement in speed
of FUNCALL (and about 10% for whole program). AFAIU part
was due to skipped checking of f to be a symbol. But part
was due to skipped handling of keyword argument and
multiple values. The second part should apply to ftype
as well. With nontrivial declaration compiler may use
return type of f to infer type of value and to use
types of arguments in backward reasoning to reconstruct
types of variables. That may give significant speedup.
Also, compiler may use ftype declaration to call alternative
entry point taking unboxed parameters and returning uboxed
values. IIUC gcl is doing this. And in the past having
ftype declarations made significant difference in speed of
gcl compiled program (now gcl seem to be able to collect
more information from the program, so declarations are
less important).

--
Waldek Hebisch