I dunno. It seems I'm in the minority, but I actually like using (o arg default). Only problem is the whole "don't use o when destructuring" thing, which is annoying. As for these options, I agree with conanite. In general, overloading either quotes or dots would be weird.
You could tweak o to be nicer to multiple parameters, though. E.g.,
(def trim (s (o where 'both) (o test whitec))
...)
If CL's &optional came to this forum it would say: If you're going to have a new keyword like o, at least take advantage of the fact that required args can't follow optional args, and get rid of the parens.
I like how you argue this is a rare case :) My motivation is purely aesthetic, under the assumption that I'll want to make a long term commitment to a 100-year language something like arc. I'm trying to add keyword arguments to arc, and I figure I might as well revisit this decision at the same time.
Two problems with (o arg default) - you need to remember not to use 'o at the start of a destructuring list (and not get confused when you see (def handle-request ((i o ip)) ...) ), and as akkartik says it's paren-inefficient, a single keyword to delimit required/optional args would mean fewer tokens.
The first problem is easy to fix though - use a symbol that's less likely to be an arg name to identify optional args. How about '= ?
(def myfun (a b (= c (something)) ...)
it has the advantage of similarity with ruby:
def myfun a, b, c=something
disadvantage: looks silly when you don't supply a default value:
Thinking about the paired-o suggestion I made, it has an advantage over grouping single optional arguments by their own parens -- at least if you have more than 2 optional parameters :). Compare:
(a b (opt-c default-c))
(a b (o opt-c default-c))
(a b (opt-c default-c) (opt-d default-d))
(a b (o opt-c default-c opt-d default-d))
(a b (opt-c default-c) (opt-d default-d) (opt-e default-e))
(a b (o opt-c default-c opt-d default-d opt-e default-e))
It's like a 3-character &optional, just with one of the characters at the end. However, you'd need to specify nil defaults by hand (though you might let an uneven pair at the end default to nil). E.g.,
(def markdown (s (o maxurl) (o nolinks))
...)
could, at best, become
(def markdown (s (o maxurl nil nolinks))
...)
But paired-o also generalizes the current behavior, so we could still use the original signature.
http://github.com/nex3/arc/blob/master/help/arc.arc is from Anarki, a community-maintained branch of "vanilla" Arc. They wrote such a help function, but there are also other extensions and differences that are reflected in said help.
This isn't exactly like your code, since I wasn't sure what you wanted it to do in the case of three or more arguments, but the important part is the alternative to map.
After hammering on your approach a little, here's what I get. ^_^ I like to maintain the fact that Arc's '+ is left-associative, so I don't give myself as much leeway with the additional arguments.
; We're going to have a meaning for number-plus-list, so we override
; the default number behavior, which assumes that only numbers can be
; added to numbers.
(extend + args (let (a b) args (and number.a number.b))
(let (a b . rest) args
(apply + (do.orig a b) rest)))
(extend + args (let (a b) args (and cdr.args number.a alist.b))
(let (n xs . rest) args
(apply + (map [+ n _] xs) rest)))
Saying (treewise cons [only.+ _ n] xs) is a bit more wordy than necessary here, but it's still a good way to accomplish (+ n xs) inline, without extending '+.
Oh, hey, if 'treewise could tell that it had a cyclic data structure, it would also be a more robust option... but that's not the case yet. Should it be?
Never thought of that. I mean, the P part of the vanilla Arc REPL breaks on them, and I've never been compelled to use cycles. When are cyclic lists used? I guess if you're representing certain graphs?
Personally, I use Vim (efficient text editing), Yakuake (shell is an F12 away), and rlwrap (make bad REPLs good!) for, well, really any language I use. Specifically for Arc, I wrote a Vim syntax highlighter & ftplugin: http://arclanguage.org/item?id=10147. Also, it's useful to
Now all I have to do is setup $ARC in my shell startup, and I can run arc from anywhere and load source files transparently from the current directory.
Yeah, that's the right one. More "officially", http://yakuake.kde.org/ leads you through some links until you finally get to its KDE-Apps page at http://kde-apps.org/content/show.php?content=29153. But you'd probably just get it from your distro's package repository, anyway. For instance, I'm on Debian right now, and the package description does the elaboration for me, so:
$ apt-cache show yakuake | grep -A7 -m1 "Description:"
Description: a Quake-style terminal emulator based on KDE Konsole technology
YaKuake is inspired from the terminal in the Quake game: when you press a key
(by default F12, but that can be changed) a terminal window slides down from
the top of the screen. Press the key again, and the terminal slides back.
.
It is faster than a keyboard shortcut because it is already loaded into memory
and as such is very useful to anyone who frequently finds themselves switching
in and out of terminal sessions.
For one, you have an extra set of parentheses around the body of the function. Parenthetical expressions like
(expr1 expr2 expr3 ...)
by default will evaluate expr1 and try to call it with the arguments expr2, expr3, .... E.g.,
arc> (+ 1 2 3)
6
This sort of rule applies recursively, so
((if (> 1 2) - +) 1 2 3)
first evaluates
(if (> 1 2) - +)
which returns the function + (since (> 1 2) is false), meaning the original evaluates to
(+ 1 2 3)
There are a few places where parenthetical expressions don't evaluate this way, but they're usually pretty obvious -- like the argument list in a def. For more details, the tutorial is a decent place to start learning about Arc & Lisp programming: http://ycombinator.com/arc/tut.txt.
To fix your code immediately, you'd just need to get rid of the extra parens. Expressions in the body of a function evaluate one after the other, returning the value of the last expression, so given
(def f (x)
(+ x 1)
(+ x 2))
a call to f will calculate (+ x 1) and (+ x 2), returning the latter.
But you should (generally) only use = for global variables. For local ones, you'll want with or let.
arc> (let x 1 (+ x 1))
2
arc> (with (x 1 y 2) (+ x y))
3
Notice how the argument 'abcdef was quoted. Macros don't evaluate their arguments, but the code they generate might (e.g., if bar was a function, it'd try to evaluate abcdef as a variable).
They aren't actually implemented that way. eval operates at run-time and doesn't have access to lexical variables. Macros expand at compile-time, so it's as if you had the expansion there to begin with. E.g.,
arc> (let y 10
(eval 'y))
Error: "reference to undefined identifier: _y"
but
arc> (mac foo (arg) arg)
#3(tagged mac #<procedure: foo>)
arc> (let y 10
(foo y))
10
because
(let y 10
(foo y))
macroexpands into
(let y 10
y)
before it ever runs.
Anonymous macros are plausible, but they might force work to be done at run-time -- essentially, you're right that eval's the closest thing to it. But since macros happen at compile-time, you can't do something like
((if (mem 'x stuff) push pull) 'y stuff)
The compiler sees the macros push and pull, but they're not applied to anything, so it doesn't expand them. Then at run-time, you get an error as it tries to evaluate each form. You have a macro trying to act like a function (i.e., at run-time).
2. do is for when you want to execute a series of expressions, but do it in just one s-expression (i.e., the thing wrapped in do). You see it a lot in macros; e.g., defs in arc.arc:
which is just one list. You couldn't return multiple values, so the macro couldn't expand into
(def f (x) (+ x 1))
(def g (y) (- y 1))
directly.
Another place you see do a lot is in if statements. Each clause is one expression, but if you want to do multiple things in one expression, you need the do. E.g.,
(if test
(do (pr #\t) (pr #\h) (pr #\e) (pr #\n))
else)
will print "then" if test is true, otherwise it'll do else. This wouldn't work without the do:
(if test ; if "test"
(pr #\t) ; print #\t
(pr #\h) ; else if (pr #\h)
(pr #\e) ; then (pr #\e)
(pr #\n) ; else if (pr #\n)
else) ; then "else"
P.S. That's the return value of the statement.
arc> (pr "a") ; prints "a" WITHOUT a newline, then returns the first thing it
; printed (the string "a")
a"a"
arc> (prn "a") ; print "a" WITH a newline, then returns the first thing it
; printed (the string "a")
a
"a"
It'd be easy, since ac.scm just changes ~foo into (complement foo) (except (~foo x) is changed to (no (foo x)), but that doesn't affect us here). Just change complement.
But I don't think I've ever made that slip. Function complementation isn't the same as Boolean-not, so overloading the visually distinctive ~foo based on context rubs me the wrong way. I'd probably get used to it, though.
Edit:
Another thought occurs. It could produce some weird bugs. E.g., you might mistakenly pass a variable to a keep that complements, which will work, but keeps t or nil instead of using a function.
arc> (= foo "bar")
"bar"
arc> (keep ~ssyntax '(a.b a:b nil a b c))
(nil a b c)
arc> (keep ~foo '(a.b a:b nil a b c))
(nil)
arc> (keep ~nil '(a.b a:b nil a b c))
nil
It doesn't seem so bad, but when you have it buried in some definition like
Also, I wonder, if there is a way to replace pairs of characters? I mean, if we need a code, that would replace 'number two number' with 'one two three,' should we use (multi)subst for it or is it done the other way (regexps etc)?
The install guide (http://arclanguage.org/install) is out of date. For one, the latest Arc (which works with the latest versions of mzscheme) is 3.1, which you can get from http://ycombinator.com/arc/arc3.1.tar. Secondly, mzscheme -m used to mute the "Welcome to MzScheme" banner, but now:
You might also notice that in the newer versions, ^C at the Arc prompt doesn't put you back into the Scheme REPL like it says in the install guide. If you want that option, use
(1) In the future, your post titles should be more descriptive; using more exclamation marks doesn't get you more help. :P E.g., you could've titled this post something like "Weird error. main: not defined or required into the top-level environment".
(2) To format code, put a blank line
and then but two or more spaces before the line of code
(and also for subsequent lines of code).
If it doesn't work right the first time you post, you're allowed to edit for a certain amount of time. A quick formatting reference: http://arclanguage.org/formatdoc.