A critic of Guido's blog on Python's lambda

B

Boris Borcic

Bill said:
It's interesting how much people who don't have macros like to put
them down and treat them as some arcane art that are too "*insane*"ly
powerful to be used well.

They're actually very straightforward and can often (shock of shocks!)
make your code more readable, without your efficiency taking a hit.

Not even efficiency of debugging ? A real problem with macros is that run-time
tracebacks etc, list macro outputs and not your macro'ed source code. And that
becomes an acute problem if you leave code for somebody else to update. Or did
lisp IDEs make progress on that front ?
 
D

David C. Ullrich

[...]

If you, um, look at the code you see that "cells.a = 42" triggers
cells.__setattr__, which fires a's callback; the callback then
reaches inside and sets the value of b _without_ going through
__setattr__, hence without triggering b's callback.

In Cells you can't have A depend on B and also B depend on A?
That seems like an unfortunate restriction - I'd want to be
able to have Celsius and Farenheit, so that setting either
one sets the other.

Realized later that I hadn't thought this through.

I'd been assuming that of course we should be allowed to
have A and B depend on each other. Hence if a change in
A propagates to a change in B that change in B has to
be a non-propagating change - thought I was just so
clever seeing a way to do that.

But duh, if that's how things are then we can't have
transitive dependencies working out right; surely we
want to be able to have B depend on A and then C
depend on B...

(And also if A and B are allowed to depend on each
other then the programmer has to ensure that the
two rules are inverses of each other, which seems
like a bad constraint in general, something non-trivial
that the programmer has to get right.)

So fine, no loops. If anything, if we know that
there are no loops in the dependencies that simplifies
the rest of the programming, no need for the sort of
finagling described in the first paragraph above.
But this raises a question:

Q: How do we ensure there are no loops in the dependencies?

Do we actually run the whole graph through some algorithm
to verify there are no loops?

The simplest solution seems like adding the cells one
at a time, and only allowing a cell to depend on
previously added cells. It's clear that that would
prevent loops, but it's not clear to me whether or
not that disallows some non-looping graphs. A
math question the answer to which is not immediately
clear to me (possibly trivial, the question just
ocurred to me this second):

Say G is a (finite) directed graph with no loops. Is it always
possible to order the vertices in such a way that
every edge goes from a vertex to a _previous_ vertex?
Of course if there are no loops it's easier to see how
you managed to do the stuff you were talking about elsewhere,
(at least sometimes) delaying execution until needed.
Anyway...)
all
the *programmer* has to care about is what values a slot depends on --
Cells takes care of inverting that for you, which is important because
that's a job that a computer is much better at than a human.


Fine. I suppose that is better; if b is going to return a + 1
the fact that this is what b returns should belong to b, not
to a. So a has an update list including b, so when a's value
is set a tells b it needs to update itself.

If we're allowed to pass (at some point, to some constructor
or other) something like (b, a + 1, [a]), which sets up a
cell b that shall return a + 1, and where the [a] is used
in the constructor to tell a to add b to a's update list
then this seems like no big deal.

And doing that doesn't seem so bad - now when the programmer
is writing b he has to decide that it should return a + 1
and also explicitly state that b shall depend on a; this
is all nice and localized, it's still _b_ telling _a_ to
add b to a's update list, and the programmer only has
to figure out what _b_ depends on when he's writing _b_.
Doesn't seem so bad.

But of course it would be better to be able to pass just
something morally equivalent to (b, a + 1) to whatever
constructor and have the system figure out automatically
that since b returns a + 1 it has to add a to b's update
list. There must be some simple trick to accomplish that
(using Python, without parsing code).

