Please help my head get around 'deftype'

Discussion of Common Lisp
findinglisp
Posts: 447
Joined: Sat Jun 28, 2008 7:49 am
Location: Austin, TX
Contact:

Re: Please help my head get around 'deftype'

Post by findinglisp » Tue Sep 30, 2008 8:19 am

tlareywi wrote: As I mentioned earlier, I don't believe it is a pervasive problem. I don't think explicit type checking needs to be littered throughout most code. I'm focusing on module level entry points...APIs if you like.
Gotcha. That's not unreasonable in a few strategic locations. TYPEDEF can help you build up more complex types and the associated predicates for checking them. You said that Graham didn't go far enough. Have you read through the appropriate sections of CLHS and CLtL2?

http://www.lispworks.com/documentation/ ... /index.htm
http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/clm.html

From a quick reading (don't take my word for it, I might have missed it), CLtL2 seems to imply that you can't check functional arguments. That is, you can determine if FOO is a function with FUNCTIONP, but not whether FOO is a function taking an integer and a list. See the description of "(FUNCTION ...)" here http://www.cs.cmu.edu/Groups/AI/html/cl ... ode49.html . It says you can use the function specifier to declare the types of arguments (optimization) but not check them.

I quickly looked through CLHS and I can't seem to find any language one way or another. You can always try it with TYPEP in your implementation and see what it does. CLtL2 says it will signal an error.
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

TheGZeus
Posts: 79
Joined: Mon Jun 30, 2008 10:46 am

Re: Please help my head get around 'deftype'

Post by TheGZeus » Tue Sep 30, 2008 8:40 am

tlareywi wrote: I'd be interested to hear from folks that have worked on mid to large Lisp programs, say 100k+ lines of code, and how they tackled API/interface sorts issues between code modules owned by different devs, etc.
It's not CL, but emacshas over on million lines of Elisp, in one namespace...

findinglisp
Posts: 447
Joined: Sat Jun 28, 2008 7:49 am
Location: Austin, TX
Contact:

Re: Please help my head get around 'deftype'

Post by findinglisp » Tue Sep 30, 2008 10:00 am

TheGZeus wrote:...emacshas over on million lines of Elisp, in one namespace...
Proving that even dumb ideas are workable if you try hard enough. :lol:

Seriously, while it is a quite amazing feat if you think about it, I'm not sure anybody would argue that it's the right way to build a large system. What happens is that all emacs symbols end up getting prefixed with the name of the "package" they are associated with. For instance, everything in SLIME has a "slime-" prefix. Essentially, it's the equivalent of typing "package:symbol" all the time, except it's "package-symbol" instead.
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

chucks
Posts: 6
Joined: Thu Sep 18, 2008 4:12 pm

Re: Please help my head get around 'deftype'

Post by chucks » Tue Sep 30, 2008 10:47 am

findinglisp wrote:
tlareywi wrote: As I mentioned earlier, I don't believe it is a pervasive problem. I don't think explicit type checking needs to be littered throughout most code. I'm focusing on module level entry points...APIs if you like.
Gotcha. That's not unreasonable in a few strategic locations.
You can always roll your own function signature tester. This is Lisp, after all.

chucks
Posts: 6
Joined: Thu Sep 18, 2008 4:12 pm

Re: Please help my head get around 'deftype'

Post by chucks » Tue Sep 30, 2008 10:50 am

findinglisp wrote:
TheGZeus wrote:...emacshas over on million lines of Elisp, in one namespace...
Proving that even dumb ideas are workable if you try hard enough. :lol:

Seriously, while it is a quite amazing feat if you think about it, I'm not sure anybody would argue that it's the right way to build a large system. What happens is that all emacs symbols end up getting prefixed with the name of the "package" they are associated with. For instance, everything in SLIME has a "slime-" prefix. Essentially, it's the equivalent of typing "package:symbol" all the time, except it's "package-symbol" instead.
It's fine until you want to merge in third-party components.

tlareywi
Posts: 11
Joined: Fri Sep 26, 2008 10:53 am
Location: Seattle, WA. USA

Re: Please help my head get around 'deftype'

Post by tlareywi » Tue Sep 30, 2008 12:10 pm

chucks wrote:You can always roll your own function signature tester. This is Lisp, after all.
Ah, I was wondering if something like that was doable. Can you briefly describe how this would be accomplished? I assume this would involve getting the AST representation for the function from the symbol referencing the function and then 'car'ing the argument list out, or something along those lines?

Thanks for all the responses thus far. I did take a look at the CLHS docs for deftype and I agree with the consensus that it can not directly express a function signature type. The ability to roll my own checkers would work fine though for the time being. I'm just unclear on how to get that level of introspection.

Cheers!

chucks
Posts: 6
Joined: Thu Sep 18, 2008 4:12 pm

Re: Please help my head get around 'deftype'

Post by chucks » Tue Sep 30, 2008 1:19 pm

tlareywi wrote:
chucks wrote:You can always roll your own function signature tester. This is Lisp, after all.
Ah, I was wondering if something like that was doable. Can you briefly describe how this would be accomplished? I assume this would involve getting the AST representation for the function from the symbol referencing the function and then 'car'ing the argument list out, or something along those lines?
It strongly depends on what you need. You mentioned stuffing an API function into a slot and wanting to know at stuff-time rather than at call-time whether a call would break.

But we don't yet know if you're concerned with blessing call exprs or with blessing the function's API signature (e.g., as compared to some declarative doc that you have available). If it's the former, you'll need to entype the call exprs and do some unification against the function's as-built signature. That'll be some significant work. If it's the latter, you'll use Equal and be done with it.

A third possibility is that you're also the function package's author. In that case, you could make the API functions, themselves, test arguments via a sample-call mechanism. And there might be more possibilities for doing what you want that haven't surfaced as yet (I'm just guessing as best I can with a couple minutes thought). Sounds like a bit of fun, though.

