Lisp HUG Maillist Archive

partitioning keyword arguments

Hello all,

This is a slightly more general Lisp oriented question, so perhaps I
should have sent it off to c.l.l, but all my examples are going to be
CAPI based, and I bet someone here has had to deal with these issues
before.

There is a macro capi:with-dialog-results which allows for (on OS X)
dialogs which are modal per window (and not for the whole
application.) For instance,

(capi:with-dialog-results (str okp)
    (capi:prompt-for-string "Enter a string")
  (when okp
    (print str)))

Now this is just syntactic sugar for (essentially) the following:

(capi:prompt-for-string
  "Enter a string"
  :continuation
  #'(lambda (str okp)
        (when okp
           (print string))))

so the point to notice is that the prompting function accepts a
:continuation keyword argument. Now prompt-for-string also accepts a
:text keyword argument specifiying initial-text for the prompt.

Now I'm trying to do something similar. Let's say I have
username/password prompt (i.e., an interface with text-input-panes for
username/password). I can display a prompt using capi:popup-confirmer,
like this:

(capi:popup-confirmer
   (make-instance
    'password-prompt
    :username username
    :password password)
    "Enter username and password:"
    :value-function #'combine-username-password))

and I'd like to wrap this up in a function, e.g.,

(defun prompt-for-username-and-password (&key username password)
  (capi:popup-confirmer
     (make-instance
      'password-prompt
      :username username
      :password password)
      "Enter username and password:"
      :value-function #'combine-username-password)))

but I'd also like to be able to use it in capi:with-dialog-results,
and so I need to have a continuation keyword:

(defun prompt-for-username-and-password (&key username password continuation)
  (capi:popup-confirmer
     (make-instance
      'password-prompt
      :username username
      :password password)
      "Enter username and password:"
      :value-function #'combine-username-password)
   :continuation continuation))

but at this point, it's clear that there a number of the
popup-confirmer args that I might want to use (e.g., owner, to make
sure that the popup is on the correct window.) Ideally I'd like to
have all these args mixed together, so as to be able to write

(defun prompt-for-username-and-password (&rest initargs &key username password)
  (apply #'capi:popup-confirmer
     (make-instance
      'password-prompt
      :username username
      :password password)
      "Enter username and password:"
      :value-function #'combine-username-password)
  initargs-w/o-username-and-password))

That is, I'd like to grab a few keyword arguments myself, and then to
send the rest of them on to the popup-confirmer. I need this behavior
so that my prompting functions can be used in a consistent manner as
the standard capi prompts.

Now, I realize that although capi:prompt-for-string has a
:continuation argument, there is still :popup-args (and
popup-confirmer /also/ accepts a :continuation argument) so things
like this can be written:

(capi:prompt-for-string
 "enter a string"
 :continuation #'(lambda (&rest args)
                   (print (cons 'prompt-for-string args)))
 :popup-args (list :continuation #'(lambda (&rest args)
                                     (print (cons 'popup-confirmer args)))))

and the result is that the continuation mentioning prompt-for-string
is invoked. So perhaps the best choice is provide :popup-args and
:continuation for my prompting functions and to hope for the best?

Thanks in advance!

-- 
=====================
Joshua Taylor
tayloj@rpi.edu

"A lot of good things went down one time,
     back in the goodle days."
               John Hartford


Re: partitioning keyword arguments

On 4/7/07, Taylor, Joshua <tayloj@rpi.edu> wrote:
> Hello all,
>
> This is a slightly more general Lisp oriented question, so perhaps I
> should have sent it off to c.l.l, but all my examples are going to be
> CAPI based, and I bet someone here has had to deal with these issues
> before.
>
> There is a macro capi:with-dialog-results which allows for (on OS X)
> dialogs which are modal per window (and not for the whole
> application.) For instance,
>
> (capi:with-dialog-results (str okp)
>     (capi:prompt-for-string "Enter a string")
>   (when okp
>     (print str)))
>
> Now this is just syntactic sugar for (essentially) the following:
>
> (capi:prompt-for-string
>   "Enter a string"
>   :continuation
>   #'(lambda (str okp)
>         (when okp
>            (print string))))
>
> so the point to notice is that the prompting function accepts a
> :continuation keyword argument. Now prompt-for-string also accepts a
> :text keyword argument specifiying initial-text for the prompt.
>
> Now I'm trying to do something similar. Let's say I have
> username/password prompt (i.e., an interface with text-input-panes for
> username/password). I can display a prompt using capi:popup-confirmer,
> like this:
>
> (capi:popup-confirmer
>    (make-instance
>     'password-prompt
>     :username username
>     :password password)
>     "Enter username and password:"
>     :value-function #'combine-username-password))
>
> and I'd like to wrap this up in a function, e.g.,
>
> (defun prompt-for-username-and-password (&key username password)
>   (capi:popup-confirmer
>      (make-instance
>       'password-prompt
>       :username username
>       :password password)
>       "Enter username and password:"
>       :value-function #'combine-username-password)))
>
> but I'd also like to be able to use it in capi:with-dialog-results,
> and so I need to have a continuation keyword:
>
> (defun prompt-for-username-and-password (&key username password continuation)
>   (capi:popup-confirmer
>      (make-instance
>       'password-prompt
>       :username username
>       :password password)
>       "Enter username and password:"
>       :value-function #'combine-username-password)
>    :continuation continuation))
>
> but at this point, it's clear that there a number of the
> popup-confirmer args that I might want to use (e.g., owner, to make
> sure that the popup is on the correct window.) Ideally I'd like to
> have all these args mixed together, so as to be able to write
>
> (defun prompt-for-username-and-password (&rest initargs &key username password)
>   (apply #'capi:popup-confirmer
>      (make-instance
>       'password-prompt
>       :username username
>       :password password)
>       "Enter username and password:"
>       :value-function #'combine-username-password)
>   initargs-w/o-username-and-password))
>
> That is, I'd like to grab a few keyword arguments myself, and then to
> send the rest of them on to the popup-confirmer. I need this behavior
> so that my prompting functions can be used in a consistent manner as
> the standard capi prompts.
>
> Now, I realize that although capi:prompt-for-string has a
> :continuation argument, there is still :popup-args (and
> popup-confirmer /also/ accepts a :continuation argument) so things
> like this can be written:
>
> (capi:prompt-for-string
>  "enter a string"
>  :continuation #'(lambda (&rest args)
>                    (print (cons 'prompt-for-string args)))
>  :popup-args (list :continuation #'(lambda (&rest args)
>                                      (print (cons 'popup-confirmer args)))))
>
> and the result is that the continuation mentioning prompt-for-string
> is invoked. So perhaps the best choice is provide :popup-args and
> :continuation for my prompting functions and to hope for the best?

I should have clarified here what I mean to provide both. I would do
the following:

(defun prompt-for-username-and-password
       (&key username password continuation popup-args)
  (apply #'capi:popup-confirmer
         (make-instance
          'password-prompt
          :username username
          :password password)
         "Enter username and password:"
         :value-function #'combine-username-password
         :continuation continuation
         popup-args))

So that the keyword argument continuation appears before any
continuation present in the popup-args (I'll have to check to make
sure that the earlier keywords are taken before later ones in the
event of duplicates.) and I get the behavior I want, I think.

> Thanks in advance!


-- 
=====================
Joshua Taylor
tayloj@rpi.edu

"A lot of good things went down one time,
     back in the goodle days."
               John Hartford


Re: partitioning keyword arguments

On 7 Apr 2007, at 21:18, Taylor, Joshua wrote:

>
> That is, I'd like to grab a few keyword arguments myself, and then to
> send the rest of them on to the popup-confirmer. I need this behavior
> so that my prompting functions can be used in a consistent manner as
> the standard capi prompts.

I'm not sure if it's a complete solution, but you can usually invent  
what you need based on using &allow-other-keys and &rest arguments.   
The combination lets you pick off keywords you care about while also  
getting the whole lot of args which you can pass on to other people.   
If you have to call things which don't allow extra keywords you may  
need a wrapper to select the good ones.


Re: partitioning keyword arguments

On 4/8/07, Tim Bradshaw <tfb@cley.com> wrote:
> On 7 Apr 2007, at 21:18, Taylor, Joshua wrote:
>
> > That is, I'd like to grab a few keyword arguments myself, and then to
> > send the rest of them on to the popup-confirmer. I need this behavior
> > so that my prompting functions can be used in a consistent manner as
> > the standard capi prompts.
>
> I'm not sure if it's a complete solution, but you can usually invent
> what you need based on using &allow-other-keys and &rest arguments.
> The combination lets you pick off keywords you care about while also
> getting the whole lot of args which you can pass on to other people.
> If you have to call things which don't allow extra keywords you may
> need a wrapper to select the good ones.

Ah, your mention of &allow-other-keys caused me to investigate, and
I've learned something very interesting:

CL-USER 7 > (defun foo (&key x)
              x)
Foo

CL-USER 8 > (foo :x 7)
7

CL-USER 9 > (foo :x 7 :y 8)

Error: Unexpected keyword :Y which is not one of (:X).
  1 (continue) Ignore the unknown keyword :Y.
  2 (abort) Return to level 0.
  3 Return to top loop level 0.

Type :b for backtrace, :c <option number> to proceed,  or :? for other options

CL-USER 10 : 1 > :a

CL-USER 11 > (foo :x 7 :y 8 :allow-other-keys t)
7

CL-USER 12 > (foo :allow-other-keys t :x 7 :y 8)
7

Now, this allows me to do exactly what I want to do, but I have to
wonder whether this is considered poor style. I can see that if
misused, the possibilities for obfuscation are huge, but perhaps used
carefully this is just such a case as it was designed for? I think I'd
end up writing something like:

(defun prompt-for-password
       (&rest args &key (username "") (password "") &allow-other-keys)
  (apply #'capi:popup-confirmer
         (make-instance 'password-prompt
                        :username username
                        :password password)
         "message"
         :allow-other-keys t
         args))

which would make my prompt-for-password compatible with with the
capi:with-dialog-results as the continuation would be passed
correctly, and all would work correctly, so long as none of my keyword
arguments conflict with those that capi:popup-confirmer accepts.

Any thoughts on style, or a better way to do this?

Thanks!

-- 
=====================
Joshua Taylor
tayloj@rpi.edu

"A lot of good things went down one time,
     back in the goodle days."
               John Hartford


Re: partitioning keyword arguments

Ah, great! Now I can do what works /and/ do it in good conscience!

Thanks!

On 4/8/07, Brian Downing <bdowning@lavos.net> wrote:
> On Sun, Apr 08, 2007 at 05:49:08PM -0400, Taylor, Joshua wrote:
> > Now, this allows me to do exactly what I want to do, but I have to
> > wonder whether this is considered poor style. I can see that if
> > misused, the possibilities for obfuscation are huge, but perhaps used
> > carefully this is just such a case as it was designed for? I think I'd
> > end up writing something like:
> >
> > (defun prompt-for-password
> >       (&rest args &key (username "") (password "") &allow-other-keys)
> >  (apply #'capi:popup-confirmer
> >         (make-instance 'password-prompt
> >                        :username username
> >                        :password password)
> >         "message"
> >         :allow-other-keys t
> >         args))
> >
> > which would make my prompt-for-password compatible with with the
> > capi:with-dialog-results as the continuation would be passed
> > correctly, and all would work correctly, so long as none of my keyword
> > arguments conflict with those that capi:popup-confirmer accepts.
> >
> > Any thoughts on style, or a better way to do this?
>
> I believe this usage is exactly why the :ALLOW-OTHER-KEYS T behavior
> was placed in the language, and the CLHS would seem to corroborate this:
>
> http://www.lispworks.com/documentation/HyperSpec/Body/03_daf.htm
>
> | As an example of the use of &allow-other-keys and :allow-other-keys,
> | consider a function that takes two named arguments of its own and also
> | accepts additional named arguments to be passed to make-array:
> |
> | (defun array-of-strings (str dims &rest named-pairs
> |                          &key (start 0) end &allow-other-keys)
> |   (apply #'make-array dims
> |          :initial-element (subseq str start end)
> |          :allow-other-keys t
> |          named-pairs))
> |
> | This function takes a string and dimensioning information and returns
> | an array of the specified dimensions, each of whose elements is the
> | specified string. However, :start and :end named arguments may be used
> | to specify that a substring of the given string should be used. In
> | addition, the presence of &allow-other-keys in the lambda list
> | indicates that the caller may supply additional named arguments; the
> | rest parameter provides access to them. These additional named
> | arguments are passed to make-array. The function make-array normally
> | does not allow the named arguments :start and :end to be used, and an
> | error should be signaled if such named arguments are supplied to
> | make-array. However, the presence in the call to make-array of the
> | named argument :allow-other-keys with a true value causes any
> | extraneous named arguments, including :start and :end, to be acceptable
> | and ignored.
>
> -bcd
>


-- 
=====================
Joshua Taylor
tayloj@rpi.edu

"A lot of good things went down one time,
     back in the goodle days."
               John Hartford


Updated at: 2020-12-10 08:46 UTC