Right, you do not want to parse code. It really would not work as
powerfully as Cells, which notice any dynamic access to another cell
while a rule is running. So my rule can call a function on "self" (the
instance that wons the slot being calculated, and since self can have
pointers to other instances, the algorithm can navigate high and low
calling other functions before finally reading another ruled slot. You
want to track those.
Exactly what the trick is I
don't see immediately.

To compute a value for a slot that happens to have a rule associated
with it, have a little cell datastructure that implements all this and
associate the cell with the slot and store a pointer to the rule in the
cell. Then have a global variable called *dependent* and locally:

currentdependent = *dependent*
oldvalue = cell.value
newvalue = call cell.rule, passing it the self instance
*dependent* = currentvalue

if newvalue not = oldvalue
call on-change on the slot name, self, newvalue and oldvalue
(the on-chnage needs to dispatch on as many arguments as
the language allows. Lisp does it on them all)

In the reader on a slot (in your getattr) you need code that notices if
the value being read is mediated by a ruled cell, and if the global
*dependent* is non empty. If so, both cells get a record of the other
(for varying demands of the implementation).
In Cells do we just pass a rule using other cells to
determine this cell's value, or do we also include
an explicit list of cells that this cell depends on?

Again, the former. Just write the rule, the above scheme dynamically
figures out the dependencies. Note then that dependencies vary over time
because of different branches a rule might take.

I want to reassure the community that this (nor the spreadsheet analogy
<g>) is not just my crazy idea. In 1992:

http://www.cs.utk.edu/~bvz/active-value-spreadsheet.html

"It is becoming increasingly evident that imperative languages are
unsuitable for supporting the complicated flow-of-control that arises in
interactive applications. This paper describes a declarative paradigm
for specifying interactive applications that is based on the spreadsheet
model of programing. This model includes multi-way constraints and
action procedures that can be triggered when constraints change the
values of variables."

Cells do not do multi-way constraints, btw.

My _guess_ is that a "multi-way constraint" is something like
what's above, where A depends on B and B depends on A?
Nor partial constraints. To
hard to program, because the system gets non-deterministic. That kinda
killed (well, left to a small niche) the whole research programme. I
have citations on that as well. :)

kenny


************************

David C. Ullrich


************************

David C. Ullrich
 
M

Marcin 'Qrczak' Kowalczyk

Followup-To: comp.lang.lisp

Bill Atkins said:
The cool thing about ITERATE is that it lets you express looping
concepts in a language designed explicitly for such a purpose, e.g.

