Arc Forumnew | comments | leaders | submitlogin
1 point by rocketnia 5137 days ago | link | parent

If at some point you say this in Arc...

  (def foo ()
    (look-up-something-in syntax-rules*))
...then (foo) will always use the most recent version of 'syntax-rules* , regardless of how many times it's been reassigned since the definition of 'foo.

This is one reason it's challenging to do modules in Arc. You have to construct 'foo so that its references to 'look-up-something-in and 'syntax-rules refer to the meanings that exist at the time 'foo is defined (already done in Arc, but something to consider as you do things from scratch), and then you have to somehow make the same name have different meanings in different contexts (hard to do on top of official Arc, thanks to Racket's opaque namespaces, but very possible to do from scratch).

I think the least surprising thing to do would be to have your core functionality work the same way as 'foo does in Arc: It would look up the value of 'syntax-rules* , and that particular binding (meaning) would be a fundamental constant of the language. Ultimately, that's the same thing as hardcoding the name, but with an extra layer of indirection to account for modules.



1 point by Pauan 5137 days ago | link

I'm not sure what you mean. Do you mean that it would always refer to the most recent version of syntax-rules* in the current module? Isn't that expected behavior? If you mean it would refer to the most recent version defined in any module, then that shouldn't be an issue, since they are/should be isolated.

My eval hack is only about updating the internal table rules when somebody changes ssyntax-rules* This avoids checking it too frequently, potentially making it run faster. So, the following should work fine:

  (= foo ssyntax-rules*)
  (= ssyntax-rules* nil)
  
  foo            -> previous ssyntax rules
  ssyntax-rules* -> nil

  (= ssyntax-rules* foo) -> works fine again
And yes, I plan to have lexical scope in PyArc. Each function has it's own environment, and a reference to the outer environment, so it can walk up the chain until it either finds the value or realizes it's undefined.

There's a bug with it right now, but I should be able to fix it. The environments get created properly, but when doing the actual lookup, it thinks the value is undefined when it isn't. That's pretty normal, since the interpreter is still missing lots of functionality.

-----

1 point by rocketnia 5137 days ago | link

"I'm not sure what you mean. Do you mean that it would always refer to the most recent version of syntax-rules * in the current module? Isn't that expected behavior? If you mean it would refer to the most recent version defined in any module, then that shouldn't be an issue, since they are/should be isolated."

The core is a module, right? At least in concept? And shouldn't you be able to modify something in another module when you need to, to fix bugs etc.? (Not that you'd be able to fix bugs in the core, necessarily. :-p )

---

"There's a bug with it right now, but I should be able to fix it. The environments get created properly, but when doing the actual lookup, it thinks the value is undefined when it isn't. That's pretty normal, since the interpreter is still missing lots of functionality."

It sounds almost like you shallowly copy the outer scope when you create a local scope, which means you miss out on later definitions made in the outer scope. Is this right? It's a pretty wild guess, and even if it is right, then I'm interested to know how you're shallowly copying arbitrary environment types. :-p

(EDIT: Oh, you've fixed this already. I'm just late to the game.)

-----

1 point by Pauan 5137 days ago | link

Yes and yes. But there's supposed to be a clean separation between modules. If you assign to a variable, it assigns it in your module. If you want to assign to a different module, you need to be explicit, like so:

  (import foo "foo.arc")
  (= foo!bar 'qux)
So in order to assign to the core module, you would need something like built-ins* to access it:

  built-ins*!ssyntax-rules*  ; modify ssyntax globally
Assuming I choose the name built-ins* of course.

"then I'm interested to know how you're shallowly copying arbitrary environment types. :-p"

Environments are just wrappers around Python's dict type, so I can use the copy() method to make a shallow copy. I might need to make my own custom copy function later, though. Arc tables are just annotated dicts, by the way. But Python lets me modify how classes behave, so for instance I should be able to get cons cells to work in a dict, even though they're mutable (and thus not normally allowed).

-----

1 point by rocketnia 5137 days ago | link

"If you want to assign to a different module, you need to be explicit, like so:"

Ah, I don't know why I didn't see that. ^_^

---

"Environments are just wrappers around Python's dict type, so I can use the copy() method to make a shallow copy. I might need to make my own custom copy function later, though."

I was harking back to "What is an environment? Anything that supports get/set on key/value pairs." Would it also need to support a copy function?

-----

1 point by Pauan 5137 days ago | link

Hm... it shouldn't, no. Only global_env is copied, so Arc types wouldn't need to worry about copying. In the hypothetical case that I would need to copy them, I could coerce them into a table and then do a copy on that. This could all be done transparently, so Arc code wouldn't need to do anything.

global_env is treated specially because it's the base for all other modules: it contains the built-in functions that modules cannot create (at least not in Arc), and also the core functions in arc.arc for convenience.

There are other strategies I could use as well, like special-casing (assign) or similar, but I figured a shallow copy would be easiest.

-----

1 point by Pauan 5136 days ago | link

"...and then you have to somehow make the same name have different meanings in different contexts (hard to do on top of official Arc, thanks to Racket's opaque namespaces, but very possible to do from scratch)."

I'm curious, how are Racket's namespaces opaque?

-----

1 point by rocketnia 5136 days ago | link

That's a good question. It's probably unfair to Racket to just call the namespaces opaque without an explanation. In fact, I'm not sure there isn't a way to do what I want to with Racket namespaces, and even if there weren't, there might be a way to configure Racket code to use a completely different kind of name resolution.

With a Racket namespace, you can get and set the values of variables, you can reset variables to their undefined state, and you can get the list of variables, so it's very much like a hash table. But suppose I want to have two namespaces which share some variable bindings. I may have been able to get and set the values of variables, but it turns out I can't manipulate the bindings they're (in my mind) stored in. I can't create two namespaces which share a binding.

Fortunately, Racket namespaces can inherit from modules, which sounds pretty much exactly what I want, and there's also the "racket/package" module, which may or may not act as an alternative (for all I know). However, you can't just create modules or packages dynamically; you have to define them with particular names and then get them back out of those names somehow. I haven't quite been able to get anything nifty to work, maybe because of some issue with phase levels.

-----

1 point by Pauan 5136 days ago | link

So... you want two different namespaces that inherit from the same namespace? Easy in PyArc (or Python):

  (= shared (new-namespace))
  (new-namespace shared)
  (new-namespace shared)

-----

1 point by rocketnia 5136 days ago | link

I did say "very possible to do from scratch." PyArc's behavior doesn't help in pg's Arc. ^^;

-----

1 point by Pauan 5136 days ago | link

Yeah, that's true. It's one of the (few?) benefits of writing the interpreter from scratch, rather than piggybacking on an existing implementation.

-----

2 points by rocketnia 5136 days ago | link

The less an interpreter piggybacks, the less will be left over to implement when porting the interpreter to another platform. That benefit's enough by itself, IMO. ^_^

But then I tend to care more about the work I'll need to do in the future than the work I do today. When my priorities are the other way around, like for fun-size DSLs that'll hopefully never need porting, being able to piggyback is nice.

-----