> > Okay, here's an example of a couple macros I use all the time. Since I > > write a lot of unit tests, I like to have a clean syntax for > > expressing the essence of a set of related tests. So I have a macro > > DEFTEST which is similar to DEFUN except it defines a "test function" > > which has some special characteristics. For one, all test functions > > are registered with the test framework so I can run all defined tests. > > And each test function binds a dynamic variable to the name of the > > test currently being run which is used by the reporting framework when > > reporting results. So, to write a new test function, here's what I > > write:
> Sweet! So you use this for dynamic testing of code as a debugging > tool? What other capabilities does it give you? Can you query the > active tests, "unregister" a test that you don't want run anymore, > selectively run tests based on conditional code, etc?
Yup. Well in theory. I haven't gotten around to implementing all that mostly because I haven't quite figured out the UI I want to have on top of it. But all the information required is there.
> It sounds like it might be a nice framework to release.
I intend to, once I get this other, er, project off my plate. ;-)
> > But the real payoff comes when we realize that innocent looking > > CHECK is also a macro. Thus to see what the *real* benefit of > > macros is we need to compare the original four-line DEFTEST form > > to what it expands into (i.e. what the compiler actually compiles) > > when all the subsidiary macros are also expanded. Which is this:
> [really long PROGN snipped]
> > Note that it's the ability, at macro expansion time, to treat the > > code as data that allows me to generate test failure messages that > > contain the literal code of the test case *and* the value that it > > evaluated to.
> Okay, maybe you already have done so, but if not, might I suggest > that you use this example in your book?
I'm way aheead of you. ;-) If you want a sneak preview, check out:
I'm sure I'll be doing a lot more work on that chapter, and perhaps splitting it up so I can introduce a simple test framework early in the book and then make it more sophisticated as I introduce more advanced language features.
Rayiner Hashem wrote: >>The vast majority of people who design languages have had minimal, >>if any, exposure to Lisp.
> To be fair, the designers of Java really do have a lot of Lisp experience. > Its just that they intentionally chose to restrict the language according > to their belief that a free-form, expressive language did not mesh-well > with the types of development models you have in internet companies. You > may disagree with that conclusion, but you can't disagree with their > backgrounds.
> PS> You'd be surprised how many language designers like and have exposure > Lisp. Even Bjarne Stroustrup is a fan of CL + CLOS in addition to ML. If > you read the new XTI proposal, you'll see bits of Lisp-y stuff in there > (even a prefix notation.)
Pascal Costanza <costa...@web.de> writes: > Rayiner Hashem wrote: >>>The vast majority of people who design languages have had minimal, >>>if any, exposure to Lisp. >> To be fair, the designers of Java really do have a lot of Lisp >> experience. >> Its just that they intentionally chose to restrict the language according >> to their belief that a free-form, expressive language did not mesh-well >> with the types of development models you have in internet companies. You >> may disagree with that conclusion, but you can't disagree with their >> backgrounds.
hanzs...@yahoo.com.au (Hannu Kankaanp??) writes: > Joe Marshall <j...@ccs.neu.edu> wrote in message <news:8ynyf5m8.fsf@ccs.neu.edu>... >> Peter Seibel <pe...@javamonkey.com> writes:
>> >> You're missing the point. Suppose Erann and I were working on some >> >> project and he had written the above macro. I don't have to know >> >> the details of implementation (I'm assuming that I can trust Erann >> >> not to write bogus code). Using the macro is *far* preferable to >> >> something like:
>> >> Especially because there is a non-obvious bug in the above code.
>> > Not obvious to you or me maybe. But Emacs Knows(tm). ;-)
>> Heh heh heh.... yet another reason that indentation is a poor choice >> for block delimiting.
> Hmm. In a previous reply you answered sarcastically that indentation > is a poor choice, so I doubt this is again sarcasm saying the contrary.
> So getting to the point, doesn't the example show that indentation > is in fact a *good* choice for block delimiting? It's an alternative that > wouldn't have the subtle bug you introduced here. So how can you > claim that this example shows indentation is a poor choice for block > delimiting? It doesn't make sense.
The point was that even though I screwed up the indentation, it was easily discovered and repaired with Emacs. If the program were whitespace-sensitive, then the screwed-up version would be mechanically indistinguishable from the intended one.
But I had a thought last night. I assume that Python has comments. I can imagine a `structural' editor that hid block information in comment fields in Python, but didn't display them to the user (and obviously Python wouldn't care about comments). This sort of editor *would* be able to detect and repair the kind of mistakes I'm talking about. (And when Microsoft gets around to releasing its Python derivative, I bet the `intellicode' system does just that.)
> The problem with the example arises from the fact that indentation > is used for human readability, but parens are used by the parser. > A clash between these two representations can lead to subtle bugs > like this one. But remove one of the representations, and there > can't be clashes. Of course removing indentation would lead > super-ugly code, so why not just remove parens like has been > done in Python? I haven't heard of people swearing about bugs > introduced by this syntax choice, nor have I personally ever > made a mistake with indentation.
> collect = [] > for l in some_file_name > if some_property: > collect.append(l)
> ...is another solution for the single collector case. Now we have two > ways to do it. Isn't this supposed to be a bad sign in the context of > Python? I am confused...
It's all about what's the "correct" way to do it. In this case, it would be
collect = [l for l in some_file_name if some_property]
And this would be expanded to the for-loop form *only* if it is needed. Of course I can do
x = 5 x += y
Or I can do
x = 5 + y
That's not breaking against Python's principles. The latter way is the right way.
(surely there are many debatable cases of which is the right way, but the principle is adhered to as often as possible. That's the best one can ever have)
just as a comment from the peanut gallery: the first thought which will occur when i see the contents entry subtitled "unit test framework", will be to check the references to the others - eg the one p.dietz uses which derives from the utility from the ai lab, the test harness associated with acl, clunit, xptest, ... to see whether they are footnotes or substantial expositions of differences - something a bit more substantial than the cliki entry.
among other things, - the "UI" issues, to which you alluded in your note; especially naming; - logistic matters - where do they live - in the source, or in isolation; - why don't any of them support descriptions of serious relations among tests. for example, while xptest defines isolated groups and clunit defines categories no tool provides a way to describe either intrinscially or extrinsically relations - for example interdependancy, versioning, supersesssion, system dependancy - among the tests.
even for a middling-piddling application, none of this scales well.
> I'm sure I'll be doing a lot more work on that chapter, and perhaps > splitting it up so I can introduce a simple test framework early in > the book and then make it more sophisticated as I introduce more > advanced language features.
>>> seq = range(10) >>> test = lambda x: x%3==0 >>> pos,neg = [(p,n) for p,n in[map(list,['']*2)] ... for q in [(x,test(x),i) for i,x in enumerate(seq)] ... if q[1] and p.append(q[0]) or not q[1] and n.append(q[0]) or not q[2]][0] >>> pos [0, 3, 6, 9] >>> neg [1, 2, 4, 5, 7, 8]
Alex Martelli <al...@aleax.it> writes: > Alexander Schmolck wrote: > > Just for the record, in python all you'd write is: v[:] = a > I suspect you may intend "v[:] = [a]*len(v)", although a good alternative > may also be "v[:] = itertools.repeat(a, len(v))".
Thanks for the correction (I sent this one of prematurely and then the network broke down for 2 days).
``v[:] = a`` will actually work fine on Numeric/numarray arrays (thanks to broadcasting), and presumably this scenario will occur more often in more in pratice than the need to similarly destructively fill a (python) list (of course Numeric/numarray are currently add-on's but I'd guess they'll make it into python core eventually). In either case, as you demonstrate above there is no need to write a for loop.
def multisplit(seq, *preds): predn = len(preds) bins = [[] for i in range(predn+1)] predpends = [(p,b.append) for (p,b) in zip(preds,bins)] rpend = bins[predn].append for item in seq: for pred,pend in predpends: if pred(item): pend(item) break else: rpend(item) return bins
raff...@mediaone.net (Raffael Cavallaro) writes: > gr...@cs.uwa.edu.au wrote in message <news:blr1cq$bb1$1@enyo.uwa.edu.au>... > > In comp.lang.functional Erann Gat <my-first-name.my-last-n...@jpl.nasa.gov> wrote: > > :> I can't see why a LISP programmer would even want to write a macro. > > : That's because you are approaching this with a fundamentally flawed > > : assumption. Macros are mainly not used to make the syntax prettier > > : (though they can be used for that). They are mainly used to add features > > : to the language that cannot be added as functions.
> > Really? Turing-completeness and all that... I presume you mean "cannot > > so easily be added as functions", but even that would surprise me. > > (Unless you mean cannot be added _to_Lisp_ as functions, because I don't > > know as much as I'd like to about Lisp's capabilities and limitations.)
> Two words: code duplication.
> Yes, anything that can be done with macros can also be done with > functions, but if you do it with functions, you will end up with more > code, and that code will be duplicated in every single source location > in which that abstraction it utilized.
Three words and a hyphen: Higher-Order Functions.
Most of the things that macros can do can be done with HOFs with just as little source code duplication as with macros. (And with macros only the source code does not get duplicated, the same not being true for compiled code. With HOFs even executable code duplication is often avoided -- depending on compiler technology.)
I say "most" because there are some things that macros can do which are difficult with HOFs -- but those things have to do with compile-time calculations. A good example that was given to me during the course of a similar discussion here on netnews is that of a parser-generator macro: it could check at compile time that the input grammar is, e.g., LL(1). With HOFs this check would have to happen at runtime, or at link-time at the earliest. (Some languages, e.g., SML can do arbitrary calculations at what we normally call "link time".)
Many static guarantees can be obtained by relying on reasonably powerful static type systems, but I know of none that is powerful enough and practical as well as general at the same time which would be able to check for LL(1)-ness. That's why macros sometimes "beat" HOFs. For most things they don't.
> With a macro, the abstraction is defined once, and the source code > reflects that abstraction everywhere that abstraction is used > throughout your program. For large projects this could be hundreds of > source locations.