424 karma · joined August 16, 2007
The names of the classes can be confusing, but ${CLASS}-hard means "no harder than ${CLASS}", not " no easier than ${CLASS}".
So, for instance, every problem in P is NP-hard.
${CLASS}-complete, on the other hand, means that its members are both:
1. ${CLASS}-hard and 2. No easier than any other problem in ${CLASS}.
Exactly this. I would actually back off a bit and only claim that reference counting has more predictable latency, but the point is to sacrifice throughput in order to reduce worst case latency.
There are a few things I learned in the process that I think are relevant here:
1. Forget garbage collection in the sense of any variant of mark-and-sweep. For an operating system latency is more important than through put, so you really want real time memory management. If you use reference counting, then there is a lazy variant that gives you just that; no pauses due to reclaiming free memory. The downside with reference counting is that it doesn't work with cycles in heap-allocated memory, but if your data is all immutable then you can prevent cycles from being created in the first place.
2. Treat mutable memory as a form of I/O. Even if you just want to use memory mapped I/O, you will need some way to read and write to (some portion of) memory. Set aside a fixed block of memory (say, for instance, the first 10 MB), and only allow that memory to be read and written in an imperative fashion. Keep the managed heap completely separate from that mutable block, and don't allow any direct access to the managed memory.
3. You can actually perform preemptive multitasking entirely at compile time (or at program load time for apps not included when you build a binary for your OS). I've done this for both my OS and for Scheme (https://github.com/ojarjur/multischeme). It works extremely well and winds up being much faster than using the native primitives usually used for building multitasking support. For more details on exactly how to do it see the link to my Scheme implementation.
4. The biggest difficulty the author will hit, and the most likely cause for performance issues, will be in code generation. Since the proposal is for a new bytecode, the author will have to implement their own interpreter and/or JIT compiler for it, and writing an efficient one is an extremely large chunk of work. I've built an operating system and a programming language, and the language is by far the harder project. This is why I went from working on a new OS written in a new language to re-implementing my compile-time multitasking technique for Scheme; it allows me to take advantage of all of the hard work people have done to make compilers for that language which produce fast code.
Does "final draft" mean this is the official R7RS-small standard, does it mean this is the final text being sent to the steering committee for endorsement, or does it mean something else entirely?
The way it works is if you send enough mail to the same zip code, then you pay a certain discount (a few cents per mail piece) if you package all of that mail together. The amount of the discount varies depending on if you sort by 5 digit zip code, or just 3 digit zip code prefix.
Unless you know a way of extending this to handle an arbitrary amount of I/O then it's just a way of implementing nondeterministic finite state machines; which are no more powerful than deterministic finite state machines.
The word itself is perfectly applicable in various contexts that have nothing to do with startup companies; charities, political parties, bands, and internal projects. As such, referring to Buchheit as the founder of Gmail is reasonable.
My suggestion would be that if you want to join Google then just send them your resume. Let them decide whether or not they want you rather than deciding that for them.
That all being said, the concept of "taking ownership" without having any control of how you do your job is bullshit. The only way to solve that problem is to tell management that they are solely to blame, but the solution you get may be to no longer work for them; you have to decide for yourself if it's worth the risk.
NIL = (lambda (x) (lambda (y) y))
CONS = (lambda (ar) (lambda (dr) (lambda (op) (op ar dr))))
CAR = (lambda (expr) (expr (lambda (ar dr) ar)))
CDR = (lambda (expr) (expr (lambda (ar dr) dr)))
sample = (\this->(\x->x))
Then, (Y sample) can reduce as follows:
1: (Y sample)
2: (sample (Y sample))
3: ((\this->(\x->x)) (Y sample))
4: (\x->x)
Now, if your evaluation strategy required evaluating arguments to final form before applying an operator to them, then line 2 would expand forever:
2: (sample (Y sample))
3: (sample (sample (Y sample)))
4: (sample (sample (sample (Y sample))))
... And so on.
However, neither call-by-need (lazy) nor call-by-name evaluation strategies require evaluating an argument before applying some operator to it. Thus, in languages that support either of those evaluation strategies, the operator can be evaluated to the point where it throws away the argument, and the argument need never be evaluated.
This is a contrived example but real world recursion works for the same reason; at some point in the evaluation of the function, it no longer needs to be applied recursively, and once it reaches that base case, the recursive function can be discarded.
Personally, I would appreciate a term that can be used to specifically refer to software hackers; something that conveys the concept of programming as an artistic medium.
First of all, you DO forget how to ride a bicycle. Anyone whose gone for more than a decade without riding on and tried to take it back up should be able to attest to that.
Secondly, this research doesn't seem to have anything to do with retaining memories; just how they are formed.