[comp.emacs] GNU Emacs special form "interactive"

ellis@ultra.dec.com (David Ellis) (12/31/88)

To have a GNU Emacs command prompt for string argument input using a fixed 
prompt string, the command should include in its body the form

	(interactive "s(This is a prompt string) : ")

The given prompt string is printed in the minibuffer, and the user's string
response typed in will be assigned as the value of the argument to the command.

What I'd like to do is have a function taking an argument that gets
inserted into a prompt string.  If I call (foo "type A"), I want
to have a prompt string that looks like

	(This is a type A prompt string) :

I can do this if I can get the special form "interactive" to take as its
argument a string-valued expression in place of a specific string, for
example

	(interactive (format "s(This is a %s prompt string) : " foo-arg))

Unfortunately, GNU Emacs rewards this effort with the error message

	Wrong type argument: listp, "s(This is a Type A prompt string) : "

saying that "interactive" expects a list (huh?) but has been given a string.

Trying a macro expansion within the scope of the "interactive" form fails
in a similar way.

I can do essentially the same thing via the function "read-string", but
is it possible to do this through the form "interactive"?

----
David Ellis
Digital Equipment Corporation -- BXB1-1/D03 
85 Swanson Road, Boxboro MA 01719 -- (617) 264-5073
Usenet:  {ucbvax,allegra,decvax}!decwrl!ultra.dec.com!ellis
ARPA:    ellis%ultra.dec@decwrl.dec.com

mesard@bbn.com (Wayne Mesard) (12/31/88)

In article <8812301512.AA10827@decwrl.dec.com> ellis@ultra.dec.com (David Ellis) writes:
>What I'd like to do is have a function taking an argument that gets
>inserted into a prompt string.

>I can do this if I can get the special form "interactive" to take as its
>argument a string-valued expression in place of a specific string, for
>example
>
>	(interactive (format "s(This is a %s prompt string) : " foo-arg))
>
>Unfortunately, GNU Emacs rewards this effort with the error message
>
>	Wrong type argument: listp, "s(This is a Type A prompt string) : "
>
>saying that "interactive" expects a list (huh?) but has been given a string.

As the docs for interactive say, if the argument is not a literal
string, it is assumed to be lisp code which, when evaluated, will
produce a LIST containing an element to be bound to each of the function
parameters.

Since interactive acts as a declaration, its args are examined at
compile time.  (Thus your attempt to use format would not produce the
desired result even if you had wrapped a (list ...) call around the
format.)  The following prompts for a single argument to a function
placing a timestamp in the prompt.

 (interactive
  (list (read-string (format "The time is %s: " (current-time-string)))))

In summary, if the arg to interactive is a literal string, it gets used
directly by the code which does the argument binding.  Otherwise, it is
evaluated and must produce a list, each element of which is bound to one
of the functions args.

-- 
void Wayne_Mesard();   Mesard@BBN.COM   Bolt Beranek and Newman, Cambridge, MA

pierson@mist (Dan Pierson) (12/31/88)

In article <8812301512.AA10827@decwrl.dec.com>, ellis@ultra (David Ellis) writes:
>	(interactive (format "s(This is a %s prompt string) : " foo-arg))
>
>Unfortunately, GNU Emacs rewards this effort with the error message
>
>	Wrong type argument: listp, "s(This is a Type A prompt string) : "
>
>saying that "interactive" expects a list (huh?) but has been given a string.

From ^H-f interactive:

   "The argument of  interactive  is usually a string ...
     ...
    If the argument is not a string, it is evaluated to get a list of
     arguments to pass to the function."

You might try:

    (interactive (list (read-string (format "This is a %s prompt string: "
                                             foo-arg))))
-- 
                                            dan

In real life: Dan Pierson, Encore Computer Corporation, Research
UUCP: {talcott,linus,necis,decvax}!encore!pierson
Internet: pierson@multimax.encore.com

Ram-Ashwin@cs.yale.edu (Ashwin Ram) (12/31/88)

In article <8812301512.AA10827@decwrl.dec.com>, ellis@ultra.dec.com (David Ellis) writes:
> To have a GNU Emacs command prompt for string argument input using a fixed 
> prompt string, the command should include in its body the form
> 	(interactive "s(This is a prompt string) : ")
> 
> What I'd like to do is have a function taking an argument that gets
> inserted into a prompt string. [...]
> I can do this if I can get the special form "interactive" to take as its
> argument a string-valued expression in place of a specific string [...]
> 	(interactive (format "s(This is a %s prompt string) : " foo-arg))
> 
> Unfortunately, GNU Emacs rewards this effort with the error message
> 	Wrong type argument: listp, "s(This is a Type A prompt string) : "
> saying that "interactive" expects a list (huh?) but has been given a string.

