Operatives faking wrap

76 views
Skip to first unread message

Sgeo

unread,
Jan 19, 2014, 5:46:50 PM1/19/14
to kl...@googlegroups.com
There seem to be several operatives defined in the Kernel report that manually eval an argument in the dynamic environment, as though doing by hand what wrap does automatically, except not for all arguments. $binds? does this for its first argument, for example. However, this means that there is no equivalent for unwrap, which hinders the usefulness of apply. applying the operative wrapped in not equivalent in all cases. As far as I can tell, the best thing to do in such a case would be to use $quote to cancel out the eval, but $quote is discouraged. 

($define! my-list (list 1 2))

($define! $length ; not a realistic operative, but there are operatives defined in the report that do something similar for some (but not all) arguments
  ($vau (x) env
    (length (eval x env))))

(apply length (list my-list)) ; 2


(apply (wrap $length) (list my-list) (get-current-environment)) ; error because it will attempt to eval (1 2)

Sgeo

unread,
Jan 20, 2014, 2:50:32 AM1/20/14
to kl...@googlegroups.com
I don't know if this sort of thing would be rejected for the same reasons as $quote, but:

($define! list-quote
  ($lambda (x)
    (cons (unwrap list) x)))

An applicative that effectively quotes a list, so that when evaled it gives the original argument, but is applicative so you still can't construct raw symbols with it.

Andres Navarro

unread,
Jan 20, 2014, 10:00:59 AM1/20/14
to kl...@googlegroups.com
On Sun, Jan 19, 2014 at 7:46 PM, Sgeo <sgeo...@gmail.com> wrote:
There seem to be several operatives defined in the Kernel report that manually eval an argument in the dynamic environment, as though doing by hand what wrap does automatically, except not for all arguments. $binds? does this for its first argument, for example. However, this means that there is no equivalent for unwrap, which hinders the usefulness of apply. applying the operative wrapped in not equivalent in all cases. As far as I can tell, the best thing to do in such a case would be to use $quote to cancel out the eval, but $quote is discouraged. 


Well, the thing is, "apply" is meant to be used with applicatives, not operatives.  See the rationale for apply in pages 66 & 67 of the report for a discussion.
Operative such as $binds? & $set! are operatives precisely in order to avoid casual uses of $quote. Older lisps for example had set and setq where the
former required a quoted symbol and the later quoted the symbol automatically like kernel's $set! and scheme set!.

If you need applicatives that perform similar operations (or operatives that do no evaluation whatsoever) you are free to construct them, although you need
to use $quote or a similar quotation device:

($define! $binds2?
  ($vau (env . symbols) denv
    (eval (list* $binds? (cons (unwrap list) env) symbols) denv)))

;; or if you prefer, instead of "(cons (unwrap list) env)" you can use "(list $quote env)"

($define! binds? (wrap $binds2?))

($define! $set2!
  ($vau (env formals exp) #ignore
    (eval (list $define! formals (cons (unwrap list) exp)) env)))

;; or if you prefer, instead of "(cons (unwrap list) exp)" you can use "(list $quote exp)"

($define! set! (wrap $set2!))

Notice the use of (unwrap list) which is more or less equivalent to $quote (it returns the operand list instead of just the first operand like $quote).
A similar trick is used in the report for the definition of $set! in terms of $define! (that also evaluates just one argument), except in that case
(unwrap eval) is used because the evaluation must be forced in another environment (see page 108 of the report).

The idea behind kernel is that you should define as many operatives & applicatives as needed, instead of just having one and using quote for
different use cases.  Notice that the above defined combiners are usually needed (unless you are writing a module system or something
that requires heavy use of bindings, symbols and environments) and so are left out in kernel.  All the regular use-cases are captured by the defined
operatives and don't require the use of $quote at all.  At least that's the idea, discussion is, of course, encouraged.
 
($define! my-list (list 1 2))

($define! $length ; not a realistic operative, but there are operatives defined in the report that do something similar for some (but not all) arguments
  ($vau (x) env
    (length (eval x env))))

(apply length (list my-list)) ; 2


(apply (wrap $length) (list my-list) (get-current-environment)) ; error because it will attempt to eval (1 2)

Here I think you are missing the mark.  There is a difference between $length as (unwrap length).  So the apply equivalence between
"(apply length ...)"  and "(apply (wrap $length) ...)" doesn't apply (pun intended).

With the correct definitions above, let's use binds? in an appropriate context

($define! $all-bound? ;; this applicatives tells us if a list of symbols is bound in the dynamic environment
  ($vau ls denv
    (apply binds? (cons denv ls))))

or equivalently "(apply (wrap $binds2?) (cons denv ls))"

In this case using $binds? would work, but that's just a coincidence:
denv would be evaluated twice but because denv is always an environment and environments evaluate to themselves,
the extra evaluation is no problem.  In other cases, the use of "(wrap $binds?)" would bring unintended consequences,
like the problem you had in your example.

If you did something like:
($define! sym ((unwrap list) . x)) ;; or ($quote x)
(apply (wrap $binds?) (list (list get-current-environment) sym))

It would "work" (when it shouldn't) because of the double evaluation (and return either #t or #f depending on whether x was defined in the current environment)
But using "binds?" (or "(wrap $binds2?)") would correctly signal a type error complaining that the first argument is a list and not an environment
For example in klisp, with the above definition of "binds?":

klisp> ($define! sym ((unwrap list) . x)) ;; or ($quote x)
klisp> (apply (wrap $binds?) (list (list get-current-environment) sym))#inert
#f

klisp> (apply binds? (list (list get-current-environment) sym))

*ERROR*:
$binds?: expected environment as first argument
Location: #[operative: $binds? @ kgenvironments.c (line: 765, col: 0)]
Backtrace:
#[continuation (repl-print-loop) @ *STDIN* (line: 1, col: 1)]
#[continuation (pass-value) @ *STDIN* (line: 2, col: 1)]
#[continuation (pass-value) @ *STDIN* (line: 2, col: 1)]
#[continuation: root-continuation (exit) @ kgcontinuations.c (line: 168, col: 0)]


This is a hairy topic, but I hope I was able to shed some light on the issue.
Regards,
Andres Navarro

Andres Navarro

unread,
Jan 20, 2014, 10:13:19 AM1/20/14
to kl...@googlegroups.com
Even though $quote is not part of the report it doesn't mean that quotation is rejected from kernel, just that it is to be used carefully.  The rationale behind not putting
$quote in the report (even though it's trivially defined as "($define! $quote ($vau (x) #ignore x))"), is that including it would encourage its use when other methods
would be better.  In this particular case, if you frequently needed to work with unevaluated symbol lists (which may be the case in certain type of programs, like module
systems, object systems or other kind of environment manipulation programs) this is quite fine.  For other cases, it would be frowned upon (at least by some people like
me and John Shutt!). 

For example I included keywords in klisp which are like symbols but self-evaluating, for use in programs that would normally use symbols as constants in scheme.  Here
I think adding a new datatype in order to avoid quotation (and semantically overloading symbols) is a Good Thing (tm).

So, what I mean is, quotation can be used in certain contexts, but are better left to the unavoidable cases, and even then are better off if encapsulated in convenient
operatives/applicatives.

Just my two cents,
Andres Navarro
Reply all
Reply to author
Forward
0 new messages