When I talk about scope i meant a region
where you can separate the function calls by a
character, for example @ and the let statement span through all the region.
You can easily write a macro that do
(scope
let x 2 @
let y 4 @
print (* x y)
)
But it seem useful to me, why not have it in the core?
A class macro (this is how i call it)
can be use to implement a more general case
of ruby blocks in lisp.
May be this idea has a problem of making a bloated binary code.
I'm a little unclear as to what @ really does. What is the reason you want @? Is it just for let statements, or for any statement? Is it to make cleaner code, or for efficiency? Can you post some code that you think works better or is more elegantly written using @, rather than without it? Maybe some code of 5-10 lines?
I've actually wanted something similar from time to time, but without the '@ and 'let. Right now, I have just one place I really miss it (which you can see at http://github.com/rocketnia/lathe/blob/c343b0/arc/utils.arc), and that place currently looks like this:
(=fn my.deglobalize-var (var)
(zap expand var)
(if anormalsym.var
var
; else recognize anything of the form (global 'the-var)
(withs (require [unless _
(err:+ "An unrecognized kind of name was "
"passed to 'deglobalize-var.")]
nil (do.require (caris var 'global))
cdr-var cdr.var
nil (do.require single.cdr-var)
cadr-var car.cdr-var
nil (do.require (caris cadr-var 'quote))
cdadr-var cdr.cadr-var
nil (do.require single.cdadr-var)
cadadr-var car.cdadr-var
nil (do.require anormalsym.cadadr-var))
cadadr-var)
))
(Note that the Lathe library defines 'anormalsym to mean [and _ (isa _ 'sym) (~ssyntax _)]. Also, 'my is a Lathe namespace here, and I use Lathe's straightforward '=fn and '=mc to define things in namespaces. Finally, don't forget that (withs (nil 2) ...) binds no variables at all ('cause it destructures).)
If I define something like 'scope, I save myself a few parentheses and nils:
(=mc my.scope body
(withs (rev-bindings nil
final nil
acc [push _ rev-bindings])
(while body
(let var pop.body
(if no.body
(= final var)
anormalsym.var
(do do.acc.var (do.acc pop.body))
(do do.acc.nil do.acc.var))))
`(withs ,rev.rev-bindings ,final)))
(=fn my.deglobalize-var (var)
(zap expand var)
(if anormalsym.var
var
; else recognize anything of the form (global 'the-var)
(my:scope
require [unless _
(err:+ "An unrecognized kind of name was passed "
"to 'deglobalize-var.")]
(do.require (caris var 'global))
cdr-var cdr.var
(do.require single.cdr-var)
cadr-var car.cdr-var
(do.require (caris cadr-var 'quote))
cdadr-var cdr.cadr-var
(do.require single.cdadr-var)
cadadr-var car.cdadr-var
(do.require anormalsym.cadadr-var)
cadadr-var)
))
Furthermore, I suspect this version of 'scope would almost always be preferable to 'do, 'let, and 'withs, partly because it's easier to refactor between those various cases. However, it doesn't do any destructuring, and it's probably a lot harder to pretty-print. (I'm not even sure how I would want to indent it in the long term.)
That example doesn't really call out to me: it looks like you could save yourself a few parentheses and nils by refactoring with something simpler, rather than with a complex macro. E.g., if I understand the code correctly:
(=fn my.aglobal (var)
(and (caris var 'global)
(single:cdr var)
(caris var.1 'quote)
(single:cdr var.1)
(anormalsym var.1.1)))
(=fn my.deglobalize-var (var)
(zap expand var)
(if (anormalsym var)
var
(aglobal var)
var.1.1
(err "An unrecognized kind of name was passed to 'deglobalize-var.")))
But then, I avoid setting variables in sequence like that.
Hmm, I kinda prefer local variables over common subexpressions. It's apparently not for refactoring's sake, since I just name the variables after the way they're calculated, so it must just be a premature optimization thing. :-p
But yeah, that particular example has a few ways it can be improved. Here's what I'm thinking:
(=fn my.deglobalize-var (var)
(zap expand var)
(or (when anormalsym.var var)
(errsafe:let (global (quot inner-var . qs) . gs) var
(and (is global 'global)
(is quot 'quote)
no.qs
no.gs
anormalsym.inner-var
inner-var))
(err:+ "An unrecognized kind of name was passed to "
"'deglobalize-var.")))
I still like 'scope, but I'm fresh out of significant uses for it.
(To continue the digression into this particular case...) I had thought about destructuring, but found the need for qs and gs ugly. Really, a pattern matching library would be opportune. But since there's no such luck in Arc proper, it'd be ad-hoc and probably altogether not worth it (though not difficult to implement). I say this with respect to vanilla Arc; I don't know if Anarki has such a library. Still, it'd be hard to beat something like
(=fn my.deglobalize-var (var)
(zap expand var)
(or (check var anormalsym)
(when-match (global (quote ?inner-var)) var
(check ?inner-var anormalsym))
(err "An unrecognized kind of name was passed to 'deglobalize-var.")))
I'm not sure I fully understand the features you're suggesting, but the question should be "does it have to be in the core". Err on the side of keeping the core as minimal as possible unless there is a clear reason to do otherwise.
Exactly. The core can't contain everything everybody would ever find useful. Instead, it's the core's job (IMO) to make it easy to define those things as they come up.
Additionally, it may or may not be the core's job to encourage canonical APIs for non-fundamental but really useful stuff, just so that programmers don't end up defining the same things over and over in ways that are flawed or incompatible with each other. I think this "may or may not" issue is the R7RS split in a nutshell.
In my own opinion, a "core" that tries to provide a comprehensive bookshelf of useful but programmer-definable things isn't a language; it's a utility library. Some libraries may be extremely popular, and they may even be standardized, distributed, and/or documented alongside their languages, but it's ultimately up to the development team to choose the combination of languages and libraries that's right for the project.