The "interactive" function expects either a string or an s-exp to evaluate to
get a list of arguments to pass to the function.  From the documentation for
"interactive":

    The argument of  interactive  is usually a string containing a code letter
     followed by a prompt.  (Some code letters do not use I/O to get
     the argument and do not need prompts.)  To prompt for multiple arguments,
     give a code letter, its prompt, a newline, and another code letter, etc.
    If the argument is not a string, it is evaluated to get a list of
     arguments to pass to the function.

For your example, you should use:

    (interactive (list (read-string (format "s(This is a %s prompt string): " foo-arg))))

The argument to interactive is responsible for returning a list of arguments
for the function, NOT another interactive spec.  See docs for more info.

Note that foo-arg cannot be one of the arguments to the function since if
that function is called interactively, its arguments will not yet be defined.
In other words,

    (define foo (foo-arg)
       (interactive (list (read-string (format "s(This is a %s prompt string): " foo-arg))))      
       ...)

is meaningless and will give you a "Symbol's value as variable is void:
foo-arg" error (unless of course foo-arg is already defined in a larger
context outside foo).

-- Ashwin.

rlk@think.com (Robert Krawitz) (01/04/89)

In article <8812301512.AA10827@decwrl.dec.com>, ellis@ultra (David Ellis) writes:
]What I'd like to do is have a function taking an argument that gets
]inserted into a prompt string.  If I call (foo "type A"), I want
]to have a prompt string that looks like
]
]	(This is a type A prompt string) :

If you call foo normally through Lisp, the interactive form is
bypassed, since the arguments are provided.  The interactive form is
only consulted when a function is called interactively (through the
keyboard or with call-interactively).  You can check how a function
has been called with the function interactive-p.

What you can do is have your function check to see if it was called
interactively, and use read-string if it was not.

What if you want a function that looks at some state at run time to
decide how to get its arguments?  You can't do this quite so easily.
However, if the "argument" to interactive is not a string, it is
evaluated to return a list of the arguments to the function.  You can
use read-string to get the arguments, and convert them to the
appropriate types.  This is not as easy as interactive, but it will
work.

]I can do this if I can get the special form "interactive" to take as its
]argument a string-valued expression in place of a specific string, for
]example

I don't think so, but it would be useful.  It wouldn't conflict with
the usage outlined above.  I don't know how feasible it would be,
though, since I don't know whether this information is processed at
run time or definition time.

]	(interactive (format "s(This is a %s prompt string) : " foo-arg))
]
]Unfortunately, GNU Emacs rewards this effort with the error message
]
]	Wrong type argument: listp, "s(This is a Type A prompt string) : "
]
]saying that "interactive" expects a list (huh?) but has been given a string.

If the form interactive is given is not LITERALLY a list, it is
evaluated and must return a list of arguments that will be passed to
the function.  The string you return is not a list from which
arguments can be extracted.

]Trying a macro expansion within the scope of the "interactive" form fails
]in a similar way.

For the same reason.
-- 
harvard >>>>>>  |	Robert Krawitz <rlk@think.com>	245 First St.
bloom-beacon >  |think!rlk	(postmaster)		Cambridge, MA  02141
topaz >>>>>>>>  .	Thinking Machines Corp.		(617)876-1111

jcgs@harlqn.harlqn.uucp (John Sturdy) (01/06/89)

Ram Ashwin writes:
>> Note that foo-arg cannot be one of the arguments to the function since if
>> that function is called interactively, its arguments will not yet
>> be defined.

You can use previous interactive arguments to the function in later
ones like this:
 (interactive (let* ((arg1 (read-from-minibuffer "Arg1: "))
                     (arg2 (read-from-minibuffer
                             (format "Arg2 for %s: " arg1))))
                (list arg1 arg2)))

--
__John            The Lord bless you and watch over you, The Lord make his face
         shine upon you and be gracious to you, The Lord look kindly on you and
   give you peace; My brothers, my sisters, God bless you. Amen.  (St. Francis)

                jcgs@uk.co.harlqn (UK notation) jcgs@harlqn.co.uk (most places)
    ...!mcvax!ukc!harlqn!jcgs (uucp - really has more stages, but ukc knows us)
John Sturdy                                            Telephone +44-223-872522
                      Harlequin Ltd, Barrington Hall, Barrington, Cambridge, UK