Perhaps something like writing in tail recursive style to help out an optimising compiler?...
You have this backwards. Optimizing compilers will turn tail-recursive style source into "normal" loops.
You can write a loop recursively, so that:
foo() {
int x=8;
int b=1;
while(x > 0) {
b << 1;
--x;
}
return b; }
becomes
foo() {
return foo-helper(10, 1); } foo-helper(int x, int b) {
if(x <= 0)
return b;
else
return foo-helper(--x, b << 1); }
Recursion in foo-helper is in the tail position. That is, foo-helper only calls itself as the final operation before returning.
Compiling this naively involves a function call per recursion, which on most architectures results in pushing data onto the stack. However, because we are doing tail-recursion, we can do a tail call elimination optimization.
How this works is that the "return" before the recursion is taken to mean that any automatic variables are dead, any stack space used for the arguments is reusable, and the recursive call is really a jump.
That is, when foo-helper calls itself, it really does an argument rewrite and jump, which in effect "pretends" that foo-helper was called with different arguments in the first place.
In other words, tail call elimination turns recursive loops into iterative loops.
Writing in "tail-recursive style" just means making sure your recursion is done in tail position (i.e., attached to a "return"). Some compilers for a variety of languages can identify recursion which is not done in the tail position, and reorder the recursion into tail position (and then the tail calls are eliminated into iterative loops). However, many compilers can't, and many more don't do tail-call elimination at all.:-(
Once you've optimized recursive loops into iterative ones, you can optimize iterative loops however you like, including partially or fully unrolling them.
In summary, recursion is a way of looping, but function calls are not free. In particular, they usually consume stack space. If you only return the result of your recursion, then you are tail-recursing. Tail recursion can be turned into code which does not incur function-call overhead.
I've never been able to get the speed of OpenMCL-compiled code to compare the SBCL-compiled code. Every now and then I find myself switching back to SBCL for that reason, as I do some hacking in my free-time that is numerically intensive.
There are always going to be tradeoffs in runtime implementations, unfortunately.
I think the overall best way around this is to work at bundling small (in terms of on-stable-storage) standalone programs built with different implementations, and toss data back and forth among them so as to use the best runtime for any given portion of work.
There's nothing hugely new in making FFI calls out to library modules built in some other language, but most of those are exclusively a high-level language calling some low-level processing functions, with bindings in the high-level language acting as a bridge. SWIG is another example of this paradigm, like your example of CFFI+Fetter.
As an aside, I have recently been living in Chicken Scheme whose origins are in compilation to C following Henry Baker's paper (check out the hand-translated cboyer). The strength of this approach is that arbitrary mixing of Scheme and C is extremely easy.
An example from the mailing list looks like:
;; simple use of foreign-lambda*
(define yo
(foreign-lambda* void ((int x))
"printf(\"yo: %d\\n\", x);"))
The chicken compiler turns the whole example into a set of C functions, one of which evaluates each of the two top-level calls. That function (or the functions it in turncalls) can be invoked by anything that can call it, including a C "main()".
FFI evolution in high level languages is very cool.
Chicken has a structural difficulty with POSIX like threading models, however, because of the use of the C runtime stack as a nursery for young objects, so for now it's one of many Lisp-like languages that don't support native/kernel threading.
Like many modern Schemes it has a growing library of useful things one would do in, for example, Common Lisp. Sometimes, however, I find myself handing a bunch of data from one Lisp-like runtime to another Lisp-like runtime for processing in it, because the latter is faster or has a library or feature set that makes a particular sub-task easier to write.
Writing to shared memory, writing a bunch of data to a file, or a socket, or anything along those lines, to be read and processed and answered by a process made in a Common Lisp environment, is something I've actually done from time to time.
This is something that can be done and shipped now, rather than waiting on changes to a given Lisp-like implementation.
Some downsides are that the runtimes can be large and resource-hungry, you will never share resources as well as with threads inside a single process, and people wanting to build from your source code will have to deal with the multiple environments' idiosyncracies in building standalone binaries.
Some upsides are that interprocess synchronization does not have to be particularly computationally or bandwidth intensive (there are terse/fast serialization tools available), and you can distribute the runtimes across multiple processors, multiple systems, or both.
Personally, I think that with computational power increasing, this kind of "heavyweight threading" (pardon the abuse of the terminology) seems a lot less painful for the advantages it can bring.
Among other things explained in the document, you'll find the following:
It is trivial to compile and link entirely LP64 code.
More commonly, the "Mac Way" of using portable front ends and specialized back ends is done in Application Bundles. Typically the front end will be a GUI developed in Cocoa under Xcode, which exchanges messages (including invocation arguments) with one or more backend processes, which will be tuned to a given platform. The backends can equally be developed in Cocoa under Xcode and turned into fat Universal Binaries, or built in any number of other ways and stored in the Application Bundle directory hierarchy.
If you had said that the overwhelming majority of Mac OS X systems in existence today have G4 processors, I might buy that. Moreover, you could argue that it will take a while for the lower end 64-bit G5 iMacs to replace the lower-end 32-bit desktop machines. Furthermore, none of Apple's laptops are equipped with 64-bit processors.
However, there are still G3 desktop machines and laptops out there, and software is still being built which only optionally uses Altivec/VMX/Velocity Engine code which the G3s do not support. Many applications have no reason to use SIMD instructions. On the other hand some newer applications simply will not run on these systems, because they make use of the faster features of the newer chips.
Likewise, for the foreseeable future, there will be a few apps which need to run on 64-bit Macs, many more that won't, and a growing number which will use 64-bit features for performance or scalability reasons when running on a 64-bit Mac, but which will also run on a 32-bit-only Mac.
Extending this to support 32-bit and 64-bit Intel x86 as well is not an obviously unmanageable proposition for developers, who are in a position to continue the practice of hiding run-time selection of machine-dependent code (PPC vs x86, 64-bit vs 32-bit, SIMD vs no-SIMD) from Application users.
"Dream?" Fantasize is more like it. If "recompiling" was all it takes, there would be no differences between what is available under OS X from anything else. Recompiling of C or C++ code (so long as it doesn't need to interact with Quartz/Aqua) targeting PPC has been available since Day One for OS X.
There probably shouldn't be much difference in how Common Lisp looks from one implementation or platform to another. Or at least that's the portability argument. I'll deal with this first, then return to whether this looks "satisfactory".
Several Common Lisp implementations on POSIX systems are highly portable in terms of functionality; many more are not. Portabiilty is at the Lisp level, performance is in the implementation. So, it really is a question of "recompiling" your portable Lisp source. Your interaction with Quartz/Aqua/The Mac in General can be mediated by e.g. CLUI or by CLX+X11 or some combination, and look and behave pretty much exactly the same as on a host Windows or Linux or NetBSD system.
Moreover, modern CL compilers target several ISAs, and modern runtimes can cope with the performance tradeoffs among various architectures, although with varying performance and possible extension gaps from one host system to another. Consequently, non-standard libraries made available by a portable implementation can also be used cross-platform.
However, that said, there are useful non-standard libraries which aren't (yet) portably cross-platform. Many of these are experimental. One in particular is the OpenMCL Cocoa programming environment, which is obviously Mac OS X centric.
While it is one thing to run faceless software that can connect to the BSD guts of OS X, once it needs to talk to the user it will have to interact with the Cocoa (Objective C) GUI layer, or be retricted to running from the Terminal window or X11 or maybe use a Java presentation layer -- none of which are completely satisfactory (assuming there is a significant amount of user interaction).
The Mac OS X "way" is cooperating processes. A GUI front ends for scripted or compiled programs, exchanging data with it via XML, text or binary streams, or even shared memory/CoreData storage. Most apps that you'd find on VersionTracker for example, are exactly like this. Some of the apps are trivial GUIs for Apple-provided "command line tools" and the like. Many more are a mix of GUI and backend. Few are completely integrated within the same single program.
There is no particular reason why a bundle could not contain one or more fat binaries supporting x86 and PPC, some ruby or python scripts, and multiple GUIs -- one native Aqua/Quartz, one X11 (useful for remote X servers), one that runs an HTML server... The binaries could be standalone compiled Lisp code.
The code base that generates this would make delivery to a platform that is only X11 and x86 pretty easy... you just make new binaries (scripts and resources probably stay the same) and omit the Mac-specific stuff.
Windozers apparently need not apply.
There are CL implementations for Windows that would gladly compile the Lisp backends without significant change to the source. The work would be in building the Windows-like GUI frontend.
Personally, I also far prefer the high-end G5 to any x86-derived implementation. However, I don't write Lisp (or Scheme or Python or shell scripts) specifically targetting the G5 or PPC... Most people writing Objective-C, C++ or even C probably aren't really writing non-portable code. So, having two Mac OS X platforms is probably not going to be as big a bear as supporting code for Mac OS X and any other platform, in some cases even including Linux/PPC.
Aggressive cross-platform thinking is a good move; abstracting away the pro
What are you writiing in Common Lisp that is processor-dependent?
Okay, implementation-dependent things happen, so you might find yourself tied to Steel Bench for some reason... but if you can also target OpenMCL you'll find it has a kick-ass compiler as well as a fully preemptive thread scheduling model. ("[as of 0.14], lisp threads are native threads and all scheduling decisions involving them are made by the OS kernel. (Those decisions might involve scheduling multiple lisp threads simultaneously on multiple processors on SMP systems.)"
I think my wish is a little more likely, despite little things like the register model and other implementational "details" that are compiler-specific. There have been enough good x86-targetting Lisp compilers to borrow ideas from that I don't think the compiler itself is the critical path.
As I understand it, SBCL's PPC implementation's blocking issue on native threading is a combination of the heap model, the existing stop-and-copy garbage collector, and fundamental differences in the dynamic linking of Mach-O and ELF (and COFF and a.out) binary formats. In particular, the x86 format and FFI and the ISA's small supply of registers to allow for register-to-register tagging and detagging, have driven a conservative collector.
While the free CL developer community is small and gets along reasonably well, and ideas (and people) seem to leak back and forth among the various projects, I think OpenMCL has it a bit easier because of the familiarity with SBCL and its antecedents and their compilers to Gary Byers and company, as well as being able to do a port with knowledge of how to use modern x86 chips' register handling. Starting with a thread-safe accurate generational collector makes many aspects of a CL implementation much easier, and not targeting the most primitive 386 ISA will also help with performance.
Underlining my thinking here is that according to the SBCL wiki PPC-port threading is wating on the port of the conservative collector. This is probably the shortest path to threading, but when you could use register-to-register tagging, boxing, mask-and-match against most-common-values, and other goodies that having lots of registers support, it doesn't seem anything like the optimal path.
However, since we're talking about programming Lisp rather than implementing it, surely the important thing to do is to start writing maximally-portable Common Lisp, get everything to work, and then optimize sections for various implementations and platforms?
If the peculiarities of SBCL favoured its use for a performance-sensitive application, I'd use it under that app. (This could happen easily enough... lots of non-consing/non-recursive arithmetic on |big| integers, for example, would obviously favour implementations where |big| is fixnum over implementations that robbed bits from fixnums to provide an accurate rather than conservative GC). If another CL implementation ran it faster or better, and it mattered, I'd use that under the app.
GC systems in a similar steady state tend to roam all over their heap, touching lots of pages. This can cause the OS to keep more physical pages mapped to the process's virtual memory space, even though the process isn't actually using more memory.
Not necessarily. Modern GCs tend to take advantage of the generational hypothesis which suggests that most objects die young. One can code with generational GC with fast handling of deaths in the "nursery" of recently-created objects in mind.
A typical generational GC with two generations of old objects and a nursery works like this.
You maintain four pointers:
G1, the top of the oldest generation. G0, the top of the 2nd oldest generation. FREE, the top of the nursery. CEIL, which limits the size of the heap.
At initialization, the first two of these all point to the bottom of the heap. CEIL will point to a memory location half way between the bottom and the top of the heap, or higher.
Set FREE to CEIL, so we begin allocating in of the top of the heap.
When we allocate an object, we simply increment FREE.
When FREE reaches the top of the heap, we collect the nursery.
We do this by sliding down all live objects between FREE and the top of the heap to the area between G0 and CEIL.
In a tracing GC system, live objects are those pointed to by the roots (registers, for example, or live stack frames) as well as objects in the older generations. We can optimize the identification of the latter by maintaining a write barrier on the older generations, which tells us whether a section (perhaps a page) in the older generation was touched since the last garbage collection. A page which hasn't been touched since the last gc cannot point to live objects in the nursery. A page which has been touched might point to live objects in the nursery, so we have to trace the pointers in the page as if they were roots.
The slide-down relocation is usually done by copying the object and leaving behind a forwarding pointer. In the case of linked lists and the like, the lists are walked recursively, often breadth-first, although depth-first can give better locality of reference.
During the migration of the live objects, we can increment G0 as we go, reusing the allocation mechanism, or we can adjust G0 after the slide-down relocations are finished. Either way, when we are done, we have expanded the area between G1 and G0, and no longer care what's above CEIL. If there is sufficient space between G0 and CEIL, we set FREE to CEIL and operate as usual.
If we are tight on space between G0 and CEIL we can either expand the heap (and increase CEIL) or we can collect one or both of the older generations. Again, we identify live objects and slide them down to the earlier generation.
In the case where we are creating lots of short-lived objects we are doing very little copying during the nursery collection -- we can end up effectively doing nothing but writing objects into the top of the heap then resetting the FREE pointer when we get there. If there are many more pages between CEIL and the top of the heap then yes we will be touching more pages than if we had a very disciplined explicit allocate-and-free mechanism. If our nursery is small, however, that is not the case.
There are other ways of handling the nursery, including the Cheney on the MTA approach of using C's runtime stack (alloca() and friends) to store young objects. We still have to find and live objects when our nursery gets too large, but the C "return" reclaims all the dead ones. The implementation is straightforward for a continuation-passing-style language compiled into C, works well with small nursery sizes, and has been implemented in the Chicken implementation of Scheme.
Optimizing the speed of nursery handling, and partitioning the
You just send photons of one frequency, and then switch to the next. As fast as you can switch is how much bandwidth you can get.
Most optical signalling is done with blinking on and off rather than other modulations. This is mainly because collimated coherent light sources make this approach really easy on both the transmit and especially the receive side.
However, through a medium like glass fibre, wave propagation speed varies nonlinearly with frequency. In other words, two identical-length pulses on different frequencies transmitted at the same time may be detected at different times. This becomes important when stepping up the pulse rate, because signals sent earlier on one frequency may be detected later than those sent later on a different one. However, even within one frequency, the pulse intensity will spread out over time because real light sources are not truly monochromatic, and because the signals may be rotated in terms of polarization, so that a rapid off-on-off may wind up looking like it has more than one "on" pulse. Although these effects increase with distance, and the distances within a single system (or especially a chip-sized package) will be small, they also increase with signalling rate.
These limitations don't look particularly scary in comparison with electronics, especially since there are other media -- including vacuum -- through which one could send the optical energy. However, at the moment there is very little real work that can be done in a purely optical system, so there will be E->O conversions (which is what the article is about) and O->E ones. Converting high bandwidth optical signals into something useful for electronics is currently a harder problem, and in telecommunications is essentially sidestepped with multiplexing.
On the scale of metres to megametres, optical pathways are clear win, mainly for the reasons you listed.
However, the reason that EO-OE links interconnecting chips via fibre aren't more popular is that on the scale of a few centimetres to a few millimetres it's an expensive engineering choice even in comparison to the difficulty in shrinking the distances between chips (to the point of putting multiple chips in a single package) or increasing the multiplex fan-out across more pins/paths/traces while maintaining a relatively slow clocking rate.
The EU does not have a common criminal justice court system, nor a common tort law or civil court system.
What you have described is common in civil code (Code Napoleon) member states. While these states are in the majority, there are other systems running in the European Union. There are also non-uniformities in the broad groupings of legal systems.
For instance, in the four common law member states (CY, IE, MT, UK), violations of rules of evidence taint the evidence and render a conviction based on the evidence unsafe. Moreover, there are some differences between English and Scottish law on these matters, even though England and Scotland are both part of a single EU member state, and both Cyprus and Malta have adopted some civil code rules into their systems, and Ireland has introduced a number of its own rules and practices as well.
Newer EU members have been spending years transitioning to the civil code courts system, mostly because it is easier for them to adopt EU rules into a civil code framework, however there are also obligations to the Organization for Security and Cooperation in Europe. OSCE is NOT an EU body -- it includes some 51 members. However EU members are expected to be OSCE members, and OSCE and the EU influence one another heavily with respect to elections and courts monitoring, reporting on judicial fairness, and so on.
That said, there are still considerable amounts of legacy legal procedure and theory in the former Communist countries which recently became full members of the European Union, and the rules of evidence can vary substantially from common practice in the civil code countries closer to the west coast.
There are a number of treaty bodies to which EU citizens may appeal a decision by a member state's supreme court. The important one is the European Court of Human Rights (in Strasbourg). The ECHR is NOT a European Union institution -- it is an international treaty organization to which EU members must belong, although non-EU countries in the region are also members, and there are some out-of-region partial-members and observers as well.
There is also the European Court of Justice (in Luxembourg), which IS an EU institution. Individuals cannot appeal to the ECJ directly, but they can ask their national courts to make a reference to them. Generally speaking this is done when there is unclarity about national legislation or rules with respect to an EU directive. The ECJ is not allowed to consider the facts of any given case, and it is expected to be very careful about conflicts between the acquis communitaire (the body of EU treaty law, directives, previous EU court decisions, and so on) and the national legal systems.
In general, the justice system in the EU is complicated by three main threads: different origins of the national legal systems, the principle of subsidiarity, and nation states having agreed to different sets of treaties. The last of these has been decreasing in significance as the EU member states and the Commission have been working very hard on that issue in particular. Subsidiarity is structural and effectively constitutional, and works against the development of a fully common system across the EU, and has led to the current approach of "translating" EU-wide rules into the local systems in a natural way, at the local level. Finally, the modern EU is only as old as the Treaty of Maastricht, which is from early 1992. There are literally thousands of years of legal tradition in many of the nation states with the occasional partial convergences brought on by conquest (the Roman system, and the Napoleonic system based on that being two of the main ones), "contagious revolution" (1848, the rise and fall of Communism in Central Europe, and so on), or multilateral negotiation (usually for economic reasons).
(Paraphrasing Dvorak) "There are 50 writers in the industry who are in bed with Apple!". Gosh, better run off and tell the House UnMicrosoft Activities Committee...
Senator Iselin: I mean, the way you keep changing the figures on me all the time. It makes me look like some kind of a nut, like an idiot.
The Red Queen: Well, you're going to look like an even bigger idiot if you don't get in there and do exactly what you're told...Who are they writing about all over this country and what are they saying? Are they saying: 'Are there any Communists in the Defense Department?' No, of course not, they're saying: 'How many Communists are there in the Defense Department?' So just stop talking like an expert all of a sudden and get out there and say what you're supposed to say.
Ah, yes, and while we're at it, let's get rid of the FDA, which reduces the supply of food and medicines ostensibly to control "quality", but really just increases the cost of food and drugs.
That people may end up becoming gravely ill -- or dead -- by consuming contaminated food or medicines is a good thing since it will weed out inefficient consumers. (The market will readily correct for the type of seriously ill person who is poor at making rational, informed choices.)
In the resulting free market, brands increase in value, and even in the presence of cut throat competition a strong brand would never risk the damage to itself by cutting costs in rigorous monitoring the safety-critical areas of their food or drug production chain.
So you're right... bad doctors aren't a problem to be prevented in advance by state-enforced controls on the market. Proactive regulation is a non-solution. A reactive tort-based system will encourage players in the free market to be even more careful about avoiding costly, harmful, and deadly mistakes.
Affiliating light with quantum theory seems like a stretch as quantum theory answers seem deus ex machina to me.
You probably don't mean deus ex machina, really, when you make the point that quantum theory seems forced. You're right -- the particles in quantum theory are not actually particles, they're quantizations that seek to capture the real-world effects of fluctuating energy fields by concentrating on the planck space with the maximum field energy at a given planck time. The only reason to do this is because the maths are easier and come up with useful and correct predictions, even though the quantization is not perfect.
This is analogous to the sampling theorem in telecommunications, where for example the useful information about a fluctuating field (pressure waves passing through air) can be quantized (digitized) at a given location (a microphone) without significant information loss if a particular sampling rate is used.
In both cases the real behaviours are complicated and involve wave-like propagations of energy-level changes, and in both cases there is substructure (i.e., there are smaller and smaller fields involved), but at most scales a 1d single-wave-like analysis or a 0d single-particle-like analysis can make sufficiently accurate predictions that performing a 2, 3 or more dimensional field type analysis is a lot of work for no gain in predictive power.
If we can now comb out light frequencies to within 15 digits of accuracy, it seems like we can increase bandwidth over laser optics by many orders of magnitude. The long term gain in communications bandwidth could be huge if the technique is feasible cheaply by industry.
The problem is not (and for a while hasn't been) the overall bandwidth of the optical window through optical fibre (or even better media), but rather the channel bandwidth. A channel is simply a frequency which carries information, and you can have multiple channels travelling through media like optical fibre.
Each channel effectively transitions between bright and dim (blinks on and off, if you like), and the speed and accuracy of differentiating between bright and dim at the detect side is tricky. An analogy would be to the human eye, which has flicker sensitivity limits... blink a light on and off at about 30 or so blinks per second and the light apepars continuous.
Moreover, the blink and detect work is done by electrical->optical and optical->electrical conversions, and the data is almost always in the digital domain, and there are practical limits to the speed of A->D/D->A conversion and digital processing.
Increasing coherence -- that is, specializing (with better lasers and better filters) on a tighter and tighter range of frequencies -- is what lets one have more blinking channels per fibre. This is called WDM, wave-division multiplexing.
There are limits to the density of WDM because even if you emit perfectly coherent light on the transmit side, on the detect side, you get a gaussian distribution of frequencies. Photons will collide with particles of matter in the fibre, changing their energy levels and directions. This spreads a light pulse out in frequency and in time. Density (and also channel bandwidth) are limited by the narrowness of the gaussian spectrum you can look at, and by how well you can distinguish a brightness transition from noise introduced by the tails of nearby (frequency-wise) channels and delayed-by-bouncing photons (right colour, wrong time, leading to possible false "bright" or false "dim" results).
There is some old fibre out there where density is a bigger practical problem because there is more oxygen and other matter -- impurities, really -- which interact more profoundly with the infrared-frequency photons), but in practice there is little constraint on the number of channels available, and a maximum (and unfortun
Pretty much what every member of the EU has done has ceded a chunk of sovereignty to a government that they at best have inderect control over.
No. Every member of the EU has pooled a chunk of its sovereignty together with the other member states, each of whom participates in the joint executive known as the Council of Ministers, which is the SOLE ORIGINATOR of EU-wide Directives. Who sits on the Council? All the heads of government.
Not one of the Prime Minister of the United Kingdom, the President of France, the Chancellor of Germany, among others, has sole control over the Council of Ministers, but each of those heads of government of the larger states can personally easily derail any initiative on a whim, and can bet that each could convince the majority, or all (as necessary) of his or her counterparts to support some joint plan that seems to make sense, although that's harder. Some combination of smaller-state heads of government can do likewise, and often do. BE-NL-LU often wind up as a bloc with the weight of one of of the larger countries, as do DK-SE-NO-FI and more recently with PL joining with one of LV-ES-LT and CZ-SK-HU.
This sort of joint pooling of sovereignty for common goals is what the EU is about. The problem is that at the Council level, the opposition is filtered out, because it does not participate. (This is worsened by a lack of transparency... many people do not know which head of government had what opinion on a given proposed Directive).
Offsetting the Council is the Parliament, which is directly elected. It cannot originate directives, but it can require changes, or delay or defeat them. The Parliament does include members of the opposition (and governing) parties of the various member states. It mostly operates in committes which pick their way through directives suggesting changes back and forth, and sometimes outright opposes a proposed directive by the Council. It has developed teeth, and would have had sharper ones under the recently deceased proposed constitution.
In the middle is the Commission. It's officially run by Commissioners (one or two from each Member State, traditionally) appointed by the Council with the consent of the Parliament (somewhat like how the U.S. Senate confirms Presidential Cabinet members, which would have been even more like that under the ex-proposed-constitution), but is really a civil service which has things delegated to it by the Council (i.e., the Member States). It aligns variously with the Council, whom it briefs and does studies for or some subset of the Member States (it often sees itself as a sort of guardian of the interests of the smaller member states, especially in cajoling the larger Member States to "play fair" and abide by things they've agreed to be obligated by, or have held smaller members to).
There is little more love between the Parliament and the Commission than the Parliament and the Council, mostly because under the current arrangements, the Parliament is fundamentally a criticising body.
There are analogous relationships between various national governments and their legislatures, especially where there is an upper house beholden to different masters than the head of government, or a lower house which they do not totally dominate.
The EU looks more and more like a normal confederation with every passing year, mostly because it seems to balance interests well enough that despite complaints about it (many of which are shockingly hypocritical) nobody can think of a better evolutionary direction than that taken by e.g. the various U.S. States, Canadian Provinces, Australian States, and so forth.
(As an aside, it's interesting that Belgium, Europe's most federalized state, tends to be overwhelmingly pro-EU-as-federation; Switzerland, Europe's 2nd most federalized state, has been quietly agreeing to a number of treaties with the EU that would have been consolidated into the proposed constitution, but generally is pretty
Mexicans consume around 3500 calories per day. We consume roughly 4000 per day. That's not a big enough difference to account for the obesity rates.
Even blindly accepting the accuracy of these numbers, at ca. 4500 kcal/lb of fat, given two people who burn the same number of calories in a day, person A consuming 500kcal/day more than person B, will put on one pound of fat more than person B every nine days.
Even if we adjust for the observation that heavier people burn more calories than lighter people, performing the same level of activity (or inactivity), person B will still put on about 2 - 3 lb more weight a month than person A.
That's more than 20lbs a year.
In that case, the source of the relative obesity is obvious, and your assertion that "[t]hat's not a big enough difference" is wrong.
I will even try to help you out a bit... let's say that Mexicans don't actually burn as many calories as Americans, because a bunch of Americans have this season called winter, where they have to burn more calories to keep warm. Let's use a nice deflator of 50% of your 500 calorie/day difference, i.e., 250 calories/day is spent dealing with the temperature difference between Mexico-average and USA-average.
We now have a weight-gain difference of 10 lb/year instead of 20. That *still* would account for a large difference in obesity rates over time.
Now, let's turn on your numbers. The "standard human" is 72kg (~160lb). That's men and women averaged together. A standard human will burn about 2300 kcal/day *in a completely sedentary state* (like in a coma).
Light exercise (walking to the toilet, getting out of bed) will burn no more than 1 kcal/minute more than being completely still. An office worker who doesn't walk around all that much will maybe burn 200-300 kcal/day this way. People who actually *exercise* may burn more... moderate exercise will burn 2 kcal/min extra and heavy exercise will burn about 3 kcal/min extra. An hour-long workout and some cool down and you can make it 400-500 kcal/day beyond base rate.
Smaller humans burn less when being comatose (they have less tissue to supply blood and nutrition to) and when they are being active (they have less mass, so have less inertia to overcome; they also have less surface area, less volume, and smaller cross-sections, which are important when moving quickly, or in water). Larger humans burn more. Moreover, muscle tissues consume more energy than fatty tissues even when they're doing nothing, so a person who is heavy because of musculature rather than fat, will burn more than any lighter person, or a somewhat heavier person who is not as muscular, even being completely still.
Taking all this into account, and cross-checking with actual observations using calorimeters, a standard human in the USA burns about 2500 kcal in a day. An average woman may burn about 2000; an average man may burn about 3000. Any more than that is very efficiently stored as fat. This is thanks to an abundance of calories being a feature of human existance only over the last 100 years -- millions of years of mammalian evolution has taken place under pressure from frequent food shortages.
To make it a bit easier, you come up with a similar number for your vegetative calorie burn rate if you multiply your weight in pounds by 12 (or your mass in kg by 26, for non-Americans). With a desk job and a commute that involves a few flights of stairs, a few minutes from a subway or bus to office (and home), a few minutes walking to and from lunch and the toilet, and so on, with occasional light housework you can multiply that number by *at most* 1.5.
At 180lb, then, it would be (180*12)*1.5 -> 3240. Round down.
If an average American (even just an average big male american) consumes *4000* calories a day when population studies observe only about 3000 a day *or less*, would mean about a pound of weight gain every five days.
Dude, *water* is a chemical. *You* are made up of chemicals.
Corn syrup generally is made by breaking down corn startch, exactly the same way the amylase enzyme in your own saliva would break it down. The decomposition produces glucose in both cases. That's what your cells like to use to make ATP, which is the supplier of energy for most in-cell chemical reactions.
HFCS is high in fructose because a second enzyme, glucose isomerase, converts some of the glucose to fructose. Fructose is a common sugar found in *all* sweet fruits, nectar, and bee honey. It has three useful properties: firstly, it is much sweeter than glucose (so you need less of it for the same sweetness); secondly, it is more readily soluble than glucose at low temperatures, which is useful in e.g. cold drinks; thirdly, it is not as instantly usable by your body as glucose is -- you convert fructose you naturally eat in large amounts (especially in a vegan diet rich in vegetables and fruits!) into glucose, but it takes some energy to do so. In other words, you add less fructose than glucose, and that fructose gives you less net dietary calories. Moreover, the reaction is spread out over time, rather than happening all at once. This moderates the insulin reaction.
Result: *less* calories per drink than if you used glucose syrup (i.e., plain corn syrup). You get less fat drinking HFCS soft drinks. Less glucose rush per drink.
Now, consider drinks sweetened with sucrose -- from cane or beets.
Sucrose is a pair of sugar molecules linked together. The molecules are glucose and fructose. When you eat ordinary sugars directly, or in the tissues of *all* plants, your body first breaks that bond, so you have a fructose molecule to deal with *anyway*, just as if you had consumed it in a tin of HF(50%F)CS-sweetened cola.
Guess what... maple syrup is mostly water and sucrose!
The weight gain you're talking about is a simple energy problem. When calories into your body exceed calories out, you gain weight. Your body stores excess calories very efficiently in fat. When calories out exceed calories in, you lose weight.
Calories in = food energy. Of all sorts. Doesn't matter if it's from sugars (~17 kilojoule/gram), proteins (also ~17 kJ/gram) or fat (~38 kJ/g). The problem with sweet drinks is that it's lots of calories that don't make you feel as full as meats, or ordinary fruits and vegetables.
Substituting low or no calorie sweeteners cuts back on a lot of these unfilling calories, and they are often a considerable fraction of what pushes people over the point where weight gain occurs.
Substituting honey for sucrose, or maple syrup for corn syrup, really makes very little difference. You still are taking in lots of carbohydrates at ~4kcal/gram.
The *plausible* (and even some of the implausible) risks of heavy consumption of artificial sweeteners is *by far* outweighed by the healthy effects of reducing food energy in overweight people.
Unfortunately, even eliminating 140+ kilocalories per can of fizzy drink (times several cans a day, perhaps), most people still eat more than the 2000-2500 kilocalories worth of food energy than they burn off in a day, and so *still* gain weight. Scaring them away from ways of reducing calories by substituting low-calorie reasonably-as-tasty stuff is *harmful* if it ends up adding 300 kilocalories/day (or more!) excess intake, or putting on about a kg (or about two pounds) of fat a month.
would be nice for hardware to provide tag bits so integers and floats wouldn't have to be twiddled before doing arithmatic on them
Tag bits are nice so you can precisely distinguish pointers from everything else.
This lets you implement a precise garbage collector rather than a conservative one. This makes modern tracing GC (fast, compacting (especially to increase locality of reference), incremental, even backgroundable) much easier to implement.
If you sacrifice one bit from your native machine's word size, you can distinguish pointers from all other objects.
Most modern systems use byte addressing rather than word addressing, and if you word-align all your data, you don't need your least significant bits. So on a 64-bits-per-word machine, you can sacrifice your LSB and still have 63 bits to play with; on a 32-bits-per-word machine, you have 31, hell, on a 16-bits-per-word machine you have 15. Don't ask about truly 8-bit machines like the 6502.:-)
Word-alignment on a byte-addressed 16-bit machine gives you 15 bit pointers -- you only have to address every second byte, starting at all zeros. Address 1 is part of the very first word, so 0000000000000001 (binary) is something we don't need to address directly. We can use that 1 bit to indicate that the object is NOT a pointer.
We can do the same for 32 and 64 bits:...00 (binary) is the address of a 32-bit word on a byte-addressed architecture;...0000 (binary) is the address of a 64-bit word, and so forth.
All you need is a shift-all-the-bits-in-the-word-left instruction.
Instead of: load register,memory; , store memory,register, we:
1. Load register
2. Shift word to the right. bit0 goes away, most-significant-bit is now 0.
3. Perform arithmetic operations as usual, leaving result in register.
4. Shift word one bit to the left, immediate-or the word with 1.
5. Store the word into memory.
This works great for 2s complement integer arithmetic, with an absolute range of values of 2^wordsize-1.
As noted, on 32-bit architectures and 64-bit architectures, we have more bits that are meaningless in pointers to word-aligned objects. We just do more shifts.
Typechecking involves a step between 1 and 2, where we look at whether the least significant bit is really 1 before doing integer arithmetic.
If we have more tag bits, we can have more typechecking along these bit-testing lines.
The cost of the register shifting can be small or large depeding on architecture. Usually it's small on modern chipsets.
It's not clear that hardware tagging would be a clear win. Mostly it would have to be evaluated in terms of decreasing the number of instructions by consolidating dropping back to e.g. three to do an untypechecked integer load-add-store from six (load-shift-add-shift-or-store). That is, the extra three instructions are unlikely to add so much extra dependency or processing time, or rob users of sufficient fixnum precision, that pushing distinguished and precise identification of pointers into the processor becomes worthwhile.
Moreover, with foreign-function-interfacing, one can jump into another language (like C) to do full native untypechecked integer (or other) maths, just so long as it doesn't put results that look like pointers into GC-managed memory.
Chicken is one example of this approach. It uses Cheney-on-the-MTA to do the nursery area of its fast, compacting, generational garbage collector *portably* to byte-addressed 32-or-64-bit word architectures. Since all objects are word-aligned, that gives two or four bits of "tag", which it uses to precisely identify pointers, integers, and a few other types, both for typing and for GC purposes.
By comparison, the CADR's hardware architecture ultimately supports polymorphism
At the time, the obviousness of being able to target an industry-standard, pervasive platform, just wasn't obvious. There simply wasn't such a platform with many megabytes of memory, many gigabytes of disk, processors which could handle hundreds of millions of operations a second, and so forth.
There were interpreted Lisps on a variety of platforms that were in common use. There were a handful of compiled Lisps. Most notably, MacLisp in ITS (and later on other OSes on PDP-10s), which was so popular at MIT's AI lab that enough was developed in it that performance became a key issue.
At the time it made sense to investigate hand-building a machine that would be an optimal target for a compiled Lisp, especially in that it would make known optimization techniques easier, and would welcome rather than tolerate large garbage-collected heaps.
Nowadays, Scheme textbooks like the excellent SICP: Structure and Interpretation of Computer Programs (or, say, The Schematics of Computation) introduce compiler-writing and target an implementation of a virtual machine. These VMs are directly analogous to the actual CADR hardware. On modern fast computers, they run faster than the actual processor hardware in the physical CADR. Running a VM with performances down in the 1/double-digit or 1/triple-digit of physical processors would have been intolerable back then.
There were always people working on targetting mass-produced general-purpose hardware, it's just that there was a lot of learning to be done about how to optimize things.
Lots of that work was done in the context of the amazing T project. The T compiler was a massive watershed development for Lisp compilation, as it implemented or introduced such things as efficient lexical scoping; simplifying the code through transformation until you were left with operations on only as many variables as you have registers; lambda-lifting; CPS-transformation; dramatically improved copying garbage collection; "pseudo-hardware typing". A number of other innovations directly stem from these.
Nowadays, with these techniques, and others learned since, compiling Lisp to practically any modern processor is straightforward. Modern Lisp and Scheme compilers generate either efficient and fast native code, or portable C (to the extent that it should work on any 32 or 64 bit platform gcc supports).
There remain some implementations that target a tight purpose-written VM, like CLISP or Scheme48 does. There are some implementation that target other VMs, particularly the Java one, compiling into JVM bytecodes or Java as an intermediate language.
The cheapness and reasonable speed of VMs, the efficiencies of compilers targeting C, assembly, or native machine code, and the tightness of runtime environments supporting type-handling and garbage collection, are such that practically nobody would seriously think about building hardware dedicated to supporting Lisp like languages.
With the benefit of hindsight, you are completely right.
The two points you raise, prior to T, would have been thought of as somewhere between funny and insane.
T was ca. 1982.
The CADR Lisp Machine was started ca. 1975 and documented in AI memo 528 in 1979.
Lisplike-compiled-language-favourable hardware chugged along until the early 1990s, when they collectively could no longer compete with the performance (never mind the price) of implementations compiling for x86, SPARC, Alpha, and a few other popular workstation instruction sets.
Wow... 12 years. Lots has changed since in far fewer years than that!
Maybe you should check out PLT Scheme, a modern scheme interpreter and compiler and IDE, with several "Teachpacks" which help one come up to speed in doing clever things with Scheme. DrScheme is the thing to download, for various platforms.
There are plenty of other Scheme implementations out there, interpreted and compiled.
Personally I do a lot in SCSH, the Scheme Shell which is handy for writing scripts and tools close to the metal on on UNIX/POSIX systems, however PLT and other environments are incorporating more and more of SCSH-like goodies into their libraries, and write stuff that needs to be fast in Chicken or Gambit, which compile Scheme to C. On the Common Lisp front, on a Mac, there is also OpenMCL, which compiles to *particularly fast* native PowerPC code and has a straightforward way of communicating with Cocoa and a number of other Mac-development-friendly features. In fact, all of these implementations have foreign-function-interface abilities which let you call e.g. C functions from Scheme/Lisp, or vice-versa, so you can write performance-critical sections in a low-level language of your choice, and use higher-level languages to develop "smarts".
Surely you were using a Symbolics machine, and not an MIT CADR?
This is not Genera.
One can see in this software and the CADR the start of what evolved into the Symbolics 3600 series and its software.
Since you mention a price tag (in non-US currency even), and imply that it was used for commercial purposes, you are almost certainly not thinking of the CADR prototype and research system.
However, to go a bit easy on you, melanogenesis leaves clear markers: the thymidine dinucleotides (four amino bases, two of which are T) from UV destruction of damaged DNA, alpha-melanin-stimulating-hormone which is produced in response to this, the binding products (other than melanin) of alpha-MSH to melanocytes, and the transport of melanin vesicles to the keratinocytes, and their consequent migration from lower layers of skin to higher layers. The last is likely to be the largest archaeological giveaway, other than studying your nuclear DNA. Pinker (with less melanin in melanocytes) flesh to darker flesh transitions in layers are what differ a suntanned pale person from a naturally dark person.
Finally, the presense of melanin at all (or tyrosinase, for that matter) will distinguish a pale, unsuntanned person from an albino.
A future in which nuclear DNA, even in scattered fragments, can be analysed for this sort of thing, is also a possibility.
2) To make things even easier on you, there is no guarantee that your remains will fossilize. Fossils, even in amber-rich areas, are very rare compared to the number of things dying in the areas fossils are found.
Most resin (amber) fossils *of animals* are generally only discoloured cavities, in which the organic structure is gone, and all that's left are chemical traces of chitin (in the case of insects and spiders), sometimes hair or feathers, or bits of bone.
While the drying effect of really sticky maple syrup (osmotic dessication... water moves out of exposed cells, where it's in greater concentration, and into the syrup, where the water:sugar ratio is lower) kills off surface bacteria and the like, your insides are just teeming with bacteria which will gladly do their best to digest you from the inside out. Moreover, neither your epidermis nor the maple syrup is chemically inert and they aren't likely to be in a stable configuration.
Your calcified bits (bones and maybe teeth) may well survive, but unfortunately that leaves future archaeologists with no direct evidence of what colour your flesh really was, since it will have long since changed into a mass of stained sugary crystal.
I, for one, welcome our new resin cast fossil underlords.
Ack, pfft, says the evil Schemer. This is just insipid syntactic sugar for what you really mean:instead of whatever dark magic your buggy ends up being mangled into by your CommonLisp compiler because it can't do a safe-for-space tail recursion.
You have this backwards. Optimizing compilers will turn tail-recursive style source into "normal" loops.
You can write a loop recursively, so that:becomesRecursion in foo-helper is in the tail position. That is, foo-helper only calls itself as the final operation before returning.
Compiling this naively involves a function call per recursion, which on most architectures results in pushing data onto the stack. However, because we are doing tail-recursion, we can do a tail call elimination optimization.
How this works is that the "return" before the recursion is taken to mean that any automatic variables are dead, any stack space used for the arguments is reusable, and the recursive call is really a jump.
That is, when foo-helper calls itself, it really does an argument rewrite and jump, which in effect "pretends" that foo-helper was called with different arguments in the first place.
In other words, tail call elimination turns recursive loops into iterative loops.
Writing in "tail-recursive style" just means making sure your recursion is done in tail position (i.e., attached to a "return"). Some compilers for a variety of languages can identify recursion which is not done in the tail position, and reorder the recursion into tail position (and then the tail calls are eliminated into iterative loops). However, many compilers can't, and many more don't do tail-call elimination at all.
Once you've optimized recursive loops into iterative ones, you can optimize iterative loops however you like, including partially or fully unrolling them.
In summary, recursion is a way of looping, but function calls are not free. In particular, they usually consume stack space. If you only return the result of your recursion, then you are tail-recursing. Tail recursion can be turned into code which does not incur function-call overhead.
There are always going to be tradeoffs in runtime implementations, unfortunately.
I think the overall best way around this is to work at bundling small (in terms of on-stable-storage) standalone programs built with different implementations, and toss data back and forth among them so as to use the best runtime for any given portion of work.
There's nothing hugely new in making FFI calls out to library modules built in some other language, but most of those are exclusively a high-level language calling some low-level processing functions, with bindings in the high-level language acting as a bridge. SWIG is another example of this paradigm, like your example of CFFI+Fetter.
As an aside, I have recently been living in Chicken Scheme whose origins are in compilation to C following Henry Baker's paper (check out the hand-translated cboyer). The strength of this approach is that arbitrary mixing of Scheme and C is extremely easy.
An example from the mailing list looks like:The chicken compiler turns the whole example into a set of C functions, one of which evaluates each of the two top-level calls. That function (or the functions it in turncalls) can be invoked by anything that can call it, including a C "main()".
FFI evolution in high level languages is very cool.
Chicken has a structural difficulty with POSIX like threading models, however, because of the use of the C runtime stack as a nursery for young objects, so for now it's one of many Lisp-like languages that don't support native/kernel threading.
Like many modern Schemes it has a growing library of useful things one would do in, for example, Common Lisp. Sometimes, however, I find myself handing a bunch of data from one Lisp-like runtime to another Lisp-like runtime for processing in it, because the latter is faster or has a library or feature set that makes a particular sub-task easier to write.
Writing to shared memory, writing a bunch of data to a file, or a socket, or anything along those lines, to be read and processed and answered by a process made in a Common Lisp environment, is something I've actually done from time to time.
This is something that can be done and shipped now, rather than waiting on changes to a given Lisp-like implementation.
Some downsides are that the runtimes can be large and resource-hungry, you will never share resources as well as with threads inside a single process, and people wanting to build from your source code will have to deal with the multiple environments' idiosyncracies in building standalone binaries.
Some upsides are that interprocess synchronization does not have to be particularly computationally or bandwidth intensive (there are terse/fast serialization tools available), and you can distribute the runtimes across multiple processors, multiple systems, or both.
Personally, I think that with computational power increasing, this kind of "heavyweight threading" (pardon the abuse of the terminology) seems a lot less painful for the advantages it can bring.
No.
See: Apple's Developing 64-bit Applications page.
Among other things explained in the document, you'll find the following:
It is trivial to compile and link entirely LP64 code.
More commonly, the "Mac Way" of using portable front ends and specialized back ends is done in Application Bundles. Typically the front end will be a GUI developed in Cocoa under Xcode, which exchanges messages (including invocation arguments) with one or more backend processes, which will be tuned to a given platform. The backends can equally be developed in Cocoa under Xcode and turned into fat Universal Binaries, or built in any number of other ways and stored in the Application Bundle directory hierarchy.
If you had said that the overwhelming majority of Mac OS X systems in existence today have G4 processors, I might buy that. Moreover, you could argue that it will take a while for the lower end 64-bit G5 iMacs to replace the lower-end 32-bit desktop machines. Furthermore, none of Apple's laptops are equipped with 64-bit processors.
However, there are still G3 desktop machines and laptops out there, and software is still being built which only optionally uses Altivec/VMX/Velocity Engine code which the G3s do not support. Many applications have no reason to use SIMD instructions. On the other hand some newer applications simply will not run on these systems, because they make use of the faster features of the newer chips.
Likewise, for the foreseeable future, there will be a few apps which need to run on 64-bit Macs, many more that won't, and a growing number which will use 64-bit features for performance or scalability reasons when running on a 64-bit Mac, but which will also run on a 32-bit-only Mac.
Extending this to support 32-bit and 64-bit Intel x86 as well is not an obviously unmanageable proposition for developers, who are in a position to continue the practice of hiding run-time selection of machine-dependent code (PPC vs x86, 64-bit vs 32-bit, SIMD vs no-SIMD) from Application users.
There probably shouldn't be much difference in how Common Lisp looks from one implementation or platform to another. Or at least that's the portability argument. I'll deal with this first, then return to whether this looks "satisfactory".
Several Common Lisp implementations on POSIX systems are highly portable in terms of functionality; many more are not. Portabiilty is at the Lisp level, performance is in the implementation. So, it really is a question of "recompiling" your portable Lisp source. Your interaction with Quartz/Aqua/The Mac in General can be mediated by e.g. CLUI or by CLX+X11 or some combination, and look and behave pretty much exactly the same as on a host Windows or Linux or NetBSD system.
Moreover, modern CL compilers target several ISAs, and modern runtimes can cope with the performance tradeoffs among various architectures, although with varying performance and possible extension gaps from one host system to another. Consequently, non-standard libraries made available by a portable implementation can also be used cross-platform.
However, that said, there are useful non-standard libraries which aren't (yet) portably cross-platform. Many of these are experimental. One in particular is the OpenMCL Cocoa programming environment, which is obviously Mac OS X centric.
The Mac OS X "way" is cooperating processes. A GUI front ends for scripted or compiled programs, exchanging data with it via XML, text or binary streams, or even shared memory/CoreData storage. Most apps that you'd find on VersionTracker for example, are exactly like this. Some of the apps are trivial GUIs for Apple-provided "command line tools" and the like. Many more are a mix of GUI and backend. Few are completely integrated within the same single program.
There is no particular reason why a bundle could not contain one or more fat binaries supporting x86 and PPC, some ruby or python scripts, and multiple GUIs -- one native Aqua/Quartz, one X11 (useful for remote X servers), one that runs an HTML server... The binaries could be standalone compiled Lisp code.
The code base that generates this would make delivery to a platform that is only X11 and x86 pretty easy... you just make new binaries (scripts and resources probably stay the same) and omit the Mac-specific stuff.
There are CL implementations for Windows that would gladly compile the Lisp backends without significant change to the source. The work would be in building the Windows-like GUI frontend.
Personally, I also far prefer the high-end G5 to any x86-derived implementation. However, I don't write Lisp (or Scheme or Python or shell scripts) specifically targetting the G5 or PPC... Most people writing Objective-C, C++ or even C probably aren't really writing non-portable code. So, having two Mac OS X platforms is probably not going to be as big a bear as supporting code for Mac OS X and any other platform, in some cases even including Linux/PPC.
Aggressive cross-platform thinking is a good move; abstracting away the pro
What are you writiing in Common Lisp that is processor-dependent?
Okay, implementation-dependent things happen, so you might find yourself tied to Steel Bench for some reason... but if you can also target OpenMCL you'll find it has a kick-ass compiler as well as a fully preemptive thread scheduling model. ("[as of 0.14], lisp threads are native threads and all scheduling decisions involving them are made by the OS kernel. (Those decisions might involve scheduling multiple lisp threads simultaneously on multiple processors on SMP systems.)"
In fact, I'd almost reverse your wish and dream for cross-platform OpenMCL Cocoa Programming support.
But then again, I'm one of those evil Schemers...
I think my wish is a little more likely, despite little things like the register model and other implementational "details" that are compiler-specific. There have been enough good x86-targetting Lisp compilers to borrow ideas from that I don't think the compiler itself is the critical path.
As I understand it, SBCL's PPC implementation's blocking issue on native threading is a combination of the heap model, the existing stop-and-copy garbage collector, and fundamental differences in the dynamic linking of Mach-O and ELF (and COFF and a.out) binary formats. In particular, the x86 format and FFI and the ISA's small supply of registers to allow for register-to-register tagging and detagging, have driven a conservative collector.
While the free CL developer community is small and gets along reasonably well, and ideas (and people) seem to leak back and forth among the various projects, I think OpenMCL has it a bit easier because of the familiarity with SBCL and its antecedents and their compilers to Gary Byers and company, as well as being able to do a port with knowledge of how to use modern x86 chips' register handling. Starting with a thread-safe accurate generational collector makes many aspects of a CL implementation much easier, and not targeting the most primitive 386 ISA will also help with performance.
Underlining my thinking here is that according to the SBCL wiki PPC-port threading is wating on the port of the conservative collector. This is probably the shortest path to threading, but when you could use register-to-register tagging, boxing, mask-and-match against most-common-values, and other goodies that having lots of registers support, it doesn't seem anything like the optimal path.
However, since we're talking about programming Lisp rather than implementing it, surely the important thing to do is to start writing maximally-portable Common Lisp, get everything to work, and then optimize sections for various implementations and platforms?
If the peculiarities of SBCL favoured its use for a performance-sensitive application, I'd use it under that app. (This could happen easily enough... lots of non-consing/non-recursive arithmetic on |big| integers, for example, would obviously favour implementations where |big| is fixnum over implementations that robbed bits from fixnums to provide an accurate rather than conservative GC). If another CL implementation ran it faster or better, and it mattered, I'd use that under the app.
Not necessarily. Modern GCs tend to take advantage of the generational hypothesis which suggests that most objects die young. One can code with generational GC with fast handling of deaths in the "nursery" of recently-created objects in mind.
A typical generational GC with two generations of old objects and a nursery works like this.
You maintain four pointers:
G1, the top of the oldest generation.
G0, the top of the 2nd oldest generation.
FREE, the top of the nursery.
CEIL, which limits the size of the heap.
At initialization, the first two of these all point to the bottom of the heap.
CEIL will point to a memory location half way between the bottom and the top of the heap, or higher.
Set FREE to CEIL, so we begin allocating in of the top of the heap.
When we allocate an object, we simply increment FREE.
When FREE reaches the top of the heap, we collect the nursery.
We do this by sliding down all live objects between FREE and the top of the heap to the area between G0 and CEIL.
In a tracing GC system, live objects are those pointed to by the roots (registers, for example, or live stack frames) as well as objects in the older generations. We can optimize the identification of the latter by maintaining a write barrier on the older generations, which tells us whether a section (perhaps a page) in the older generation was touched since the last garbage collection. A page which hasn't been touched since the last gc cannot point to live objects in the nursery. A page which has been touched might point to live objects in the nursery, so we have to trace the pointers in the page as if they were roots.
The slide-down relocation is usually done by copying the object and leaving behind a forwarding pointer. In the case of linked lists and the like, the lists are walked recursively, often breadth-first, although depth-first can give better locality of reference.
During the migration of the live objects, we can increment G0 as we go, reusing the allocation mechanism, or we can adjust G0 after the slide-down relocations are finished. Either way, when we are done, we have expanded the area between G1 and G0, and no longer care what's above CEIL. If there is sufficient space between G0 and CEIL, we set FREE to CEIL and operate as usual.
If we are tight on space between G0 and CEIL we can either expand the heap (and increase CEIL) or we can collect one or both of the older generations. Again, we identify live objects and slide them down to the earlier generation.
In the case where we are creating lots of short-lived objects we are doing very little copying during the nursery collection -- we can end up effectively doing nothing but writing objects into the top of the heap then resetting the FREE pointer when we get there. If there are many more pages between CEIL and the top of the heap then yes we will be touching more pages than if we had a very disciplined explicit allocate-and-free mechanism. If our nursery is small, however, that is not the case.
There are other ways of handling the nursery, including the Cheney on the MTA approach of using C's runtime stack (alloca() and friends) to store young objects. We still have to find and live objects when our nursery gets too large, but the C "return" reclaims all the dead ones. The implementation is straightforward for a continuation-passing-style language compiled into C, works well with small nursery sizes, and has been implemented in the Chicken implementation of Scheme.
Optimizing the speed of nursery handling, and partitioning the
Sadly it's Verizon that has used the voice of James Earl Jones in its ads.
"No, Luke, *I* am your phone company. Search your phone bill, you know it to be true."
NOOOOOOO!
Most optical signalling is done with blinking on and off rather than other modulations. This is mainly because collimated coherent light sources make this approach really easy on both the transmit and especially the receive side.
However, through a medium like glass fibre, wave propagation speed varies nonlinearly with frequency. In other words, two identical-length pulses on different frequencies transmitted at the same time may be detected at different times. This becomes important when stepping up the pulse rate, because signals sent earlier on one frequency may be detected later than those sent later on a different one. However, even within one frequency, the pulse intensity will spread out over time because real light sources are not truly monochromatic, and because the signals may be rotated in terms of polarization, so that a rapid off-on-off may wind up looking like it has more than one "on" pulse. Although these effects increase with distance, and the distances within a single system (or especially a chip-sized package) will be small, they also increase with signalling rate.
These limitations don't look particularly scary in comparison with electronics, especially since there are other media -- including vacuum -- through which one could send the optical energy. However, at the moment there is very little real work that can be done in a purely optical system, so there will be E->O conversions (which is what the article is about) and O->E ones. Converting high bandwidth optical signals into something useful for electronics is currently a harder problem, and in telecommunications is essentially sidestepped with multiplexing.
On the scale of metres to megametres, optical pathways are clear win, mainly for the reasons you listed.
However, the reason that EO-OE links interconnecting chips via fibre aren't more popular is that on the scale of a few centimetres to a few millimetres it's an expensive engineering choice even in comparison to the difficulty in shrinking the distances between chips (to the point of putting multiple chips in a single package) or increasing the multiplex fan-out across more pins/paths/traces while maintaining a relatively slow clocking rate.
The EU does not have a common criminal justice court system, nor a common tort law or civil court system.
What you have described is common in civil code (Code Napoleon) member states. While these states are in the majority, there are other systems running in the European Union. There are also non-uniformities in the broad groupings of legal systems.
For instance, in the four common law member states (CY, IE, MT, UK), violations of rules of evidence taint the evidence and render a conviction based on the evidence unsafe. Moreover, there are some differences between English and Scottish law on these matters, even though England and Scotland are both part of a single EU member state, and both Cyprus and Malta have adopted some civil code rules into their systems, and Ireland has introduced a number of its own rules and practices as well.
Newer EU members have been spending years transitioning to the civil code courts system, mostly because it is easier for them to adopt EU rules into a civil code framework, however there are also obligations to the Organization for Security and Cooperation in Europe. OSCE is NOT an EU body -- it includes some 51 members. However EU members are expected to be OSCE members, and OSCE and the EU influence one another heavily with respect to elections and courts monitoring, reporting on judicial fairness, and so on.
That said, there are still considerable amounts of legacy legal procedure and theory in the former Communist countries which recently became full members of the European Union, and the rules of evidence can vary substantially from common practice in the civil code countries closer to the west coast.
There are a number of treaty bodies to which EU citizens may appeal a decision by a member state's supreme court. The important one is the European Court of Human Rights (in Strasbourg). The ECHR is NOT a European Union institution -- it is an international treaty organization to which EU members must belong, although non-EU countries in the region are also members, and there are some out-of-region partial-members and observers as well.
There is also the European Court of Justice (in Luxembourg), which IS an EU institution. Individuals cannot appeal to the ECJ directly, but they can ask their national courts to make a reference to them. Generally speaking this is done when there is unclarity about national legislation or rules with respect to an EU directive. The ECJ is not allowed to consider the facts of any given case, and it is expected to be very careful about conflicts between the acquis communitaire (the body of EU treaty law, directives, previous EU court decisions, and so on) and the national legal systems.
In general, the justice system in the EU is complicated by three main threads: different origins of the national legal systems, the principle of subsidiarity, and nation states having agreed to different sets of treaties. The last of these has been decreasing in significance as the EU member states and the Commission have been working very hard on that issue in particular. Subsidiarity is structural and effectively constitutional, and works against the development of a fully common system across the EU, and has led to the current approach of "translating" EU-wide rules into the local systems in a natural way, at the local level. Finally, the modern EU is only as old as the Treaty of Maastricht, which is from early 1992. There are literally thousands of years of legal tradition in many of the nation states with the occasional partial convergences brought on by conquest (the Roman system, and the Napoleonic system based on that being two of the main ones), "contagious revolution" (1848, the rise and fall of Communism in Central Europe, and so on), or multilateral negotiation (usually for economic reasons).
-- The Manchurian Candidate.
I bet Dvorak spends a lot of time playing Solitaire on his Windows machines.
I'm sorry, I didn't hear you over that netsplit.
I'm sorry, I can't connect to the server you like to use.
I'm sorry, there's been a division of the world into EFNET and Undernet.
I'm sorry, there's been another division of the world, breeding IRCnet.
I'm sorry, I was just nick-collided.
I'm sorry, I've been kicked and banned from #xyz by someone with a really thin skin.
I'm sorry, I've been squited.
I'm sorry, I've been klined.
I'm sorry, there's a flood attack going on.
It was fun in 1991, but since then IRC == It's Really Crappy.
Ah, yes, and while we're at it, let's get rid of the FDA, which reduces the supply of food and medicines ostensibly to control "quality", but really just increases the cost of food and drugs.
That people may end up becoming gravely ill -- or dead -- by consuming contaminated food or medicines is a good thing since it will weed out inefficient consumers. (The market will readily correct for the type of seriously ill person who is poor at making rational, informed choices.)
In the resulting free market, brands increase in value, and even in the presence of cut throat competition a strong brand would never risk the damage to itself by cutting costs in rigorous monitoring the safety-critical areas of their food or drug production chain.
So you're right... bad doctors aren't a problem to be prevented in advance by state-enforced controls on the market. Proactive regulation is a non-solution. A reactive tort-based system will encourage players in the free market to be even more careful about avoiding costly, harmful, and deadly mistakes.
Suuuuuure...
You probably don't mean deus ex machina, really, when you make the point that quantum theory seems forced. You're right -- the particles in quantum theory are not actually particles, they're quantizations that seek to capture the real-world effects of fluctuating energy fields by concentrating on the planck space with the maximum field energy at a given planck time. The only reason to do this is because the maths are easier and come up with useful and correct predictions, even though the quantization is not perfect.
This is analogous to the sampling theorem in telecommunications, where for example the useful information about a fluctuating field (pressure waves passing through air) can be quantized (digitized) at a given location (a microphone) without significant information loss if a particular sampling rate is used.
In both cases the real behaviours are complicated and involve wave-like propagations of energy-level changes, and in both cases there is substructure (i.e., there are smaller and smaller fields involved), but at most scales a 1d single-wave-like analysis or a 0d single-particle-like analysis can make sufficiently accurate predictions that performing a 2, 3 or more dimensional field type analysis is a lot of work for no gain in predictive power.
The problem is not (and for a while hasn't been) the overall bandwidth of the optical window through optical fibre (or even better media), but rather the channel bandwidth. A channel is simply a frequency which carries information, and you can have multiple channels travelling through media like optical fibre.
Each channel effectively transitions between bright and dim (blinks on and off, if you like), and the speed and accuracy of differentiating between bright and dim at the detect side is tricky. An analogy would be to the human eye, which has flicker sensitivity limits... blink a light on and off at about 30 or so blinks per second and the light apepars continuous.
Moreover, the blink and detect work is done by electrical->optical and optical->electrical conversions, and the data is almost always in the digital domain, and there are practical limits to the speed of A->D/D->A conversion and digital processing.
Increasing coherence -- that is, specializing (with better lasers and better filters) on a tighter and tighter range of frequencies -- is what lets one have more blinking channels per fibre. This is called WDM, wave-division multiplexing.
There are limits to the density of WDM because even if you emit perfectly coherent light on the transmit side, on the detect side, you get a gaussian distribution of frequencies. Photons will collide with particles of matter in the fibre, changing their energy levels and directions. This spreads a light pulse out in frequency and in time. Density (and also channel bandwidth) are limited by the narrowness of the gaussian spectrum you can look at, and by how well you can distinguish a brightness transition from noise introduced by the tails of nearby (frequency-wise) channels and delayed-by-bouncing photons (right colour, wrong time, leading to possible false "bright" or false "dim" results).
There is some old fibre out there where density is a bigger practical problem because there is more oxygen and other matter -- impurities, really -- which interact more profoundly with the infrared-frequency photons), but in practice there is little constraint on the number of channels available, and a maximum (and unfortun
No. Every member of the EU has pooled a chunk of its sovereignty together with the other member states, each of whom participates in the joint executive known as the Council of Ministers, which is the SOLE ORIGINATOR of EU-wide Directives. Who sits on the Council? All the heads of government.
Not one of the Prime Minister of the United Kingdom, the President of France, the Chancellor of Germany, among others, has sole control over the Council of Ministers, but each of those heads of government of the larger states can personally easily derail any initiative on a whim, and can bet that each could convince the majority, or all (as necessary) of his or her counterparts to support some joint plan that seems to make sense, although that's harder. Some combination of smaller-state heads of government can do likewise, and often do. BE-NL-LU often wind up as a bloc with the weight of one of of the larger countries, as do DK-SE-NO-FI and more recently with PL joining with one of LV-ES-LT and CZ-SK-HU.
This sort of joint pooling of sovereignty for common goals is what the EU is about. The problem is that at the Council level, the opposition is filtered out, because it does not participate. (This is worsened by a lack of transparency... many people do not know which head of government had what opinion on a given proposed Directive).
Offsetting the Council is the Parliament, which is directly elected. It cannot originate directives, but it can require changes, or delay or defeat them. The Parliament does include members of the opposition (and governing) parties of the various member states. It mostly operates in committes which pick their way through directives suggesting changes back and forth, and sometimes outright opposes a proposed directive by the Council. It has developed teeth, and would have had sharper ones under the recently deceased proposed constitution.
In the middle is the Commission. It's officially run by Commissioners (one or two from each Member State, traditionally) appointed by the Council with the consent of the Parliament (somewhat like how the U.S. Senate confirms Presidential Cabinet members, which would have been even more like that under the ex-proposed-constitution), but is really a civil service which has things delegated to it by the Council (i.e., the Member States). It aligns variously with the Council, whom it briefs and does studies for or some subset of the Member States (it often sees itself as a sort of guardian of the interests of the smaller member states, especially in cajoling the larger Member States to "play fair" and abide by things they've agreed to be obligated by, or have held smaller members to).
There is little more love between the Parliament and the Commission than the Parliament and the Council, mostly because under the current arrangements, the Parliament is fundamentally a criticising body.
There are analogous relationships between various national governments and their legislatures, especially where there is an upper house beholden to different masters than the head of government, or a lower house which they do not totally dominate.
The EU looks more and more like a normal confederation with every passing year, mostly because it seems to balance interests well enough that despite complaints about it (many of which are shockingly hypocritical) nobody can think of a better evolutionary direction than that taken by e.g. the various U.S. States, Canadian Provinces, Australian States, and so forth.
(As an aside, it's interesting that Belgium, Europe's most federalized state, tends to be overwhelmingly pro-EU-as-federation; Switzerland, Europe's 2nd most federalized state, has been quietly agreeing to a number of treaties with the EU that would have been consolidated into the proposed constitution, but generally is pretty
Even blindly accepting the accuracy of these numbers, at ca. 4500 kcal/lb of fat, given two people who burn the same number of calories in a day, person A consuming 500kcal/day more than person B, will put on one pound of fat more than person B every nine days.
Even if we adjust for the observation that heavier people burn more calories than lighter people, performing the same level of activity (or inactivity), person B will still put on about 2 - 3 lb more weight a month than person A.
That's more than 20lbs a year.
In that case, the source of the relative obesity is obvious, and your assertion that "[t]hat's not a big enough difference" is wrong.
I will even try to help you out a bit... let's say that Mexicans don't actually burn as many calories as Americans, because a bunch of Americans have this season called winter, where they have to burn more calories to keep warm. Let's use a nice deflator of 50% of your 500 calorie/day difference, i.e., 250 calories/day is spent dealing with the temperature difference between Mexico-average and USA-average.
We now have a weight-gain difference of 10 lb/year instead of 20. That *still* would account for a large difference in obesity rates over time.
Now, let's turn on your numbers. The "standard human" is 72kg (~160lb). That's men and women averaged together. A standard human will burn about 2300 kcal/day *in a completely sedentary state* (like in a coma).
Light exercise (walking to the toilet, getting out of bed) will burn no more than 1 kcal/minute more than being completely still. An office worker who doesn't walk around all that much will maybe burn 200-300 kcal/day this way. People who actually *exercise* may burn more... moderate exercise will burn 2 kcal/min extra and heavy exercise will burn about 3 kcal/min extra. An hour-long workout and some cool down and you can make it 400-500 kcal/day beyond base rate.
Smaller humans burn less when being comatose (they have less tissue to supply blood and nutrition to) and when they are being active (they have less mass, so have less inertia to overcome; they also have less surface area, less volume, and smaller cross-sections, which are important when moving quickly, or in water). Larger humans burn more. Moreover, muscle tissues consume more energy than fatty tissues even when they're doing nothing, so a person who is heavy because of musculature rather than fat, will burn more than any lighter person, or a somewhat heavier person who is not as muscular, even being completely still.
Taking all this into account, and cross-checking with actual observations using calorimeters, a standard human in the USA burns about 2500 kcal in a day. An average woman may burn about 2000; an average man may burn about 3000. Any more than that is very efficiently stored as fat. This is thanks to an abundance of calories being a feature of human existance only over the last 100 years -- millions of years of mammalian evolution has taken place under pressure from frequent food shortages.
To make it a bit easier, you come up with a similar number for your vegetative calorie burn rate if you multiply your weight in pounds by 12 (or your mass in kg by 26, for non-Americans). With a desk job and a commute that involves a few flights of stairs, a few minutes from a subway or bus to office (and home), a few minutes walking to and from lunch and the toilet, and so on, with occasional light housework you can multiply that number by *at most* 1.5.
At 180lb, then, it would be (180*12)*1.5 -> 3240. Round down.
If an average American (even just an average big male american) consumes *4000* calories a day when population studies observe only about 3000 a day *or less*, would mean about a pound of weight gain every five days.
The population does *not* ga
Dude, *water* is a chemical. *You* are made up of chemicals.
Corn syrup generally is made by breaking down corn startch, exactly the same way the amylase enzyme in your own saliva would break it down. The decomposition produces glucose in both cases. That's what your cells like to use to make ATP, which is the supplier of energy for most in-cell chemical reactions.
HFCS is high in fructose because a second enzyme, glucose isomerase, converts some of the glucose to fructose. Fructose is a common sugar found in *all* sweet fruits, nectar, and bee honey. It has three useful properties: firstly, it is much sweeter than glucose (so you need less of it for the same sweetness); secondly, it is more readily soluble than glucose at low temperatures, which is useful in e.g. cold drinks; thirdly, it is not as instantly usable by your body as glucose is -- you convert fructose you naturally eat in large amounts (especially in a vegan diet rich in vegetables and fruits!) into glucose, but it takes some energy to do so. In other words, you add less fructose than glucose, and that fructose gives you less net dietary calories. Moreover, the reaction is spread out over time, rather than happening all at once. This moderates the insulin reaction.
Result: *less* calories per drink than if you used glucose syrup (i.e., plain corn syrup). You get less fat drinking HFCS soft drinks. Less glucose rush per drink.
Now, consider drinks sweetened with sucrose -- from cane or beets.
Sucrose is a pair of sugar molecules linked together. The molecules are glucose and fructose. When you eat ordinary sugars directly, or in the tissues of *all* plants, your body first breaks that bond, so you have a fructose molecule to deal with *anyway*, just as if you had consumed it in a tin of HF(50%F)CS-sweetened cola.
Guess what... maple syrup is mostly water and sucrose!
The weight gain you're talking about is a simple energy problem. When calories into your body exceed calories out, you gain weight. Your body stores excess calories very efficiently in fat. When calories out exceed calories in, you lose weight.
Calories in = food energy. Of all sorts. Doesn't matter if it's from sugars (~17 kilojoule/gram), proteins (also ~17 kJ/gram) or fat (~38 kJ/g). The problem with sweet drinks is that it's lots of calories that don't make you feel as full as meats, or ordinary fruits and vegetables.
Substituting low or no calorie sweeteners cuts back on a lot of these unfilling calories, and they are often a considerable fraction of what pushes people over the point where weight gain occurs.
Substituting honey for sucrose, or maple syrup for corn syrup, really makes very little difference. You still are taking in lots of carbohydrates at ~4kcal/gram.
The *plausible* (and even some of the implausible) risks of heavy consumption of artificial sweeteners is *by far* outweighed by the healthy effects of reducing food energy in overweight people.
Unfortunately, even eliminating 140+ kilocalories per can of fizzy drink (times several cans a day, perhaps), most people still eat more than the 2000-2500 kilocalories worth of food energy than they burn off in a day, and so *still* gain weight. Scaring them away from ways of reducing calories by substituting low-calorie reasonably-as-tasty stuff is *harmful* if it ends up adding 300 kilocalories/day (or more!) excess intake, or putting on about a kg (or about two pounds) of fat a month.
Tag bits are nice so you can precisely distinguish pointers from everything else.
:-)
...00 (binary) is the address of a 32-bit word on a byte-addressed architecture; ...0000 (binary) is the address of a 64-bit word, and so forth.
This lets you implement a precise garbage collector rather than a conservative one. This makes modern tracing GC (fast, compacting (especially to increase locality of reference), incremental, even backgroundable) much easier to implement.
If you sacrifice one bit from your native machine's word size, you can distinguish pointers from all other objects.
Most modern systems use byte addressing rather than word addressing, and if you word-align all your data, you don't need your least significant bits. So on a 64-bits-per-word machine, you can sacrifice your LSB and still have 63 bits to play with; on a 32-bits-per-word machine, you have 31, hell, on a 16-bits-per-word machine you have 15. Don't ask about truly 8-bit machines like the 6502.
Word-alignment on a byte-addressed 16-bit machine gives you 15 bit pointers -- you only have to address every second byte, starting at all zeros. Address 1 is part of the very first word, so 0000000000000001 (binary) is something we don't need to address directly. We can use that 1 bit to indicate that the object is NOT a pointer.
We can do the same for 32 and 64 bits:
All you need is a shift-all-the-bits-in-the-word-left instruction.
Instead of: load register,memory; , store memory,register, we:
1. Load register
2. Shift word to the right. bit0 goes away, most-significant-bit is now 0.
3. Perform arithmetic operations as usual, leaving result in register.
4. Shift word one bit to the left, immediate-or the word with 1.
5. Store the word into memory.
This works great for 2s complement integer arithmetic, with an absolute range of values of 2^wordsize-1.
As noted, on 32-bit architectures and 64-bit architectures, we have more bits that are meaningless in pointers to word-aligned objects. We just do more shifts.
Typechecking involves a step between 1 and 2, where we look at whether the least significant bit is really 1 before doing integer arithmetic.
If we have more tag bits, we can have more typechecking along these bit-testing lines.
The cost of the register shifting can be small or large depeding on architecture. Usually it's small on modern chipsets.
It's not clear that hardware tagging would be a clear win. Mostly it would have to be evaluated in terms of decreasing the number of instructions by consolidating dropping back to e.g. three to do an untypechecked integer load-add-store from six (load-shift-add-shift-or-store). That is, the extra three instructions are unlikely to add so much extra dependency or processing time, or rob users of sufficient fixnum precision, that pushing distinguished and precise identification of pointers into the processor becomes worthwhile.
Moreover, with foreign-function-interfacing, one can jump into another language (like C) to do full native untypechecked integer (or other) maths, just so long as it doesn't put results that look like pointers into GC-managed memory.
Chicken is one example of this approach. It uses Cheney-on-the-MTA to do the nursery area of its fast, compacting, generational garbage collector *portably* to byte-addressed 32-or-64-bit word architectures. Since all objects are word-aligned, that gives two or four bits of "tag", which it uses to precisely identify pointers, integers, and a few other types, both for typing and for GC purposes.
By comparison, the CADR's hardware architecture ultimately supports polymorphism
Hindsight.
At the time, the obviousness of being able to target an industry-standard, pervasive platform, just wasn't obvious. There simply wasn't such a platform with many megabytes of memory, many gigabytes of disk, processors which could handle hundreds of millions of operations a second, and so forth.
There were interpreted Lisps on a variety of platforms that were in common use. There were a handful of compiled Lisps. Most notably, MacLisp in ITS (and later on other OSes on PDP-10s), which was so popular at MIT's AI lab that enough was developed in it that performance became a key issue.
At the time it made sense to investigate hand-building a machine that would be an optimal target for a compiled Lisp, especially in that it would make known optimization techniques easier, and would welcome rather than tolerate large garbage-collected heaps.
Nowadays, Scheme textbooks like the excellent SICP: Structure and Interpretation of Computer Programs (or, say, The Schematics of Computation) introduce compiler-writing and target an implementation of a virtual machine. These VMs are directly analogous to the actual CADR hardware. On modern fast computers, they run faster than the actual processor hardware in the physical CADR. Running a VM with performances down in the 1/double-digit or 1/triple-digit of physical processors would have been intolerable back then.
There were always people working on targetting mass-produced general-purpose hardware, it's just that there was a lot of learning to be done about how to optimize things.
Lots of that work was done in the context of the amazing T project. The T compiler was a massive watershed development for Lisp compilation, as it implemented or introduced such things as efficient lexical scoping; simplifying the code through transformation until you were left with operations on only as many variables as you have registers; lambda-lifting; CPS-transformation; dramatically improved copying garbage collection; "pseudo-hardware typing". A number of other innovations directly stem from these.
One look at this is Olin Shivers writing about his memories of the T project.
Nowadays, with these techniques, and others learned since, compiling Lisp to practically any modern processor is straightforward. Modern Lisp and Scheme compilers generate either efficient and fast native code, or portable C (to the extent that it should work on any 32 or 64 bit platform gcc supports).
There remain some implementations that target a tight purpose-written VM, like CLISP or Scheme48 does. There are some implementation that target other VMs, particularly the Java one, compiling into JVM bytecodes or Java as an intermediate language.
The cheapness and reasonable speed of VMs, the efficiencies of compilers targeting C, assembly, or native machine code, and the tightness of runtime environments supporting type-handling and garbage collection, are such that practically nobody would seriously think about building hardware dedicated to supporting Lisp like languages.
With the benefit of hindsight, you are completely right.
The two points you raise, prior to T, would have been thought of as somewhere between funny and insane.
T was ca. 1982.
The CADR Lisp Machine was started ca. 1975 and documented in AI memo 528 in 1979.
Lisplike-compiled-language-favourable hardware chugged along until the early 1990s, when they collectively could no longer compete with the performance (never mind the price) of implementations compiling for x86, SPARC, Alpha, and a few other popular workstation instruction sets.
Wow... 12 years. Lots has changed since in far fewer years than that!
Maybe you should check out PLT Scheme, a modern scheme interpreter and compiler and IDE, with several "Teachpacks" which help one come up to speed in doing clever things with Scheme. DrScheme is the thing to download, for various platforms.
There are plenty of other Scheme implementations out there, interpreted and compiled.
Personally I do a lot in SCSH, the Scheme Shell which is handy for writing scripts and tools close to the metal on on UNIX/POSIX systems, however PLT and other environments are incorporating more and more of SCSH-like goodies into their libraries, and write stuff that needs to be fast in Chicken or Gambit, which compile Scheme to C. On the Common Lisp front, on a Mac, there is also OpenMCL, which compiles to *particularly fast* native PowerPC code and has a straightforward way of communicating with Cocoa and a number of other Mac-development-friendly features. In fact, all of these implementations have foreign-function-interface abilities which let you call e.g. C functions from Scheme/Lisp, or vice-versa, so you can write performance-critical sections in a low-level language of your choice, and use higher-level languages to develop "smarts".
Surely you were using a Symbolics machine, and not an MIT CADR?
This is not Genera.
One can see in this software and the CADR the start of what evolved into the Symbolics 3600 series and its software.
Since you mention a price tag (in non-US currency even), and imply that it was used for commercial purposes, you are almost certainly not thinking of the CADR prototype and research system.
Probably this is what you are thinking of: Symbolics Lisp Machine Museum.
There is a link on that page to the CADR, under "Precursors and Competitors". The CADR was a precursor.
Couple quickies:
1) There are non-white geeks.
However, to go a bit easy on you, melanogenesis leaves clear markers: the thymidine dinucleotides (four amino bases, two of which are T) from UV destruction of damaged DNA, alpha-melanin-stimulating-hormone which is produced in response to this, the binding products (other than melanin) of alpha-MSH to melanocytes, and the transport of melanin vesicles to the keratinocytes, and their consequent migration from lower layers of skin to higher layers. The last is likely to be the largest archaeological giveaway, other than studying your nuclear DNA. Pinker (with less melanin in melanocytes) flesh to darker flesh transitions in layers are what differ a suntanned pale person from a naturally dark person.
Finally, the presense of melanin at all (or tyrosinase, for that matter) will distinguish a pale, unsuntanned person from an albino.
A future in which nuclear DNA, even in scattered fragments, can be analysed for this sort of thing, is also a possibility.
2) To make things even easier on you, there is no guarantee that your remains will fossilize. Fossils, even in amber-rich areas, are very rare compared to the number of things dying in the areas fossils are found.
Most resin (amber) fossils *of animals* are generally only discoloured cavities, in which the organic structure is gone, and all that's left are chemical traces of chitin (in the case of insects and spiders), sometimes hair or feathers, or bits of bone.
While the drying effect of really sticky maple syrup (osmotic dessication... water moves out of exposed cells, where it's in greater concentration, and into the syrup, where the water:sugar ratio is lower) kills off surface bacteria and the like, your insides are just teeming with bacteria which will gladly do their best to digest you from the inside out. Moreover, neither your epidermis nor the maple syrup is chemically inert and they aren't likely to be in a stable configuration.
Your calcified bits (bones and maybe teeth) may well survive, but unfortunately that leaves future archaeologists with no direct evidence of what colour your flesh really was, since it will have long since changed into a mass of stained sugary crystal.
I, for one, welcome our new resin cast fossil underlords.