Thanks for the feedback. The caps style is actually my present split test. I have caps and non-caps variations running for each of my 6 ads right now and will see in a few weeks how their performance compares.
I think a tailored landing page is a great idea. I worry about it not being clear enough how to get started for the people who click through.
Fair enough on the caps issue. I can't argue with "if it works better...", even if I don't like it personally.
Who do you want to target with this campaign? People learning programming for the first time, people learning a Lisp for the first time, or Lispers new to Clojure (and Arc)? You'll require very different instruction pages for each group. I'd be happy to help you write something, if you're interested. I'm sure many of us here would be.
I'd love to get some collaboration from the community here.
I agree 100% about different group requiring different landing pages. Both of the ones you mentioned are good targets, I think. I'll do more keyword research to help dissect the audiences.
The beginning programmers' page is easiest, because pg's tutorial already targets them:
"This is a brief tutorial on Arc. It's intended for readers with
little programming experience and no Lisp experience. It is thus
also an introduction to Lisp."
All we have to do is put the tutorial in an iFrame next to the REPL, as thaddeus suggested. [1] I can take care of this one. (Eventually it would be cool to have an interactive tutorial though, as you suggested.)
Can I get your input on a page that targets a Lisper who's new to Arc? They can be coming from the Lisp dialect of your choice.
And what other audiences would you consider? Maybe people new to Lisp but coming from other programming experience, such as Ruby or JavaScript?
I noticed within your feedback forum there were suggestions for REPL window size changes (for both bigger and smaller) and I got to thinking that when I wrote PetroEnergyNews (http://petroenergynews.ca/map ... which has a similar bounding box look to it) I did a bunch of experimenting with different systems, different monitors and different screen resolutions before coming up with the following optimal combinations:
where the defaults were also the best suited to the iPad.
If you would like you can create an account on PetroEnergyNews then go to the user preferences to select the various combinations to get a feel for these sizes.
The "Widest" by "Tallest" setting is perfect for my 27 inch iMac screen :)
Anyways hopefully this info is helpful/useful.
[edit:
1. oops, that's only the map inset window, so it doesn't include the top and bottom bars, but still they should be easy to guesstimate at about 24px each.
2. I guess the width really doesn't matter when you can just set to 100%, but it may help with the height settings?]
Contents pasted below for convenience with clickable links. Anybody else miss the Arc Forum search that SearchYC provided?
--
It's a bittersweet situation as we rejoice pg adding a search bar to Hacker News [1] while mourning the sudden loss of SearchYC [2].
One service SearchYC provided that remains a need unfulfilled even with the new feature is search for Arc Forum, pg’s other news.arc forum [3]. This had been an extremely valuable resource to the Arc language community as SearchYC had been to the Hacker News community. It kept the old gem posts and comments which tend to get buried in the forum accessible to new readers.
How can we address the problem of lost search to Arc Forum and the other Hacker News sister forums in the wake of SearchYC’s shutdown?
I used http://af.searchyc.com/ a lot, but for the past month or two I was noticing new posts weren't showing up in the search results, so I kinda expected something to happen to it pretty soon anyway.
I'm having a lot of "shoulda mentioned that earlier" moments recently. O-o;
andres said he could help do it if there's an existing crawler for Arc Forum. http://news.ycombinator.com/item?id=2620661 Has anybody written an AF crawler they'd like to volunteer for getting search back?
"Is a cons based list system actually bad? Are there better things that we could be doing?"
They are interesting questions. I think that to be a lisp, a language must homoiconic and applicative. A cons-based system is non-essential, and I am coming to agree with the OP that it's not the best way.
Update: Hmm... my mind is playing tricks on me now. What would you use besides conses to represent s-expressions? Maybe that is a good role for cons.
I wonder if there's a way to merge quote and lambda into a single operator. This is done in concatenative languages like Joy and Factor, where a "quotation" acts both as quoted code and as an anonymous function. But I'm struggling to see how you could translate that into applicative terms.
"What would you use besides conses to represent s-expressions?"
What are s-expressions? I thought their main aspect was the way they used lists to represent user-extensible syntax forms (macro forms, function applications, fexpr applications). I'm not a fan, but even if we assume s-expressions, they could always use some other implementation of lists.
--
"I wonder if there's a way to merge quote and lambda into a single operator."
Do you mean like in PicoLisp, where all user-defined functions are lists containing their code, and applying a list causes it to be interpreted as a function?
I don't like it, 'cause you don't get any lexical scoping this way. It probably makes more sense in Factor and Joy thanks to the lack of local variables for run-time-created functions to capture.
"What would you use besides conses to represent s-expressions? Maybe that is a good role for cons."
Arrays, objects, pretty much anything that can represent nested sequences. In fact, in PyArc, conses are a class, because Python has an infatuation with classes.
I don't see conses as an "ultimate abstraction". They're a low-level primitive that can be used to create lists, among other things. To put it bluntly, they're a simple way to implement linked lists. The only difference between an array and a linked list is performance, but they have the same semantics.
Most popular languages choose arrays as their default sequence type, but Lisp chose linked lists. I could represent conses as Python lists (which are like arrays), and then define car so it returns list[0] and cdr so it returns list[1:].
As far as Arc programs would be concerned, everything would look exactly the same. The only difference is that certain operations (like prepending) would be slower with arrays than with a linked list. Specifically, (cons 'a some-list) would be O(n) rather than O(1).
So... to say that conses are bad is like saying that arrays are bad. They're both sequence types, that have differing performance characteristics. Use whichever one happens to suit your language/program the best.
Indeed. One might say the real problem with Lisp is that it's not concatenative. ;)
I think the OP's point about cons cells is excellent. I've spent a long time thinking cons cells were some ultimate abstraction only to realize they're more of a pretty hack.
I don't think cons cells are a hack, but I do think it's a hack to use them for things other than sequences. Since we almost always want len(x)=len(cdr(x))+1 for cons cells, rather than len(x)=2, they aren't really useful as their own two-element collection type.
Yeah, I'm tempted to agree with you. In the arc.arc source code, pg even mentions a solution to improper lists: allow any symbol to terminate a list, rather than just nil.
Of course, an easier fix would be to change `cons` so it throws an error if the second argument isn't a cons or nil. Honestly, are improper lists useful often enough to warrant overloading the meaning of cons? We could have a separate data-type for B-trees.
That's one area that I can actually agree with the article, but that has nothing to do with conses in general (when used to create proper lists), only with improper lists. And contrary to what he says, it's not an "unfixable problem", instead it would probably take only 2 lines of Python code to fix it.
One thing though... function argument lists:
(fn (a b . c))
Of course I can special-case this in PyArc, so . has special meaning only in the argument list. This is, in fact, what I do right now. But that may make it harder for implementations in say... Common Lisp or Scheme (assuming you're piggybacking, rather than writing a full-on parser/reader/interpreter).
If so... then you may end up with the odd situation that it's possible to create improper lists, using the (a . b) syntax, but not possible to create improper lists using `cons`
---
By the way... how about this: proper lists would have a type of 'list and improper lists would have a type of 'cons. Yes, it would break backwards compatibility, but it might be a good idea in the long run. Or we could have lists have a type of 'cons and improper lists have a type of 'pair.
I don't know if improper lists are really a problem, just hackish. :) My "solution" would be to remove the need for them by changing the rest parameter syntax (both in parameter lists and in destructuring patterns).
---
"how about this: proper lists would have a type of 'list and improper lists would have a type of 'cons."
I don't think I like the idea of something's type changing when it's modified. But then that's mostly because I don't think the 'type function is useful on a high level; it generally seems more flexible to do (if afoo.x ...) rather than (case type.x foo ..), 'cause that way something can have more than one "type." Because of this approach, I end up using the 'type type just to identify the kind of concrete implementation a value has, and the way I think about it, the concrete implementation is whatever invariants are preserved under mutation.
That's just my take on it. Your experience with 'type may vary. ^_^
"we see the actual example of Arc running in the actual environement, not some different or pretend environment that might be the same as the cell phone or might not."
And from https://eval.to/, after clicking the "start new repl" button:
"Note the connector allows this website to execute arbitrary code on the computer running the connector. If you'd rather not do this on your own personal computer, you may wish to use a virtual machine or an Amazon EC2 instance, etc."
Presuming that most people concerned about security would opt to run the connector through a virtual machine or something other than their personal computer, wouldn't we be effectively running the Arc examples through just the kind of different or pretend environments that we're trying to avoid?
I probably just have some misunderstanding about the project's intent or how the pastebin is going to work. Looking forward to your response. :)
Actually I imagine that many people won't have a problem with running the connector, in much the same way that many people don't have a problem with downloading and running other kinds of software from the Internet on their personal computer.
And... naturally there will be other people who don't want to run the connector, and so the site will be less useful to them.
Since this is a new and different kind of system, I wanted to be clear about what's going on -- so no one is surprised, and can make their own informed decision about if and where they want to run the connector.
I also plan to eventually have an API so that, among other things, people can perform their own runs and post the results to the site.
"The problem is that it's trying to fulfill two different purposes at the same time (addition and concatenation), so there's inconsistencies with regard to numbers."
If your numbers were Church encoded, then I guess there wouldn't be a difference between addition and concatenation. XD (This is given that concatenating functions is the same as composing them.)
Yeah, sure, and let's just use lambdas for everything:
(def cons (x y) (fn (f) (f x y)))
(def car (x) (x (fn (a b) a)))
(def cdr (x) (x (fn (a b) b)))
(car (cons 'a nil)) -> a
:P
On a semi-related note, I had an interest a while back for implementing a Lisp interpreter using only lambda-calculus. I'm still not sure if it's possible or not, but I wanted to give it a try.
All Lisp readers I know of treat '(a . (b c)) the same as '(a b c).
arc> (read "(a . (b c))")
(a b c)
The reason is, '(a . something) = (cons 'a 'something), and if "something" is a list like '(b c d), then '(a . (b c d)) = (cons 'a '(b c d)) = '(a b c d). You can even write code like this.
* (+ 1 . (2 3)) ;SBCL
6
This has the advantage of making it really easy to write a basic print function:
(def print (x)
(if (acons x)
(do (disp "(")
(print (car x))
(disp " . ")
(print (cdr x))
(disp ")"))
<handle remaining cases: x is a symbol, number, string...>))
I'm not too strongly opposed to this change, but it would break compatibility with probably all existing Lisp readers, including Racket's.
This is a bit off-topic, but technically, Racket's syntax reader treats (a b c) and (a . (b c)) differently. It wraps just about every node of the parse tree in a syntax object, but the exceptions are the nodes which, from a certain point of view, aren't even part of the parse tree: The list tails which aren't written using their own parentheses. In particular, the (b c) part of (a b c) isn't wrapped, but the (b c) part of (a . (b c)) is.
Welcome to Racket v5.1.
> (define abc (read-syntax #f (open-input-string "(a b c)")))
> (define a.bc (read-syntax #f (open-input-string "(a . (b c))")))
> (define abc.null (read-syntax #f (open-input-string
"(a b c . ())")))
> abc
#<syntax::1 (a b c)>
> a.bc
#<syntax::1 (a b c)>
> abc.null
#<syntax::1 (a b c)>
> (syntax-e abc)
'(#<syntax::2 a> #<syntax::4 b> #<syntax::6 c>)
> (syntax-e a.bc)
'(#<syntax::2 a> . #<syntax::6 (b c)>)
> (syntax-e abc.null)
'(#<syntax::2 a> #<syntax::4 b> #<syntax::6 c> . #<syntax::10 ()>)
True, the first version is more explicit, and the parens make it clearly distinct from the required arguments, but then again, it does add an extra level of (arguably useless) parens. I still think it looks nicer than Arc's current version, though:
If you're going to create a special syntax for optional arguments, then I agree with you. But why uphold the required arg / optional arg distinction at all? If you make function arguments always optional (as they are in JavaScript) then you need no special syntax, and it is more concise.
As somebody who has programmed for years in JavaScript, I am certainly used to that style, but I actually like required arguments, even if simply for error checking. If I call a function, and it spits back an error, "function expected 3 arguments (2 given)" then I have a pretty good idea of what to do. When the function call fails silently, however, it can lead to some icky debugging situations.
On the other hand, even if you made all arguments default to nil, you could still have some error checking:
(def foo (a b c))
(foo) ; (a b c) are (nil nil nil)
(foo 1 2) ; (a b c) are (1 2 nil)
(foo 1 2 3) ; (a b c) are (1 2 3)
(foo 1 2 3 4) ; error: expected 0 to 3 arguments (4 given)
In other words, it would still be possible to throw an error when calling with too many arguments, but not when calling with too few.
I'm pretty okay with that tradeoff, actually. It gives up a bit of safety and control, but in exchange it avoids the whole icky mess with "what syntax do we use for optional arguments?" I might try that approach with my interpreter.
In fact, we could reverse the question. Rather than asking, "how do we define optional arguments?" we can instead ask "how do we define required arguments?"
In other words, all arguments default to nil, but it's possible to specify certain arguments as required, or to change what the default is. That may be a better approach than specifying what arguments are optional.
There is one problem with that approach, though. You'll likely want a way to specify what the default for an argument is, which would require some sort of construct. But then if you want to specify a required parameter, you'll need a second (different) construct.
If you have required arguments by default, then you can use a single construct to specify both optional arguments and what their defaults are. But if you have optional arguments by default, you need two constructs.
Unless you're suggesting to not have required arguments at all, and require the programmer to manually write an (if (no a)) check and manually throw an error? That could work, assuming most people don't need required arguments most of the time.
You could even wrap it up in a macro or something, like this:
(def foo (a b c)
(require a))
Which could be extended for type information as well:
(def foo (a b c)
(require a 'sym))
Which would specify that the first argument is required, and must be a symbol. I kinda like that approach, though it does mean running checks at runtime. On the other hand, how frequently do you actually need those checks? Not very often, right? So that could work, I think.
Here's a more functional version of require that works in the current version of arc:
(mac require (n (o typ))
`(if (and ,typ ,n (no:isa ,n ,typ))
(err:string "parameter "
',n
" must be of type "
,typ)
(no ,n)
(err:string "parameter " ',n " is required")))
That's because ((fn (nil) nil) 10) is supposed to be the same as ((fn (()) nil) 10), which destructures the first 0 elements from 10, treating it as a degenerate cons list.
I actually use this behavior in practice, 'cause it's useful to have at least some parameter syntax that doesn't bind any variables, rather than just using a conventional name like "ignored" or "_". Most of the time I use it in a much-too-big 'withs block, where I abuse one of the bindings for sanity-checking or other side effects. ^^ I wouldn't worry about it too much, though.
On the other hand, nil's behavior makes it more verbose to determine whether something's usable as a local variable; you have to check (and x (isa x 'sym) (~ssyntax x)). In Lathe, I define 'anormalsym to do just that. (It might be more accurate to change it to (and (isa x 'sym) (~in x nil t 'o) (~ssyntax x)).)
Speaking of (~ssyntax x), ssyntax is usable for local variable names, but naturally, you can only get the values from the Racket side:
Personally, I'd like for ssyntax-burdened names in parameter lists to use user-defined parameter syntaxes, like custom forms of destructuring and whatnot. And then, just for axiomatic simplicity's sake, I'd like things like 'o to be implemented using the same system, using ssyntax-burdened names like ".o".
"That's because ((fn (nil) nil) 10) is supposed to be the same as ((fn (()) nil) 10)"
nil is such a weird value... it's false, a symbol, and an empty list all at once. I actually had to do some hacky stuff to get () and '() and nil to behave properly.
"Speaking of (~ssyntax x), ssyntax is usable for local variable names, but naturally, you can only get the values from the Racket side:"
I plan to implement ssyntax at the reader level in my interpreter, so that shouldn't be an issue.
"nil is such a weird value... it's false, a symbol, and an empty list all at once."
Perhaps nil should serve as the base case for every type, rather than just some of them. In Arc, it already does this for booleans, lists and strings (""), but it could be extended to support tables (#hash()), numbers (0), symbols (||) etc. Then it would have more consistency as a concept.
I've run into issues with nil the symbol vs nil the value. It's caused some issues when I wanted to use arc's sym type to represent variable names in a class compiler project I'm working on. If the variable's name is nil, all kinds of things stop working, and I've had to resort to calling some scheme functions directly to get proper handling of symbols.
I wish that 'nil and nil could be kept somewhat separate, with the first being just a symbol, and the second being equivalent to the base value for all data structures. Otherwise the symbol type is broken and inconsistent, since you cannot accurately (or at least completely) represent code as data.
However, I can easily define nil so that it's type is 'cons, but it would throw an error if you try to assign to the car or cdr. That may break code that assumes that 'cons != nil though.
Actually, I could represent nil as an actual 'cons cell, so that assigning to the car or cdr would work. Crazy? Probably. Especially since nil is a singleton and you can't create more nil's, so it would be a global change.
Right now, nil does have a type of 'sym, but it seems weird to treat it mostly like an empty cons cell, but not have it's type be 'cons. So I figured I could play around with it and try giving it a type of 'cons and see how badly it breaks stuff.
"Actually, I could represent nil as an actual 'cons cell, so that assigning to the car or cdr would work. Crazy?"
That's a bit crazy. :)
PicoLisp does something reminiscent. Every one of its data structures (numbers, symbols, nil and conses) is implemented using the low-level cons cell structure (i.e. a pair of machine words). [1] They talk about nil's representation fulfilling its dual nature as both a symbol whose value is nil and a list whose car and cdr are nil; both the symbol predicate and the list predicate return true when applied to nil:
: (sym? NIL)
-> T
: (lst? NIL)
-> T
I'm not sure that they let nil's car and cdr be assignable though, because "NIL is a special symbol which exists exactly once in the whole system." [2]
My code has a lot of [string:or _ "nil"] to handle that kind of stuff. ^_^ I've just kinda accepted it ever since http://arclanguage.org/item?id=10793.
I do 'string lists into strings a lot, but that would continue to work if 'nil and '() were separate values.
If the variable's name is nil, all kinds of things stop working
Can you give an example? Are you trying to use nil as a variable name in Arc, or simply to have a data structure representing variables where some of those variables are named nil?
I'm creating a compiler for a class, and I've been representing variable names in the ast with symbols. I could use strings instead, and may end up doing so, but symbols seemed a more elegant solution.
So nil would be an ubertype that basically just means "empty"? One issue with that is (is 0 nil) would be t... I think it's useful to not always treat 0 as the same as nil.
Not sure about empty tables, though... maybe it would be fine to treat those as nil. We already treat an empty list as nil, after all.
Actually, what you're suggesting is very similar to how languages like JavaScript and Python do it, with 0 null undefined NaN "" etc. being falsy. So in JS, you can do this:
var foo = 0;
if (foo) {} // will not be evaluated
foo = "";
if (foo) {} // will not be evaluated
foo = NaN;
if (foo) {} // will not be evaluated
On the other hand, both those languages support actual Booleans, and they treat an empty array as true.
As far as prior work goes, I think this topic is the last time "can't rebind t" came up: http://arclanguage.org/item?id=13080 It's mainly just me linking back to those other two pages, but it's also a pretty good summary of my own opinion of the issues involved.
In my runtime project, nil and t are ordinary global variables... if someone turns out to want the rebind protection feature, I'll add it as a compiler extension (which other people could then choose to apply, or not, as they wished).
What I would do is make it print a warning, but still allow it. Something like, "hey, you! it's a bad idea to rebind nil; you'll probably break everything! use (= nil ()) to fix the mess you probably made"
Printing a warning is a good idea. Whether rebinding nil could be fixed with "(= nil ())" is an interesting question, you might (or might not, I haven't tried it) find that rebinding nil breaks Arc so badly that = no longer works... :-)
I see a potential for a contest here: how to permanently break Arc (with "permanently" meaning you can't recover by typing something at the REPL), in the most interesting way, using the fewest characters. (Non-interesting being, for example, going into an infinite loop so that you don't return to the REPL).
Quite possibly. It should work in my interpreter, though. Actually, () and nil are two different values, but they're special-cased to be eq to each other. It was the only way I found to make one print as "nil" and the other print as "()" From an external point of view, though, they should seem the same.
Hmm... the issue with that is that you might start having to quote nil or t whenever you want to actually mean nil or t, instead of just typing them in normally.
I do wish there was an easier way to tell whether or not a value was provided as nil, or was left empty and defaults to nil. Maybe doing destructuring on rest args would help solve that problem in most cases?
"I do wish there was an easier way to tell whether or not a value was provided as nil, or was left empty and defaults to nil."
I too have sometimes wished for that in JavaScript, but let me tell you a little story. I was writing a syntax highlighter, and got it working fine in Chrome and Firefox 3.5, but there was a bug in Firefox 3.0.
You see, I was using this bit of code here:
output.push(text.slice(curr.index[1], next && next.index[0]));
If `next` doesn't exist, it will pass the value `undefined` to the `slice` method. In JS, if you don't pass an argument, it defaults to `undefined`, so this is supposed to behave like as if I hadn't passed in the argument at all.
But in Firefox 3.0, the slice method behaves differently depending on whether you pass it `undefined`, or don't pass it any arguments. So, I had to use this instead:
if (!next) {
output.push(text.slice(curr.index[1]));
} else {
output.push(text.slice(curr.index[1], next.index[0]));
}
This was (thankfully) fixed in 3.5. The moral of the story: most of the time it doesn't matter whether the caller passed nil, or didn't pass anything. You can treat the two situations as the same.
Consider this hypothetical example in Arc:
(your-func 5 (and x y z))
If x, y, or z are non-nil, it will be passed in as usual. On the other hand, if any of them are nil, it will be like as if you had used (your-func 5 nil).
By behaving differently when nil is passed in vs. not passing in an argument, you might cause the above example to break. Or perhaps it would work, but the behavior would be subtly different... introducing bugs.
By having different behavior depending on whether an argument is passed or not, you force callers to do this, instead:
(iflet val (and x y z)
(your-func 5 val)
(your-func 5))
Note the redundancy. In fact, this is even more important in Arc (compared to JavaScript) because you can use any expression, such as (if), a macro call, etc.
So... let me ask: what situations do you really need to know whether the caller actually passed in nil, or didn't pass anything at all?
Great point. In fact, I don't check whether an optional argument was passed very often, and the times I do, I usually expect to regret it at some point, for exactly that reason. ^_^
"I do wish there was an easier way to tell whether or not a value was provided as nil"
I share this sentiment. One thing we could do is have a space of hidden-from-the-programmer variables which tell you whether other variables have been bound. They can be accessed using a macro:
(= given-prefix* (string (uniq) "-given-"))
(mac given (var)
; NOTE: I don't think this will work properly for nil, but nil is
; never a local variable name anyway.
(sym:+ given-prefix* var))
The implementation of argument lists would need to be aware of 'given-prefix* and bind the prefixed variables at the same time as the regular ones.
---
"Maybe doing destructuring on rest args would help solve that problem in most cases?"
Well, if you use a rest arg for all optional values, and then use some form of destructuring bind on that list to extract your optional arguments, then you can tell whether or not they were passed in or merely defaulted to nil by just searching the arg list.
(def test args
(if (assoc 'c args)
(pr "c was passed")
(pr "c was not passed")))
I still don't follow. We can already manage the argument list manually, but in most of the suggestions here, we can only do it if we don't destructure it in the signature (unless we use more complicated kinds of destructuring).
; Current options:
(def test args
(let ((o c)) args
(pr:if (len> args 0)
"c was passed"
"c was not passed")))
(let missing list.nil ; This is just something unique.
(def test ((o c missing))
(pr:if (is c missing)
"c was not passed"
"c was passed")))
; Some hypothetical options and non-options:
(def test (& (c))
(pr "no way to tell if c was passed"))
(let missing list.nil
(def test (& (c))
(pr "still no way to tell if c was passed")))
(def test (& args)
(let ((o c)) args
(pr:if (len> args 0)
"c was passed"
"c was not passed")))
(def test (& (&both args (c))) ; Destructure twice.
(pr:if (len> args 0)
"c was passed"
"c was not passed"))
(def test ((o c nil c-passed))
(pr:if c-passed
"c was passed"
"c was not passed"))
(def test ((o c))
(pr:if given.c
"c was passed"
"c was not passed"))
(def test (c) ; Parameter lists are just destructuring.
(pr:if given.c
"c was passed"
"c was not passed"))
(def test (&both args (c))
(pr:if (len> args 0)
"c was passed"
"c was not passed"))
It fails on functions that take required and rest args, though:
(defreq foo (x y . args) (list x y args)) -> error
Err... right, you were talking about detecting if an argument was nil or not given... but I realized that the same technique could be used to write a version of def that implements required arguments even in a language where every argument is optional.
Only if you actually rebind them. It's like using `quote` as a variable name: you can do it, but most people won't because that's silly. I just think it's nice to allow it, on the off chance it's actually useful. It just feels weird to arbitrarily say "you can't rebind nil and t" but allow rebinding of everything else.
The and is required to make sure that a type was provided, otherwise it will always fail the type check. Also, if you leave n out of the and clause, it will still pass if the required type is sym. Maybe that should be fixed.
Hm... yes, you're right. Odd, I remember it working fine when I tested it earlier. This should work correctly:
(mac require (n (o t))
`(if (no ,n) (err:string "parameter " ',n " is required")
(and ,t (no:isa ,n ,t)) (err:string "parameter " ',n " must be of type " ,t)))
Make all arguments optional, as per evanrmurphy's suggestion. I'll still support the (o foo) form for backwards compatibility, but I think it has so many problems that it really should be avoided.
This isn't final, but I figure the simplicity of not supporting required parameters will be a net gain most of the time. If you really need required parameters, it's possible to write macros that do the checks at run-time. I might even provide such a macro in arc.arc so you don't have to write it yourself.
Also, for telling Arc what the default is (it's normally nil), you use the (= foo 'bar) form, like so:
(def foo ((o a 1) (o b 2))) ; pgArc
(def foo ((= a 1) (= b 2))) ; PyArc
I also plan to support argument-ssyntax, which is basically ssyntax that is only expanded in the argument list. Then you can do this:
(def foo (a=1 b=2))
This should, of course, be customizable within Arc.
That should work in Clojure, you're right. + But the analogous code in an arc-like would not work because (. (a b)) is not a valid list (since it has no car).