Okay, now that I've actually gotten to see it - I think you forgot to push codegen.arc, since the current version on git don't actually generate code that calls DBL2OBJ.
Edit: Also, it might be useful to organize the primitives as "functions", like what I did with cons_fun().
So:
#define CONS() {obj d = POP(); TOS() = cons_fun(TOS(), d);}
This should allow us to express some primitive calls directly:
Let's call the cons_fun(FIX2OBJ(1) ...) call cons_fun1, and the other as cons_fun2. cons_fun2 is called first (because it's more inner). The value is then pushed into the C stack. Then cons_fun1 is called. However, what if GC is triggered at exactly cons_fun1? Then the cons cell returned by cons_fun2 is on the C stack, not the Arc stack; the C stack is not scanned by the GC! This means we actually lose memory areas.
Probably means I have to modify the way constants are stored.
Hmmm, good point. The stack, GLOBALS and QUOTE_CONSTANTS are all explored when GC is triggered. Maybe the solution would be to add a temporary storage (that should be taken into consideration by GC too) before putting things in the constants, or something like that ?
> Maybe the solution would be to add a temporary storage (that should be taken into consideration by GC too) before putting things in the constants, or something like that ?
Okay, converted init_constants() to use the Arc stack instead of the C stack. Fear the pointer arithmetic foo-ness when using a processor with special PUSH and POP instructions (which are arguably dying out, because RISC processors handle stacks using explicit pointer ariths).
In previous Lisps if you wanted to create a local variable you had to do it explicitly with a let. In Arc we were planning to let users declare local variables implicitly, just by assigning values to them. This turns out not to work, and the problem comes from an unforseen quarter: macros.
If you're going to declare local variables implicitly, you have to decide what the scope of the new variable will be. We decided we would make new variables local to blocks: a local variable created implicitly by assignment would have a scope that extended to the nearest enclosing do (Arc's progn).
...
What convinced us to toss it was not a technical problem but a social one. In a language with implicit local variables and macros, you're always tripping over unexpected lexical contours. You don't want to create new lexical contours without announcing it. But a lot of macros that don't look like blocks in the call expand into blocks. So we provided a second block operator, called justdo, which was like do but didn't create a new lexical contour (i.e. it is Common Lisp progn), and this is what you were supposed to use in macroexpansions.
The trouble was, I kept forgetting and using do instead. And I was thereby writing utilities with the worst sort of bug: the kind that might not show up for years, and only then in someone else's code.
...
This problem is not limited to Lisp. Macros and implicit local variables just don't seem to work well together. Meaning that any language that already has implicit local variables will run into trouble if they try to add macros.
----------
Maybe your "explicit var" would work, but it might just put the problem back from the other direction, so to speak. Perhaps if you give macros some form of "lexical hygiene" (yeah, yeah, hygiene bad, etc.)... How does Goo do it?
'range is an arc.arc function in canonical Arc, so you should be leery of redefining it - if you decide to use some random library, and it uses 'range in the canonical sense, you can expect it to break.
Instead, define it in subfunction form:
(def directawayfrombound (creature)
(withs (range
cons
within
(fn (range num)
(let (min . max) range
(<= min num max)))
awaydir
(fn (pos dim dir)
(if (within (range 0 (- dim 1)) (+ pos dir))
dir
(- dir)))
(xpos ypos) creature!pos
(xdir ydir) creature!dir)
(list
(awaydir xpos (len world.0) xdir)
(awaydir ypos (len world.0) ydir))))
Thanks for the correction. Although I did know about the canonical range function, it slipped my mind when I wrote that (though I did have the feeling that there was a "real" range function). I should have mentioned that I wasn't near any computer with Arc when I wrote, though I still should have known better.
Both of you, thanks!, more learning arc. (1) "withs" is "with sequentially"', so that "range" makes sense in the function "awaydir". (2) cons isn't just appending a number to a list.
I like the table!key syntax, not sure what I'm thinking about world.0 as a syntax, especially because world.0.0 doesn't give what I'd expect (first element of the first element of world), but just returns the same as world.0
(def makeworld (x)
(= world (n-of x (n-of x nil))) ; world of nil
nil)
Actually, almkglor's code would (I'm pretty sure) have worked with just a normal with; function definitions are unevaluated at definition, so that (fn (x) (afergergghersgergferg x)) would not give "Error: afergergghersgergferg is not a function" until it's called. If a defined a function called afergergghersgergferg in the meantime, as with a normal with statement, it should have worked.
Anyway, you're not alone in being surprised that x.y.z expands to (x y z) instead of (x (y z)).
Not quite. It works in the toplevel because doing (def name args ...) is effectively to (= name (fn args ...)); this establishes a global, not a lexical, binding. Using let/with/withs adds a lexical binding, and since functions (or "closures") close over their scopes, they can't get at functions defined at the same time. Thus, you need to use withs. Observe:
That implies that at the top level, the order in which I define my functions (which use each other) doesn't matter..
nice! Then I can group them in a way that is more sensible for humans.
Anyway, the program is now far enough that I've a critter running around the field and filling its memory with associations between the things it encounters. Next stop is to let it learn what is edible and to let it steer itself based on its observations :).
First tip: Avoid using '( ... ) . Use (list ...) instead. When you learn about mutating objects, then use '( ... ) , and only for optimization or if you really, really want to.
So.. (let newdir '(nil nil) should become (let newdir (list nil nil) and creature!pos is equivalent to (creature 'pos) and you make a temporary anonymous function within a function but still give it a name (delimit, through the with command) that does the comparing. Three new things learned :).
I'll see if I can improve it some more though, might be able to do something with testing whether position + car / cadr direction would put a creature out of bounds in the world array, and then multiply the direction by -1. Not at home now, so will have to wait.
As an aside: maybe it's better to make the primitives really, really primitive?
Basically we define separate %n+ for adding two fixnums, %f+ for adding two flonums, %fn+ for adding a flonum and a fixnum (in that order) and, %nf+ for adding a fixnum and a flonum (in that order).
As for macros: Hmm. You see... Look, up in the sky! Is it a bird? A plane? Superman? Nothing? Oh, what were we talking about again? ^^ LOL
Okay now serious: currently I'm blocked with creating a "protected" eval for macros. Programmer's block. The bit blocking me is the destructuring thing. Erk.
I think you're right for primitives. I asked pg the same question a few time ago, he answered he didn't know yet. Anyway, for an optimizing compiler, we will need these (as in : "hmmm, n can only hold a fixnum, let's translate this heavy '+ in a lightweight '%n+ !")
Now, macros. I tried to think about it yesterday, and I don't really understand why you need this protected mode. I mean, macros have to be defined (globally) before they are called and treated as such. Since you can already identify all the globals (and since a macro is a global thing annotated by 'mac), it should be easy to
- make a list of all the symbols holding a macro at a given moment in the code
- explore all the sexprs to see if one of those symbols is the first element of any sublist
If I understand well, we have to interpret the code as we compile it (as a macro's body is executed when a function is defined/compiled, not when executed) ? To me, macros were something translating a piece of code into another one, period. But that's not always the case as your example shows. If someone writes
(mac foo
(prn "blah"))
(def bar (n)
(foo) (* n 2))
That means we have to display "blah" on compile time, right ? Hmm, that's much harder than I thought... It really means implementing an arc interpreter in arc (the way the official one is written in mzscheme), with for example prepending all the interpreted names with _ to distinguish them from the compiler's names...
Ok, I think I'm starting to realy get it ; or am I still missing an even trickier issue ?
That is about it. ^^ However my current implementation uses tables as environments for the function. Keys in the table are symbols for the variable names in that environment. A "parent" string key accesses the environment's parent.
The global environment is shadowed by a table; when this table is accessed, if a table entry does not exist, we look it up in the real environment using 'eval (!!) and retain a copy. This allows the macro to use stuff like 'map.
However there is still a leak here: if the real global-level environment has a function that modifies a global, it will modify a different global variable from the one that is visible to the macro. This shouldn't happen anyway: we should really avoid using globals in utility functions ^^.
capital L means left-associative, so foo/bar/nitz is grouped {{foo / bar} / nitz}.
As an aside, why not <- ? Basically parent<-owner<-id means "send 'id to the result of sending 'owner to parent".
For that matter if an object, such as parent above, is something of very specific type, you can even use the Anarki 'defcall syntax. For example if messageable objects are of type 'messageable, you can do:
I considered <- (along with a lot of other things), but rejected it because the left-associativity of it seemed counter-intuitive, it didn't stand out as much as slashes, and I want it to stand out because it's inconsistent with symbol syntax, it's confusing with -> which conventionally means type-conversion (and which doesn't break up symbols), and it's three times as much effort to type as /.
Slashes are fairly common in symbols, but not at the beginning (except for / itself), so it's unambiguously parseable, though maybe it won't work with this system, now that I think about it. I don't see how to prevent embedded slashes from meaning anything special without the first slash. I.e., I want foo/bar/nitz to be one symbol, but /foo/bar/nitz to be a symbol and two msg calls. Maybe that's too hairy, but I'd hate to break things like w/uniq.
I guess the general solution to any syntax that is confusing to people is a good syntax-highlighting editor.
Well, for that matter having (foo msg) mean "send msg to foo" would be much shorter, and also shows that Scheme was originally a synchronous message-passing language anyway.
But anyway, I still think they're not worth loosing an ASCII character, and using mathematical notation would be very useful. It would make code readable by people a little aware of mathematics. That is, most programers. It would be definitely better than arbitrary characters.
Why should we restrict to ASCII anyway ? I mean, a lot of symbols I use are not ASCII anymore (they are accentuated, I'm French so something like 'year is translated into 'année, not into 'annee). Sure, they're hard to type, but are they any longer than they symbol counterpart ? If you type them often, just make them a vi macro (or whatever in your favorite text editor) and you're done.
It might end up looking like APL, for sure, but I think Fortress, Sun's new language designed by Guy Steele, is going that way too. And Steele cannot be wrong :)
I don't mind non-ASCII, I mind weird non-ASCII. Even in English, we leave ASCII behind: “As I was going to the café—what fun—my naïve friend said…” It's just that I don't know of any keyboard layout that supports ∧ or ∨. I agree that they would look great, as would Fortress.
I wonder if anyone's given any though to using (La)TeX in a language? So that something like (number{\wedge}acons var) would be the same as (number∨acons var)? Or just as a nice way of typing things like Fortress? (Which I agree looks very interesting.)
I'd probably use them a lot more often if the syntax was a little easier, which is why I suggested using ssyntax for them. Currently the syntax is ((orf this that the-other) foo), and doubled starting parens feel rather strange to me.