lnostdal
Posts: 20
Joined: Thu Jul 03, 2008 2:01 pm
Location: Skien, Norway
Contact:

Re: Please help my head get around 'deftype'

Post by lnostdal » Fri Oct 10, 2008 10:13 pm

tlareywi wrote:
findinglisp wrote: So, I'm still not sure what you are after, exactly:
  1. Documentation
  2. API enforcement
  3. Performance optimization
API enforcement is what I'm after in this case.
So I still don't see the benefit. In a C++/COM world, type mismatches cause big problems because operations are not type-checked. In Lisp, you can't accidentally add a string and an integer. When you try, you'll generate a condition and be dropped into the debugger. At that point you typically have a full stack trace and can quickly identify where things went wrong.
Consider a case where the thing being passed into the API is a function and the function is stored off in a slot. As long as you give lisp some function, no error condition will arise immediately. Instead, assuming the function provided does not accept the expected arguments, you wont get an error until the function is actually invoked from somewhere. This could be immediately, much later, never, etc. If your code, the API client, is not the one doing the invocation, this could be quite surprising and confusing.
the thing i've marked in bold in your text is the key point; how is this "thing" being defined/created?

* you are providing an api in form of something that accepts objects/functions(#1) of a certain kind
* but, at the same time, you are not providing an api for creating (these) objects/functions of that specific certain kind

Code: Select all

(defmacro mk-3d-callback ((x-sym y-sym z-sym) &body body)
  `(lambda (,x-sym ,y-sym ,z-sym)
     ,@body))
(export 'mk-3d-callback) ;; Export this to your users; screw the ones who do not use it.


(defun user-api (3d-callback)
  "3D-CALLBACK is a function created by MK-3D-CALLBACK."
  (let ((inner-x 1) (inner-y 2) (inner-z 3)) ;; You want to combine this internal data with behaviour+data from the outside.
    (funcall 3d-callback inner-x inner-y inner-z)))
(export 'user-api)



(defun the-dumb-users-code ()
  (let ((x 3) (y 2) (z 1))                              ;; This is data "from the outside"..
    (user-api (mk-3d-callback (inner-x inner-y inner-z) ;; We use the INNER- prefix to avoid name-clashes(#2).
                (values (+ inner-x x)                   ;; ..and here is the behaviour+data passed in.
                        (+ inner-y y)
                        (+ inner-z z))))))

(the-dumb-users-code)
=> 4, 4, 4

..this was typed in a hurry.. ..maybe someone has pointed something like this out already .. or maybe i've missed something

edit: ..note that you can control or enforce "form of" return data (from the outside, to the internals) in a similar way! (or in many ways really)

#1: functions really are objects you can pass around like anything else in lisp ..
#2: if you don't want to always type the argument-names: (defmacro mk-3d-callback ((&key (x-sym 'x) (y-sym 'y) (z-sym 'z)) &body body) ...) .. but this is something one usually only do when really needed

qbg
Posts: 64
Joined: Mon Jun 30, 2008 1:05 pm
Location: Minnesota

Re: Please help my head get around 'deftype'

Post by qbg » Sat Oct 11, 2008 12:55 pm

For functions you could do something like this:

Code: Select all

(defmacro defun* (name limited-lambda-list &body body)
  "Like defun, but saves type information.
   Special lambda lists (&optional, &key, etc.) not supported
   Lambda list treated like that of defmethod; ((integer a) b (integer c)) would
    yield variable a, b, and c where a&c are integers and b is of type T"
  (let ((ll (mapcar (lambda (itm) (if (consp itm) (cadr itm) itm)) limited-lambda-list))
        (types (mapcar (lambda (itm) (if (consp itm) (car itm) t)) limited-lambda-list))
        (args (gensym))
        (docstring (if (stringp (car body)) (car body)))
        (body (if (stringp (car body)) (cdr body) body)))
    `(defun ,name (&rest ,args)
       (declare (special *inspect-function-type*))
       ,docstring
       (cond
	((and (boundp '*inspect-function-type*) *inspect-function-type*)
         ',types)
        (t
         (destructuring-bind ,ll ,args
           ,@body))))))

(defun check-function (function &rest types)
  "Checks that a given function (defined with defun*) is of the correct type"
  (let ((ftypes (let ((*inspect-function-type* t))
                 (declare (special *inspect-function-type*))
                 (funcall function))))
    (when (or (/= (length ftypes) (length types))
              (notevery #'subtypep types ftypes))
      (error "Function ~a is typed ~a; expected ~a" function ftypes types))))
Downsides:
1) check-function will only work correctly when the given function behaves like a function that defun* would define.
2) The lambda-list for the function defun* creates isn't helpful
3) As mentioned in the code, you (currently) don't get &option, &rest, &aux, &key, etc.
4) The functions created currently don't check to see that the arguments they are given match their declared types

Side note: Having functions bound to a symbol and knowing the symbol would fix problems 1&2 as you could store the information in the symbol's plist then.

My advice: Trusting the programmers who are going to be using your functions is probably the better option.

tlareywi
Posts: 11
Joined: Fri Sep 26, 2008 10:53 am
Location: Seattle, WA. USA

Re: Please help my head get around 'deftype'

Post by tlareywi » Sun Oct 12, 2008 8:36 pm

The above strategy makes sense to me and seems quite robust. That's basically what I'm after. Thanks for all the responses!

Post Reply