ctype icon indicating copy to clipboard operation
ctype copied to clipboard

ctype= Returns nil with Keyword Parameters

Open charJe opened this issue 2 years ago • 6 comments

(assert
 (ctype:ctype=
  (ctype:cfunction (make-instance
                    'ctype:lambda-list
                    :required ()
                    :optional ()
                    :rest (ctype:bot)
                    :keyp t
                    :keys (list (cons 'var (ctype:specifier-ctype 'integer)))
                    :aokp nil)
                   (ctype:values-specifier-ctype '(values integer &rest t)))
  (ctype:specifier-ctype
   '(function (&key (var integer)) integer))))
The assertion
(ctype:ctype=
 #<ctype:cfunction (function (&key (var integer))
                    (values integer &rest t))>
 #<ctype:cfunction (function (&key (var integer))
                    (values integer &rest t))>)

That looks a bit funky to me. Similar experiments work for required, optional, and rest style parameters. Is there something I'm missing when it comes to the keyword parameters?

charJe avatar Jan 17 '23 03:01 charJe

You have the rest type as bottom, which is probably not correct. Since the function takes keywords, any type of argument can be validly provided in the rest list (if only through :allow-other-keys). That might not be the problem here though. Did your paste cut off?

Bike avatar Jan 21 '23 04:01 Bike

I thought rest type should be bottom to indicate that there is not rest parameter (no &rest in lambda-list). Are you saying that it should not be the bottom since &key comes after &rest? These functions I'm talking about don't have &allow-other-keys.

charJe avatar Jan 21 '23 04:01 charJe

a rest type of bottom indicates that that the function is not passed arguments other than required and optional. so if the rest type is bottom, keyword arguments, which are not required or optional, cannot be accepted. if the function accepts keywords the rest type should be top.

function types are not strictly related to the lambda list of the function: they just describe what calls are valid. It is also permissible to declare local function types that describe calls without bothering with what the function actually accepts. For example you could have (defun mylist (&rest elems) elems); it would then be permissible to declare in some scope that (ftype (function (float float) list) mylist) to indicate that within that scope, mylist is only ever passed two floats.

I was referring to :allow-other-keys, which is a distinct mechanism from the &allow-other-keys lambda list keyword. The short version is that if you pass :allow-other-keys t, any function that accepts keywords can be made to accept any keywords even if the function lambda list does not have &allow-other-keys. See CLHS 3.4.1.4.1 for details.

Bike avatar Jan 22 '23 16:01 Bike

That is all really interesting and makes perfect sense now. Except when I inspect (ctype:specifier-ctype '(function (&key (var integer)))) I get:

All Slots:
[ ]  %required = nil
[ ]  %optional = nil
[ ]  %rest     = #<ctype:disjunction nil>
[ ]  %keyp     = t
[ ]  %keys     = ((var . #<ctype:range integer>))
[ ]  %aokp     = nil

It looks to me like the rest is (ctype:bot).

charJe avatar Jan 22 '23 20:01 charJe

It's quite possible keyword parameter types are in fact broken. I have not used this library as thoroughly as I had hoped. I might be able to take a look tomorrow.

Bike avatar Jan 23 '23 02:01 Bike

okay, yeah, somewhat more fundamental problem is that I did not implement function type subtypep in the presence of keyword types: https://github.com/s-expressionists/ctype/blob/main/cfunction.lisp#L42-L44

Bike avatar Jan 23 '23 14:01 Bike