Are there function prototypes in Common Lisp?
Asked Answered
T

3

6

I have been programming in common lisp for a little while now, and throughout my experience using lisp, I have yet to see any function/macro that acts anything similar to function prototypes in C or C++.

Currently I have to very careful about the ordering of my functions, otherwise, when I try to call a function from another, Lisp says the function "does not exist" because it is defined later in the file. Is there a way to get around this? Can I declare all my function prototypes at the top of the file, and the full definitions below?

Tantrum answered 30/8, 2014 at 21:26 Comment(4)
If you use compile-file then you will usually NOT see a warning about functions, when they are later defined in the file and used earlier.N
My program requires the lisp interpreter, I use load not compile-fileTantrum
@ShockWave They should only be warnings not errors. They should not prevent you from actually executing any code.Ingressive
@Shockwave: I doubt that you use an interpreter. An interpreter does not complain about undefined functions. You may want to describe in more detail what you are actually doing and what you want to achieve.N
B
13

Declaim and Proclaim

You can use declaim to globally declare that a certain thing has a certain function type. For instance, look at what happens first if you define foo1 that calls undefined baz (in SBCL):

CL-USER> (defun foo1 ()
           (baz))

; in: DEFUN FOO1
;     (BAZ)
; 
; caught STYLE-WARNING:
;   undefined function: BAZ
; 
; compilation unit finished
;   Undefined function:
;     BAZ
;   caught 1 STYLE-WARNING condition
FOO1

Now, let's add a declamation that says that baz is a function of no arguments, and returns something. You could obviously add more type information if you wanted to, but this will at least provide the arity and the knowledge that baz is a function.

CL-USER> (declaim (ftype (function () t) baz))
; No value

Now when you define foo2 that also calls baz, you'll get no warning:

CL-USER> (defun foo2 ()
           (baz))
FOO2

Declaim is a macro, but if you need to be able to generate some of these things at runtime, you can use proclaim, which is a function. E.g.,

CL-USER> (dolist (f '(square cube))
           (proclaim `(ftype (function (number) number) ,f)))
NIL
CL-USER> (defun add-square-and-cube (x y)
           (+ (square x) (cube y)))
ADD-SQUARE-AND-CUBE

That said, this is not very idiomatic Common Lisp. It's much more common to put the code you need into a file and then to compile that file and load it. If that's not possible for some reason, this will work, but it's worth considering other options of loading your code if they're available.

Muffling warnings

It's also worth noting that while SBCL will take the hint from proclaim or declaim and silence the undefined function warning, the function is still actually undefined. Other implementations (e.g., CLISP) will still issue a warning about the undefined function.

I don't really recommend the following approach, because warnings are around for a reason, but you can choose to muffle warnings when you evaluate code. E.g., in CLISP, we get a warning when we compile with undefined functions:

CL-USER> (compile nil (lambda () (baz)))
WARNING: Function BAZ is not defined
#<COMPILED-FUNCTION NIL>
1
1

We can bind a handler that will muffle any warnings that occur when the form is evaluated, though:

CL-USER> (handler-bind ((warning
                         (lambda (x)
                           (muffle-warning x))))
           (compile nil (lambda () (baz))))
#<COMPILED-FUNCTION NIL>
1
1

This has its ow caveats, too, since the type of warning that you might get for compiling a reference to an undefined function might vary, and what muffling the warning does may vary.

Barman answered 30/8, 2014 at 21:52 Comment(0)
H
4

As Rainer pointed out you can ignore this issue if the forward reference is within a single compilation unit. But that won't help if the forward reference crosses compilation units. Usually that's a sign that your code is poorly layered. Low level code makes calls to higher level code? Well, people will say the low level code is providing a hook for the high level code.

That said I certainly have seen and written code that had this problem. Spaghetti code, yum! It can arise when you start breaking huge source files into smaller ones. A compilation unit is usually a single file. But take a look at with-compilation-unit. I don't recall asdf doesn't provide easy access that though.

I don't know if Joshua's solution of using providing a declaration will work across all CL implementations. My memory is that when I had to solve this problem many many years ago we had to implement something more crude, we would give the function a stand in definition, and then hack together a way to suppress the definition warnings.

No doubt cl-launch could be used to see if Joshua's solution works across the spectrum of implementations.

Hazlitt answered 2/9, 2014 at 15:3 Comment(1)
+1 for the mention of cl-launch! And I'll mention right away that the "solution" I mentioned won't work on all platforms; indeed, that's why I started exploring some other options with muffle-warnings.Barman
A
0

You can define something to be a generic function before you define any of its methods. So, if you write

(defgeneric foo (x y z))

then any function calling FOO will see a defined function (although one without any methods). The body of the function can then be added later as a method

(defmethod foo (x y z) ;; The body of the function ...)

Apex answered 9/10, 2015 at 16:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.