From: jimka
Subject: programmatically determine if argument is list compatible to a given lambda list
Date: 
Message-ID: <1177789740.400482.90040@n76g2000hsh.googlegroups.com>
I'd like to write a function which at run-time will determine whether
a given list
of arguments is compatible to a given lambda list.
For example.

(callable-as '(3) '(x))
  ==> return t because you CAN apply (lambda (x) nil) to '(3)

(callable-as '(3) '(x &rest y))
  ==> return t because you CAN apply (lambda (x &rest y) nil) to '(3)

(callable-as '(3) '(x y))
  ==> return nil because you CANNOT apply (lambda (x y) nil) to '(3)


Here is my attempt which fails miserably.

(defun callable-as (arg-list lambda-list)
  (unwind-protect (apply `(lambda ,lambda-list
			    (return-from callable-as t))
			   arg-list)
      (return-from callable-as nil)))

From: Pascal Costanza
Subject: Re: programmatically determine if argument is list compatible to a given lambda list
Date: 
Message-ID: <59hq4hF2le6snU1@mid.individual.net>
jimka wrote:
> I'd like to write a function which at run-time will determine whether
> a given list
> of arguments is compatible to a given lambda list.
> For example.
> 
> (callable-as '(3) '(x))
>   ==> return t because you CAN apply (lambda (x) nil) to '(3)
> 
> (callable-as '(3) '(x &rest y))
>   ==> return t because you CAN apply (lambda (x &rest y) nil) to '(3)
> 
> (callable-as '(3) '(x y))
>   ==> return nil because you CANNOT apply (lambda (x y) nil) to '(3)
> 
> 
> Here is my attempt which fails miserably.
> 
> (defun callable-as (arg-list lambda-list)
>   (unwind-protect (apply `(lambda ,lambda-list
> 			    (return-from callable-as t))
> 			   arg-list)
>       (return-from callable-as nil)))

...but it's quite close:

(defun callable-as (arg-list lambda-list)
   (unwind-protect (apply (coerce `(lambda ,lambda-list
			            (return-from callable-as t))
                            'function)
			   arg-list)
       (return-from callable-as nil)))

[untested]

Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: jimka
Subject: Re: programmatically determine if argument is list compatible to a given lambda list
Date: 
Message-ID: <1177793670.558982.152980@e65g2000hsc.googlegroups.com>
if i try that in sbcl, this is what happens.  (callable-as '(3) '(x
y))

invalid number of arguments: 1
   [Condition of type SB-INT:SIMPLE-PROGRAM-ERROR]

Restarts:
  0: [ABORT-REQUEST] Abort handling SLIME request.
  1: [TERMINATE-THREAD] Terminate this thread (#<THREAD "repl-
thread" {10010910F1}>)

Backtrace:
  0: ((LAMBDA (X Y)) #<unavailable argument>)
  1: (CALLABLE-AS (3) (X Y))
  2: (SB-INT:EVAL-IN-LEXENV (CALLABLE-AS (QUOTE (3)) (QUOTE (X Y)))
#<NULL-LEXENV>)
  3: (SWANK::EVAL-REGION "(callable-as '(3) '(x y))
" T)


On Apr 28, 10:46 pm, Pascal Costanza <····@p-cos.net> wrote:
> jimka wrote:
> > I'd like to write a function which at run-time will determine whether
> > a given list
> > of arguments is compatible to a given lambda list.
> > For example.
>
> > (callable-as '(3) '(x))
> >   ==> return t because you CAN apply (lambda (x) nil) to '(3)
>
> > (callable-as '(3) '(x &rest y))
> >   ==> return t because you CAN apply (lambda (x &rest y) nil) to '(3)
>
> > (callable-as '(3) '(x y))
> >   ==> return nil because you CANNOT apply (lambda (x y) nil) to '(3)
>
> > Here is my attempt which fails miserably.
>
> > (defun callable-as (arg-list lambda-list)
> >   (unwind-protect (apply `(lambda ,lambda-list
> >                        (return-from callable-as t))
> >                       arg-list)
> >       (return-from callable-as nil)))
>
> ...but it's quite close:
>
> (defun callable-as (arg-list lambda-list)
>    (unwind-protect (apply (coerce `(lambda ,lambda-list
>                                     (return-from callable-as t))
>                             'function)
>                            arg-list)
>        (return-from callable-as nil)))
>
> [untested]
>
> Pascal
>
> --
> My website:http://p-cos.net
> Common Lisp Document Repository:http://cdr.eurolisp.org
> Closer to MOP & ContextL:http://common-lisp.net/project/closer/
From: Pascal Costanza
Subject: Re: programmatically determine if argument is list compatible to a given lambda list
Date: 
Message-ID: <59hrr0F2k1qhkU1@mid.individual.net>
jimka wrote:
> if i try that in sbcl, this is what happens.  (callable-as '(3) '(x
> y))
> 
> invalid number of arguments: 1
>    [Condition of type SB-INT:SIMPLE-PROGRAM-ERROR]
> 
> Restarts:
>   0: [ABORT-REQUEST] Abort handling SLIME request.
>   1: [TERMINATE-THREAD] Terminate this thread (#<THREAD "repl-
> thread" {10010910F1}>)
> 
> Backtrace:
>   0: ((LAMBDA (X Y)) #<unavailable argument>)
>   1: (CALLABLE-AS (3) (X Y))
>   2: (SB-INT:EVAL-IN-LEXENV (CALLABLE-AS (QUOTE (3)) (QUOTE (X Y)))
> #<NULL-LEXENV>)
>   3: (SWANK::EVAL-REGION "(callable-as '(3) '(x y))
> " T)

Ah, of course. You should use handler-case, not unwind-protect:

(defun callable-as (arg-list lambda-list)
   (handler-case (apply (coerce `(lambda ,lambda-list
                                    (return-from callable-as t))
                            'function)
                           arg-list)
     (program-error () nil)))

You should probably also make sure that the safety settings are correct 
at the time when coerce is executed. (See 3.5.1.1 in the HyperSpec.)

Finally, the lambda expression is embedded in the null lexical 
environment, so it doesn't see the 'callable-as block. But it should be 
good enough to just return 't from the lambda expression - you don't 
need to be specific about where to return from.

Finally, it might make sense to consider a macro version - if the lambda 
lists are quoted constants all of the time, a macro can be more 
efficient. (Or you could define a compiler macro.)


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: jimka
Subject: Re: programmatically determine if argument is list compatible to a given lambda list
Date: 
Message-ID: <1177795682.733953.26230@h2g2000hsg.googlegroups.com>
ahh, thanks... yes i also considered a macro... but i do not think it
works for what i'm doing.
Although i may go back and rethink it.

It looks like the COERSE functions causes sbcl to print a bunch of
uninteresting
stuff to stdout.


TYPE> (callable-as '(3) '(x))
;     (RETURN-FROM TYPE::CALLABLE-AS T)
;
; caught ERROR:
;   return for unknown block: CALLABLE-AS

;     #'(LAMBDA (TYPE::X) (RETURN-FROM TYPE::CALLABLE-AS T))
;
; caught STYLE-WARNING:
;   The variable X is defined but never used.
;
; compilation unit finished
;   caught 1 ERROR condition
;   caught 1 STYLE-WARNING condition
NIL
TYPE>
From: jimka
Subject: Re: programmatically determine if argument is list compatible to a given lambda list
Date: 
Message-ID: <1177797821.700224.220320@q75g2000hsh.googlegroups.com>
even after getting rid of the lexical binding problem, i'd like to
tell sbcl that i'm not interesting in
seeing the other comments.  any ideas?



(defun callable-as (arg-list lambda-list)
  (declare (sb-ext:muffle-conditions sb-ext:compiler-note))
  (handler-case (apply (coerce `(lambda ,lambda-list
				  t)
			       'function)
                           arg-list)
     (program-error () nil)))

TYPE> (callable-as '(3) '(x))
;     #'(LAMBDA (TYPE::X) T)
;
; caught STYLE-WARNING:
;   The variable X is defined but never used.
;
; compilation unit finished
;   caught 1 STYLE-WARNING condition
T
TYPE>
From: Pascal Costanza
Subject: Re: programmatically determine if argument is list compatible to a given lambda list
Date: 
Message-ID: <59i8p8F2jhpv1U1@mid.individual.net>
jimka wrote:
> even after getting rid of the lexical binding problem, i'd like to
> tell sbcl that i'm not interesting in
> seeing the other comments.  any ideas?
> 
> 
> 
> (defun callable-as (arg-list lambda-list)
>   (declare (sb-ext:muffle-conditions sb-ext:compiler-note))
>   (handler-case (apply (coerce `(lambda ,lambda-list
> 				  t)
> 			       'function)
>                            arg-list)
>      (program-error () nil)))
> 
> TYPE> (callable-as '(3) '(x))
> ;     #'(LAMBDA (TYPE::X) T)
> ;
> ; caught STYLE-WARNING:
> ;   The variable X is defined but never used.
> ;
> ; compilation unit finished
> ;   caught 1 STYLE-WARNING condition
> T
> TYPE>

Hm, maybe:

(defun callable-as-p (arg-list lambda-list)
   (handler-case
       (apply
         (handler-bind ((style-warning #'muffle-warning))
           (locally (declare (optimize (safety 3)))
             (coerce `(lambda ,lambda-list t) 'function)))
         arg-list)
     (program-error () nil)))


The coercion happens at runtime, so the sb-ext:muffle-conditions 
declaration probably doesn't have any effect here, because the latter 
only applies at compile time.


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Alan Crowe
Subject: Re: programmatically determine if argument is list compatible to  a given lambda list
Date: 
Message-ID: <868xcbt249.fsf@cawtech.freeserve.co.uk>
Pascal Costanza <··@p-cos.net> writes:

> jimka wrote:
> > even after getting rid of the lexical binding problem, i'd like to
> > tell sbcl that i'm not interesting in
> > seeing the other comments.  any ideas?
> > 

> Hm, maybe:
> 
> (defun callable-as-p (arg-list lambda-list)
>    (handler-case
>        (apply
>          (handler-bind ((style-warning #'muffle-warning))
>            (locally (declare (optimize (safety 3)))
>              (coerce `(lambda ,lambda-list t) 'function)))
>          arg-list)
>      (program-error () nil)))
> 

Seeing Pascal's handler for program-error made me think of
the third value from COMPILE

CL-USER> (let ((*error-output* (make-broadcast-stream)))
           (compile nil '(lambda() ((lambda(&key a b )nil) :a 3 ))))
#<Function "LAMBDA NIL" {48A60019}>
T
NIL <-- OK so error=nil

CL-USER> (let ((*error-output* (make-broadcast-stream)))
           (compile nil '(lambda() ((lambda(&key a b )nil) :a 3 :c 3))))
#<Function "LAMBDA NIL" {48AA49D1}>
T
T <-- :c not declared to error=T

Alan Crowe
Edinburgh
Scotland
From: Pascal Costanza
Subject: Re: programmatically determine if argument is list compatible to  a given lambda list
Date: 
Message-ID: <59kl37F2kv4nuU1@mid.individual.net>
Alan Crowe wrote:
> Pascal Costanza <··@p-cos.net> writes:
> 
>> jimka wrote:
>>> even after getting rid of the lexical binding problem, i'd like to
>>> tell sbcl that i'm not interesting in
>>> seeing the other comments.  any ideas?
>>>
> 
>> Hm, maybe:
>>
>> (defun callable-as-p (arg-list lambda-list)
>>    (handler-case
>>        (apply
>>          (handler-bind ((style-warning #'muffle-warning))
>>            (locally (declare (optimize (safety 3)))
>>              (coerce `(lambda ,lambda-list t) 'function)))
>>          arg-list)
>>      (program-error () nil)))
>>
> 
> Seeing Pascal's handler for program-error made me think of
> the third value from COMPILE
> 
> CL-USER> (let ((*error-output* (make-broadcast-stream)))
>            (compile nil '(lambda() ((lambda(&key a b )nil) :a 3 ))))
> #<Function "LAMBDA NIL" {48A60019}>
> T
> NIL <-- OK so error=nil
> 
> CL-USER> (let ((*error-output* (make-broadcast-stream)))
>            (compile nil '(lambda() ((lambda(&key a b )nil) :a 3 :c 3))))
> #<Function "LAMBDA NIL" {48AA49D1}>
> T
> T <-- :c not declared to error=T

I specifically went for coerce instead of compile because coerce may 
give you interpreted code while compile is required to do at least 
minimal compilation. Since these are one-off functions by definition, 
interpreted code is more efficient in this case - the overhead of 
compilation is not justified here, especially in implementations with 
slow compilers (like SBCL).

Having said that, I have to admit that I would have probably overlooked 
the additional return values of compile anway, so thanks for the hint. ;)


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Pascal Bourguignon
Subject: Re: programmatically determine if argument is list compatible to a given lambda list
Date: 
Message-ID: <877irv7zxq.fsf@thalassa.lan.informatimago.com>
jimka <·····@rdrop.com> writes:

> I'd like to write a function which at run-time will determine whether
> a given list
> of arguments is compatible to a given lambda list.
> For example.
>
> (callable-as '(3) '(x))
>   ==> return t because you CAN apply (lambda (x) nil) to '(3)
>
> (callable-as '(3) '(x &rest y))
>   ==> return t because you CAN apply (lambda (x &rest y) nil) to '(3)
>
> (callable-as '(3) '(x y))
>   ==> return nil because you CANNOT apply (lambda (x y) nil) to '(3)
>
>
> Here is my attempt which fails miserably.
>
> (defun callable-as (arg-list lambda-list)
>   (unwind-protect (apply `(lambda ,lambda-list
> 			    (return-from callable-as t))
> 			   arg-list)
>       (return-from callable-as nil)))


This is dangerous.

What if the lambda list is: 

  (x &optional (y (delete-file "/some/important/file"))) 

to not write anything worse ?


I would rather parse the lambda list and check the parameters
myself.  See: PARSE-LAMBDA-LIST in:
http://darcs.informatimago.com/darcs/public/lisp/common-lisp/source-form.lisp

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

COMPONENT EQUIVALENCY NOTICE: The subatomic particles (electrons,
protons, etc.) comprising this product are exactly the same in every
measurable respect as those used in the products of other
manufacturers, and no claim to the contrary may legitimately be
expressed or implied.
From: Pascal Costanza
Subject: Re: programmatically determine if argument is list compatible to a given lambda list
Date: 
Message-ID: <59k9mjF2jsfh6U1@mid.individual.net>
Pascal Bourguignon wrote:
> jimka <·····@rdrop.com> writes:
> 
>> I'd like to write a function which at run-time will determine whether
>> a given list
>> of arguments is compatible to a given lambda list.
>> For example.
>>
>> (callable-as '(3) '(x))
>>   ==> return t because you CAN apply (lambda (x) nil) to '(3)
>>
>> (callable-as '(3) '(x &rest y))
>>   ==> return t because you CAN apply (lambda (x &rest y) nil) to '(3)
>>
>> (callable-as '(3) '(x y))
>>   ==> return nil because you CANNOT apply (lambda (x y) nil) to '(3)
>>
>>
>> Here is my attempt which fails miserably.
>>
>> (defun callable-as (arg-list lambda-list)
>>   (unwind-protect (apply `(lambda ,lambda-list
>> 			    (return-from callable-as t))
>> 			   arg-list)
>>       (return-from callable-as nil)))
> 
> 
> This is dangerous.
> 
> What if the lambda list is: 
> 
>   (x &optional (y (delete-file "/some/important/file"))) 
> 
> to not write anything worse ?

You can call any function with (delete-file "/bla") as one of its 
parameters. ?!?


Pascal

-- 
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Pascal Bourguignon
Subject: Re: programmatically determine if argument is list compatible to a given lambda list
Date: 
Message-ID: <87mz0q7toq.fsf@thalassa.lan.informatimago.com>
Pascal Costanza <··@p-cos.net> writes:

> Pascal Bourguignon wrote:
>> jimka <·····@rdrop.com> writes:
>>
>>> I'd like to write a function which at run-time will determine whether
>>> a given list
>>> of arguments is compatible to a given lambda list.
>>> For example.
>>>
>>> (callable-as '(3) '(x))
>>>   ==> return t because you CAN apply (lambda (x) nil) to '(3)
>>>
>>> (callable-as '(3) '(x &rest y))
>>>   ==> return t because you CAN apply (lambda (x &rest y) nil) to '(3)
>>>
>>> (callable-as '(3) '(x y))
>>>   ==> return nil because you CANNOT apply (lambda (x y) nil) to '(3)
>>>
>>>
>>> Here is my attempt which fails miserably.
>>>
>>> (defun callable-as (arg-list lambda-list)
>>>   (unwind-protect (apply `(lambda ,lambda-list
>>> 			    (return-from callable-as t))
>>> 			   arg-list)
>>>       (return-from callable-as nil)))
>>
>>
>> This is dangerous.
>>
>> What if the lambda list is: 
>>
>>   (x &optional (y (delete-file "/some/important/file"))) 
>>
>> to not write anything worse ?
>
> You can call any function with (delete-file "/bla") as one of its
> parameters. ?!?

The point is that you can put side effects in the lambda-list:

C/USER[32]> (defvar *x* 0)
*X*
C/USER[33]> (defun f (x &optional (y (incf *x*))) (values x y))
F
C/USER[34]> (f 1)
1 ;
1
C/USER[35]> (f 1)
1 ;
2
C/USER[36]> (f 1)
1 ;
3
C/USER[37]> *x*
3
C/USER[38]> 


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"Klingon function calls do not have "parameters" -- they have
"arguments" and they ALWAYS WIN THEM."