(iter (for x in '(1 3 3))
(summing x)) => 7

(iter (for x in '(1 -3 2))
(finding x maximizing (abs x))) => -3

(iter (for x in '(a b c 1 d 3 e))
(when (symbolp x)
(collect x))) => (a b c d e)

While such macros indeed allow to generate efficient code, I don't
find these examples convincing in terms of readability. The same
examples in my language where iteration is based on higher order
functions are shorter and also clear:

Sum [1 3 3]
=> 7

[1 (-3) 2]->MaximumBy Abs
=> -3

[#a #b #c 1 #d 3 #e]->Select (_ %Is SYMBOL)
=> [#a #b #c #d #e]
 
C

Chris F Clark

David C Ullrich asked:
Q: How do we ensure there are no loops in the dependencies?

Do we actually run the whole graph through some algorithm
to verify there are no loops?

The question you are asking is the dependency graph a "directed
acyclic graph" (commonly called a DAG)? One algorithm to determine if
it is, is called "topological sort". That algorithm tells you where
there are cyclces in your graph, if any, and also tells you the order
of the dependencies, i.e. if x is updated, what you have to update
downstream AND THE ORDER you have to perform the downstream
computations in. We use this algorithm for solving just the kind of
dataflow problems you are talking about in our circuit design tools.
Circuit designs have one-way dependencies that we want to sort and
resolve--similarly, we don't want cycles in our circuits except ones
that pass through clocked flip-flops. We solve such problems on
circuits with millions of gates, i.e. enough gates to represent the
CPU of your computer or a disk controller chip or a router.

I believe there are also algorithms that allow you to construct only
acyclic (the technical term for non-looping) graphs and don't require
you to enter the vertexes (verticies if you prefer) of the graph in
any specific order, and in the worst case you can always run the
topological sort on any graph and determine if the graph is cyclic.

The area is well-studied and you can find a variety of algorithms that
solve most interesting graph problems as they all occur over-and-over
in numerous diverse fields.

Hope this helps,
-Chris

*****************************************************************************
Chris Clark Internet : (e-mail address removed)
Compiler Resources, Inc. Web Site : http://world.std.com/~compres
23 Bailey Rd voice : (508) 435-5016
Berlin, MA 01503 USA fax : (978) 838-0263 (24 hours)
------------------------------------------------------------------------------
 
K

Ken Tilton

sross said:
AFAIK It should be fine.
In LW, SBCL and ACL all bindings of dynamic variables are thread-local.

Ah, I was guilty of making an unspoken segue: the problem is not with
the *dependent* special variable, but with the sequentially growing
numeric *datapulse-id* ("the ID") that tells a cell if it needs to
recompute its value. The ID is not dynamically bound. If threads T1 and
T2 each execute a toplevel, imperative assignment, two threads will
start propagating change up the same dependency graph... <shudder>

Might need to specify a "main" thread that gets to play with Cells and
restrict other threads to intense computations but no Cells?

Actually, I got along quite a while without an ID, I just propagated to
dependents and ran rules. This led sometimes to a rule running twice for
one change and transiently taking on a garbage value, when the
dependency graph of a Cell had two paths back to some changed Cell.

Well, Cells have always been reengineered in the face of actual use
cases, because I am not really smart enough to work these things out in
the abstract. Or too lazy or something. Probably all three.

kenny

--
Cells: http://common-lisp.net/project/cells/

"Have you ever been in a relationship?"
Attorney for Mary Winkler, confessed killer of her
minister husband, when asked if the couple had
marital problems.
 
A

Alex Martelli

M Jared Finder said:
Your reasoning, taken to the extreme, implies that an assembly language,
by virtue of having the fewest constructs, is the best designed language

Except that the major premise is faulty! Try e.g.
<http://docs.sun.com/app/docs/doc/817-5477/6mkuavhrf#hic> and count the
number of distinct instructions -- general purpose, floating point,
SIMD, MMX, SSE, SSE2, OS support... there's *hundreds*, each with its
own rules as to what operand(s) are allowed plus variants such as (e.g.)
cmovbe{w,l,q} for "conditional move if below or equal" for word, long,
quadword (no byte variant) -- but e.g cmpxchg{b,w,l,q} DOES have a byte
variant too, while setbe for "set if below or equal" ONLY has a byte
variant, etc, etc -- endless memorization;-).

When you set up your strawman arguments, try to have at least ONE of the
premises appear sensible, will you?-)

I never argued against keeping languages at a high level, of course
(that's why your so utterly unfounded argument would be a "strawman"
even if it WAS better founded;-).
prone, code. I think the advantages of anonymous functions: ...
e) making the language simpler to implement

Adding one construct (e.g., in Python, having both def and lambda with
vast semantic overlap, rather than just one) cannot "make the language
simpler to implement" -- no doubt this kind of "reasoning" (?) is what
ended up making the instruction-set architecture of the dominant
families of CPUs so bizarre, intricate, and abstruse!-)


Alex
 
D

David Hopwood

M said:
Your reasoning, taken to the extreme, implies that an assembly language,
by virtue of having the fewest constructs, is the best designed language
ever.

Assembly languages don't have the fewest constructs; kernel languages such
as Core ML or Kernel-Oz do. In any case, I didn't read Alex's point as
being that simplicity was the only criterion on which to make decisions
about what practices a language should enforce or facilitate; just
"a good criterion".

However, IMHO anonymous lambdas do not significantly increase the complexity
of the language or of programs, and they can definitely simplify programs in
functional languages, or languages that use them for control constructs.
 
K

Ken Tilton

Boris said:
As far as natural language and understanding are concerned, "to mean"
means conformity to what most people understand, Humpty Dumpties
notwithstanding.

Nonsense. You are confusing that quality of natural language with most
people's quality of being sloppy readers, or in your case, a sloppy
thinker. Misapplying an analogy is not a question of usage -- when I
said spreadsheet and they thought of spreadsheets, so far so good,
right? -- it just sloppiness and laziness.

I do it, too, all the time. :) Life is too short, we get by precisely by
using partial information.

You remind me of American educators recent (past several decades, that
is) history of apologizing for asking students to work and softening the
curriculum until they all get A's.

Here is another analogy. Sometimes people hit the gas and think they hit
the brake pedal. They crash around a parking lot pushing the gas pedal
down harder and harder. Did they take out the brake pedal to avoid that
confusion? No, they put an interlock between the brake and the ignition key.

Same thing. :)

kenny

--
Cells: http://common-lisp.net/project/cells/

"Have you ever been in a relationship?"
Attorney for Mary Winkler, confessed killer of her
minister husband, when asked if the couple had
marital problems.
 
K

Ken Tilton

Boris said:
Not even efficiency of debugging ? A real problem with macros is that
run-time tracebacks etc, list macro outputs and not your macro'ed source
code. And that becomes an acute problem if you leave code for somebody
else to update. Or did lisp IDEs make progress on that front ?

AllegroCL now shows macros in the stack frame. Relatively recent
feature, and their IDE really stands out above the rest.

