4 ms·
This is an interesting article, but it unaccountably fails to say what an fexpr is. As fexprs are not present in either Common Lisp or Scheme, only aficionados
by foldr 5y ago
This is an interesting article, but it unaccountably fails to say what an fexpr is. As fexprs are not present in either Common Lisp or Scheme, only aficionados of the history of Lisp will know. Here is the definition from the Wikipedia article:
> A fexpr is a function whose operands are passed to it without being evaluated. When a fexpr is called, only the body of the fexpr is evaluated; no other evaluations take place except when explicitly initiated by the fexpr.
- User23 5y agoThat sounds a lot like a macro. Is the main difference that macros evaluate at compile time and fexprs evaluate at EVAL time? I imagine a facility like EVAL-WHEN[1] can blur the distinction. [1] http://www.lispworks.com/documentation/HyperSpec/Body/s_eval_w.htm http://www.lispworks.com/documentation/HyperSpec/Body/s_eval...
- HexDecOctBin 5y agoThe fundamental difference as far as I can see will be removing the distinction between macros and function. For example, (assuming parameters starting with @ are fexprs): (defun if (bool @then @else) (cond (bool (eval @then))) (t (eval @else))))
- Jtsummers 5y agoThe parameters aren't the fexprs, the function is the fexpr. Even bool will need to be wrapped in an eval.
- agumonkey 5y agoI guess there's no more 'time' because it can happen anytime on any fexpr due to any reason.
- dragonwriter 5y ago> That sounds a lot like a macro Yes, with fexprs, functions can serve as (runtime-evaluated) macros. REBOL used this (though they aren't call fexprs in REBOL) so that its functions could also serve the role of Lisp macros, and it was pretty awesome. It’s too bad that REBOL became such a dead end for nontechnical reasons.
- sparkie 5y agoMacros are also not first-class, whereas fexprs are. Macros and other special forms must appear in their own name when they are called. You can't necessarily bind them to a symbol and then use that symbol in its place with the same behavior. Consider a symbol which could represent some abstract "binary function", which you might later call. You can bind functions like `add` and `mul` to this without trouble, but if you try to bind `and` or `or` to it, you might not be able to (depending on which lisp you are using). You could try to wrap the call to `and` in a function, like `(define (and* x y) (and x y))`, but this `and*` does not actually have the correct behavior, because `y` should not be evaluated if `x` evaluates to `#f` - but being a function, both arguments are evaluated implicitly. If using fexprs, this issue is non-existent. `and` and `or` are simply defined as fexprs, are first-class, and their operands are not implicitly evaluated.
- rjsw 5y agoIf people want to see how fexprs can be implemented they could look at the Franz Lisp sources.
- afranchuk 5y agoThanks for filling in the blanks. To clarify (hopefully): I think it's a bit odd to refer to fexprs as if they are an expression themselves. A language could potentially implement it as a flag on an expression to indicate how it should be evaluated, but normally one says that a language/expression has fexpr semantics, meaning that it is passed unevaluated forms. If a language has fexpr semantics, you can for instance implement both macros and "normal" functions (among many other things) in very similar ways, since the only difference is that a normal function will first evaluate its arguments and then use the results of evaluation, whereas a macro may not do that (or may conditionally evaluate them!). Hope that helps a little...
- kazinator 5y agoThe term FEXPR most likely refers to the fact that in the original LISP, a FEXPR is an interpreted function: it contains expressions which the interpreter must process. All the built-in functions in LISP which do the same thing are called FSUBR, and are written in machine language. For instance LET has a binding to a FSUBR, whereas a user-defined LET-like operator would be a FEXPR. While that adds a new construct to the language, it must itself be interpreted to evoke its semantics.
- tromp 5y agoSo fexprs are just like functions in Haskell, which is lazy by default?
- martinflack 5y agoI think the difference is what the function can "see." e.g. if you have x and y in the environment as variables, and call your fexpr with (max x y) as the single argument called m: (myfexpr (max x y)) In haskell, m is not calculated (i.e. "max" is not run, nor x or y consulted) until "used". But the function doesn't "know" that it's max that's waiting to run, just that m is available if needed. In a fexpr, you get something like a macro does, so you can see '(max x y) if you care to examine m, and you must call (eval m) to get the answer, but you could theoretically decide to do something else, like form a new expression if you walk m and make a decision based on the code itself (i.e. that it's a call to "max").
- lispm 5y agoExample on a Lisp Machine in ZetaLisp: Command: (zl:defun my-if zl:fexpr (form) (list :my-if form)) MY-IF Command: (my-if (> a b) a b) (:MY-IF ((> A B) A B)) Thus one can see that the original source of the arguments is bound to the symbol FORM as a list. A more recent language with FEXPRs seems to be the language R.
- kazinator 5y agoFexprs are present in many Lisps that have an interpreter. They are just not application definable. If your interpreter processes special forms by looking up the operator symbol in a table and calling a retrieved function, that's an fexpr. If the application can wire its own entries into that table, then it can extend the language with new special forms. The original terminology in LISP was that the FSUBR was a machine-language coded special operator. When the program wired its own interpreted funtion to create a new operator, that was a called FEXPR. FEXPR is an extension of the interpreter which is itself an interpreted function. The name probably alludes to the FEXPR containing a body of expressions that must be evaluated. Whereas the machine language analog, the FSUBR, is a pure subroutine: the interpreter isn't required to process expressions inside it (though of course it calls the interpreter, as necessary).