Arc Forumnew | comments | leaders | submitlogin
3 points by rocketnia 4446 days ago | link | parent

"Which reminds me... rocketnia, you mentioned being able to define vau in terms of wart's magical fn. You're mostly right, except... there will be two ways to grab the environment: the env parameter and the implicit global caller-scope. So that's a bit of a leaky abstraction."

Hmm... yeah, it's a pretty leaky abstraction. It's one of these features with ambient authority:

  (if designed like a global variable assigned at the beginning of each
  call)
  
  Right now, get the most recently started call's caller's environment.
  
  
  (if designed like a continuation mark)
  
  Right now, get the deepest-on-the-stack call's caller's environment.
  
  
  (if designed like a local variable bound by each (fn ...))
  
  Given a non-global lexical environment, its local variables must have
  been initially bound by some call, so get the lexical environment
  that was used to evaluate that call.
---change of topic--

...Actually, the first two of these designs are broken! Say I define a simple fexpr like this one:

  (def idfn '(result)
    (eval result caller-scope))
Now if I run the code ((fn () (idfn caller-scope))), the call stack at the evaluation of 'caller-scope looks something like this:

  ((fn (a) ((fn () (idfn caller-scope)))) 1)  ; eval'd in global scope
  ((fn () (idfn caller-scope)))  ; eval'd in local scope of (fn (a) ...)
  (idfn caller-scope)            ; eval'd in local scope of (fn () ...)
  (eval result caller-scope)     ; eval'd in local scope of idfn
  caller-scope                   ; eval'd in local scope of (fn () ...)
IMO, the value we get should be the local scope of (fn (a) ...), so that it doesn't matter if we replace ((fn () (idfn caller-scope))) with ((fn () caller-scope)). However, the calls to 'idfn and 'eval are deeper on the stack and their start times are more recent, so we get one of those caller scopes instead.

Does Wart have this awkward behavior, akkartik?

---/change of topic---

To get back to the point, the local-variable-style version of the feature (the only version that works?) doesn't strike me as completely undesirable. It lets the programmer evaluate expressions in arbitrary ancestor stack frames, and that isn't a shocking feature to put in a debugger.

Sure, it makes it almost impossible to hide implementation details and keep privilege from leaking to all areas of the program, but it could be useful in a language where all code that runs in a single instance of the language runtime has been personally scrutinized (or even written) by a single programmer. That's the primary use case akkartik has in mind anyway.



1 point by akkartik 4446 days ago | link

the value we get should be the local scope of (fn (a) ...)

Wart returns the scope with a set to 1. Is that the one you mean? It's different from the value of:

  ((fn (a) caller-scope) 1)

-----

1 point by rocketnia 4446 days ago | link

That's all what I was hoping for. ^_^

I'm impressed you're managing to keep track of that with mutation. Are you setting 'caller-scope on every entry and exit of a function and on every entry and exit of an expression passed to 'eval?

-----

1 point by akkartik 4446 days ago | link

caller-scope acts like an implicit param of all functions:

http://github.com/akkartik/wart/blob/98685057cd/008eval.cc#L...

Perhaps you're concerned about something I haven't even thought of :)

-----

1 point by rocketnia 4445 days ago | link

Oh, Pauan had mentioned 'caller-scope being a "global hard-coded" variable, and I didn't see you disagreeing, so I assumed you were just assigning to it a lot behind the scenes. :-p

-----