kenny

--
Cells: http://common-lisp.net/project/cells/

"Have you ever been in a relationship?"
Attorney for Mary Winkler, confessed killer of her
minister husband, when asked if the couple had
marital problems.
 
K

Ken Tilton

[Sorry, I missed this one originally.]

[...]

If you, um, look at the code you see that "cells.a = 42" triggers
cells.__setattr__, which fires a's callback; the callback then
reaches inside and sets the value of b _without_ going through
__setattr__, hence without triggering b's callback.

In Cells you can't have A depend on B and also B depend on A?
That seems like an unfortunate restriction - I'd want to be
able to have Celsius and Farenheit, so that setting either
one sets the other.

Set Kelvin, and make Celsius and Fahrneheit functions of that. ie, There
is only one datapoint, the temperature. No conflict unless one creates one.
Realized later that I hadn't thought this through.

I'd been assuming that of course we should be allowed to
have A and B depend on each other. Hence if a change in
A propagates to a change in B that change in B has to
be a non-propagating change - thought I was just so
clever seeing a way to do that.

I think it could be arranged, if one were willing to tolerate a little
fuzziness: no, there would be no strictly correct snapshot at which
point everyone had their "right value". Instead, A changes so B
recomputes, B changes so A recomputes... our model has now come to life,
we just have to poll for OS events or socket data, and A and B never get
to a point where they are self-consistent, because one or the other
always needs to be recalculated.

I sometimes wonder if the physical universe is like that, explaining why
gravity slows time: it is not the gravity, it is the mass and we are
seeing system degradation as the matrix gets bogged down recomputing all
that matter.

[Cue Xah]

But duh, if that's how things are then we can't have
transitive dependencies working out right; surely we
want to be able to have B depend on A and then C
depend on B...

(And also if A and B are allowed to depend on each
other then the programmer has to ensure that the
two rules are inverses of each other, which seems
like a bad constraint in general, something non-trivial
that the programmer has to get right.)

Right, when I considered multi-way dependencies I realized I would have
to figure out some new syntax to declare in one place the rules for two
slots, and that would be weird because in Cells it is the instance that
gets a rule at make-instance time, so i would really have to have some
new make-instance-pair capability. Talk about a slippery slope. IMO, the
big constraints research program kicked off by Steele's thesis withered
into a niche technology because they sniffed at the "trivial"
spreadsheet model of linear dataflow and tried to do partial and
multi-way dependencies. I call it "a bridge too far", and in my
experience of Cells (ten years of pretty intense use), guess what?, all
we need as developers is one-way, linear, fully-specified dependencies.
So fine, no loops. If anything, if we know that
there are no loops in the dependencies that simplifies
the rest of the programming, no need for the sort of
finagling described in the first paragraph above.

Actually, I do allow an on-change callback ("observer" in Cells
parlance) to kick off a toplevel, imperative state change to the model.
Two cells who do that to each other will run until one decides not to do
so. I solve some GUI situations (the classic being a scrollbar thumb and
the text offset, which each at different times control the other, by
having them simply set the other in an observer. On the second
iteration, B is setting A to the value A has already, so propagation
stops (a longstanding Cells feature).

These feel like GOTOs, by the way, and are definitely to be avoided
because they break the declarative paradigm of Cells in which I can
always look at one (anonymous!) rule and see without question from where
any value it might hold comes. (And observers define where they take
effect outside the model, but those I have to track down by slot name
using OO browsing tools.)

But this raises a question:

Q: How do we ensure there are no loops in the dependencies?

Elsewhere I suggested the code was:

(let ((*dependent* this-cell))
(funcall (rule this-cell) (object this-cell)))

It is actually:

(let ((*dependents* (list* this-cell *dependents*)))
(funcall (rule this-cell) (object this-cell)))

So /before/ that I can say:

(assert (not (find this-cell *dependents*)))
Do we actually run the whole graph through some algorithm
to verify there are no loops?

The simplest solution seems like adding the cells one
at a time, and only allowing a cell to depend on
previously added cells. It's clear that that would
prevent loops, but it's not clear to me whether or
not that disallows some non-looping graphs.

As you can see, the looping is detected only when there is an actual
circularity, defined as a computation requiring its own computation as
an input.

btw, a rule /does/ have access to the prior value it computed, if any,
so the cell can be value-reflective even though the rules cannot be
reentrant.
A
math question the answer to which is not immediately
clear to me (possibly trivial, the question just
ocurred to me this second):

Say G is a (finite) directed graph with no loops. Is it always
possible to order the vertices in such a way that
every edge goes from a vertex to a _previous_ vertex?

I am just a simple application programmer, so I just wait till Cells
breaks and then I fix that. :)

