[UPDATE]
You have to make some minor tweaks to my original code to make it work. Here's a fully tested version:
(defun _true (then else) then)
(defun _false (then else) else)
(defmacro _if (condition then else)
`(funcall (funcall ,condition (lambda () ,then) (lambda () ,else))))
(setf (get t 'altboolean) '_true)
(setf (get nil 'altboolean) '_false)
(defun ->altboolean (condition) (get condition 'altboolean))
(defun _fact (n)
(_if (->altboolean (<= n 1)) 1 (* n (_fact (1- n)))))>> In Common Lisp IF is built in and COND is a macro. One of the conditionals needs to be built-in.
> That's actually not true.
The context was about Common Lisp, not Haskell nor Scheme (of course other languages do things as they wish, including lazy evaluation). There need to be at least one conditional built-in in CL in order to map generalized booleans to {T, NIL}. This conditional could even be eager, but there has to be a construct which selects the approprate value. Which of the other 24 special forms is suitable?
As implemented, it's not quite a drop-in replacement for IF, but it can be made so by rolling the call to ->ALTBOOLEAN in to the _IF macro.
> The context was about Common Lisp
Yes, I know that. The example code is written in Common Lisp.
> there has to be a construct which selects the approprate value
Yes. LAMBDA can do that.
> Which of the other 24 special forms is suitable?
LAMBDA. It can be used both to select the appropriate value and to delay evaluation. LAMBDA is really all you need. Making IF a special form is just an optimization.
> LAMBDA.
I am nitpicking, but LAMBDA is not a special form, FUNCTION/FLET/LABELS are: http://www.lispworks.com/documentation/HyperSpec/Body/03_aba...
> It's a pretty elementary exercise to fix ->altboolean so that this invariant is maintained.
And how do you map generalized booleans to your functions?
Right now, I can think of two possible ways. You can map NIL to #'_false in a hash-table, and use (gethash nil table #'_true) to provide a default value. Or you can also write a generic function:
(defgeneric ->altboolean (x)
(:method ((x null)) #'_false)
(:method (x) #'_true))
There could be other ways, but as far as I know, none of them are portable, because even though it is allowed for all defined CL operators to be implemented as built-ins, you generally cannot expect or assume that it is done so (see gethash in CCL and SBCL). As far as you know, and even though it would be terribly inefficient, they could all be implemented only by combining special forms.If you discard IF, can you perform the elementary exercise using only special forms? I cannot imagine how, but I honestly would like to be shown a working approach.
You certainly are.
> can you perform the elementary exercise using only special forms?
I don't even need any special forms. All I need to do is use the default argument to GET.
CL-USER 16 > (defparameter a 0)
A
CL-USER 17 > (if (foo) (foo) (foo))
T
CL-USER 18 > a
4
CL-USER 19 > (defparameter a 0)
A
CL-USER 20 > (_if (->altboolean (foo)) (foo) (foo))
T
CL-USER 21 > a
5
Given that foo is defined as: (defmacro foo () (incf a) '(progn (incf a) t))
4 vs. 5Does not look like a true replacement for IF in Common Lisp...