kenny

--
Cells: http://common-lisp.net/project/cells/

"Have you ever been in a relationship?"
Attorney for Mary Winkler, confessed killer of her
minister husband, when asked if the couple had
marital problems.
 
J

Jonathan Ellis

Randal said:
Alex> The difference, if any, is that gurus of Java, C++ and Python get to
Alex> practice and/or keep developing their respectively favorite languages
Alex> (since those three are the "blessed" general purpose languages for
Alex> Google - I say "general purpose" to avoid listing javascript for
Alex> within-browser interactivity, SQL for databases, XML for data
Alex> interchange, HTML for web output, &c, &c), while the gurus of Lisp,
Alex> Limbo, Dylan and Smalltalk don't (Rob Pike, for example, is one of the
Alex> architects of sawzall -- I already pointed to the whitepaper on that
Alex> special-purpose language, and he co-authored that paper, too).

That's crazy. Some of the key developers of Smalltalk continue to work
on the Squeak project (Alan Kay, Dan Ingalls, and I'm leaving someone
out, I know it...). So please remove Smalltalk from that list.

I thought it was clear that Alex was talking about "smalltalk gurus who
work for Google."

-Jonathan
 
K

Ken Tilton

Chris said:
David C Ullrich asked:



The question you are asking is the dependency graph a "directed
acyclic graph" (commonly called a DAG)? One algorithm to determine if
it is, is called "topological sort". That algorithm tells you where
there are cyclces in your graph,

Yep. But with Cells the dependency graph is just a shifting record of
who asked who, shifting because all of a sudden some outlier data will
enter the system and a rule will branch to code for the first time, and
suddenly "depend on" on some new other cell (new as in never before used
by this cell). This is not subject to static analysis because, in fact,
lexically everyone can get to everything else, what with closures,
first-class functions, runtime branching we cannot predict... fuggedaboutit.

So we cannot say, OK, here is "the graph" of our application model. All
we can do is let her rip and cross our fingers. :)

kenny
 
M

Michele Simionato

jayessay said:
You are mistaken. In particular, VAR doesn't have dynamic scope.

I said "it could be used to implement this", and since in a previous
post on mine in this
same thread I have shown how to implement thread local variables in
Python I figured
out people would be able to do the exercise for themselves.

Michele Simionato
 
K

Ken Tilton

Ketil said:
Sometimes the best documentation is the code itself. Sometimes the
best name for a function is the code itself.

Absolutely. When I take over someone else's code I begin by deleting all
the comments. Then I read the code. If a variable or function name makes
no sense (once I have figured out what they /really/ do) I do a global
change. Pretty soon the system is "documented". And I usually find a
couple of bugs as the renaming produces things like:

count = count + weight

I think one good argument for anonymous functions is a hefty Cells
application, with literally hundreds of rules. The context is set by the
instance and slot name, and as you say, the rule speaks for itself:

(make-instance 'frame-widget
:bounds (c? (apply 'rect-union (all-bounds (subwidgets self)))))

Why do I have to give that a name? And if the algorithm gets hairier,
well, why is the reader looking at my code? If the reader is debugging
or intending to modify the rule, they damn well better be looking at the
code, not the name and not the comments. (Never a problem with my code. <g>)

kenny

--
Cells: http://common-lisp.net/project/cells/

"Have you ever been in a relationship?"
Attorney for Mary Winkler, confessed killer of her
minister husband, when asked if the couple had
marital problems.
 
B

Boris Borcic

Ken said:
:)

You are confusing that quality of natural language with most
people's quality of being sloppy readers, or in your case, a sloppy
thinker. Misapplying an analogy is not a question of usage -- when I
said spreadsheet and they thought of spreadsheets, so far so good,
right?

No, as Adam Jones pointed out. Like Bush speaking of "crUSAde" after 9/11.
-- it just sloppiness and laziness.

I do it, too, all the time. :)

Right.
 
J

Joe Marshall

Alex said:
Why is that a problem? Even for so-called "global" names, Python
supports a structured, hierarchical namespace, so there can never be any
collision betwen the "globals" of distinct modules (including modules
which happen to have the same name but live in distinct packages or
subpackages) -- I did mention that names could usefully be displayed in
some strcutured form such as apackage.somemodule.thefunction but perhaps
I was too tangential about it;-).

Can you refer to inner functions from the global context? Suppose I
have this Python code:

def make_adder(x):
def adder_func(y):
sum = x + y
return sum
return adder_func

Can I refer to the inner adder_func in any meaningful way?

If I used continuations (I assume you mean in the call/cc sense rather
than some in which I'm not familiar?) I might feel the same way, or not,
but I don't (alas), so I can't really argue the point either way for
lack of real-world experience.

I meant continuations as in the receiver function in
continuation-passing-style. If you have a function that has to act
differently in response to certain conditions, and you want to
parameterize the behavior, then one possibility is to pass one or more
thunks to the function in addition to the normal arguments. The
function acts by selecting and invoking one of the thunks. A classic
example is table lookup. It is often the case you wish to proceed
differently depending upon whether a key exists in a table or not.
There are several ways to provide this functionality. One is to have a
separate `key-exists?' predicate. Another is to have a special return
value for `key not found'. Another is to throw an exception when a key
is not found. There are obvious advantages and drawbacks to all of
these methods. By using continuation-passing-style, we can
parameterize how the table lookup proceeds once it determines whether
or not the key is found. We have the lookup procedure take two thunks
in addition to the key. If the key is found, the first thunk is
invoked on the associated value. If the key is not found, the second
thunk is invoked. We can subsume all the previous behaviors:

(define (key-exists? key table)
(lookup key table
(lambda (value) #t) ;; if found, ignore value, return true
(lambda () #f))) ;; if not found, return false.

(define (option1 key table)
(lookup key table
(lambda (value) value)
(lambda () 'key-not-found)))

(define (option2 key table)
(lookup key table
(lambda (value) value)
(lambda () (raise 'key-not-found-exception))))

(define (option3 key table default-value)
(lookup key table
(lambda (value) value)
(lambda () default-value)))

The unnamed functions act in this regard much like a `local label'. We
wrap two chunks of code in a lambda and the lookup function `jumps' to
the appropriate chunk. (If the compiler knows about thunks, the
generated assembly code really will have local labels and jump
instructions. It can be quite efficient.)

This may look odd and cumbersome, but with a little practice the
lambdas fade into the background and it becomes easy to read.

My point with Matthias, however, was that defining all these
continuations (the thunks) as named internal functions was not only
cumbersome, but it obscured the control flow. Notice:

(define (named-option3 key table default-value)
(define (if-found value)
value)
(define (if-not-found)
default-value)
(lookup key table if-found if-not-found))

When we enter the function, we skip down to the bottom (past the
internal definitions) to run lookup, which transfers control to a
function defined earlier in the code.

There are many reasons to avoid this style in Python, so this probably
won't win you over, but my point is that there are times where
anonymous functions have an advantage over the named alternative and
that disallowing anonymous functions can be as cumbersome as
disallowing anonymous integers.
 
J

jayessay

Michele Simionato said:
I said "it could be used to implement this", and since in a previous
post on mine in this
same thread I have shown how to implement thread local variables in
Python I figured
out people would be able to do the exercise for themselves.

I was saying that you are mistaken in that pep-0343 could be used to
implement dynamically scoped variables. That stands.


/Jon
 
A

aaronwmail-usenet

Cameron said:
....I'm confused, Alex: I sure
think *I* have been writing DSLs as specializations of Python,
and NOT as "a language in its own right"....

I think Alex is suggesting that if they used, for example, a version of
scheme
with a good optimizing compiler they could implement sawzall like
convenience
with almost the same performance, including startup, etc. whereas even
a
highly optimized python based approach would at least have a
comparatively
large startup penalty. For an environment like Google where they
scrape
thru their logs of various sorts doing lots of trivial scans you can
probably save
a lot of money and time on lots of machines by optimizing such scrapes
(but keep your bactine handy). And as the sawzall paper pointed out,
even static
type checks can prevent a lot of wasted machine bandwidth by avoiding
dumb
errors.

But the real question for someone like Rob Pike is why use scheme when
you
can invent another little language instead, I suspect :).

-- Aaron Watters

===

Stop procrastinating soon.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,262
Messages
2,571,056
Members
48,769
Latest member
Clifft

Latest Threads

Top