2006-01-14

Copyright Reform, First Draft

At this point things aren't well organized. Just a list of items. At some point I'll make a new post once I have sufficient changes collected and stop editing this one. I'll edit it to add a link at that point.

  1. The intent of this law is support a diverse field of artistic works. The intent is not to prevent the use or distribution of such works. (XXX or to be used as corporate leverage?)
  2. This law shall not impose any effects or restrictions on works not of direct artistic intent, or who's practical necessity exceeds that of the artistic intent. This includes (but is not limited to) any legal documents, data collections, specifications of interactions(XXX), XXX.
    1. A specification of interaction is defined as a work explaining the size, behavior, or other property of a subject of interaction, as necessary for it to be used or interacted with.
    2. Subjects of interaction include (for example) physical objects (such as a bolt), processes, software protocols, formats or other interfaces.
  3. This law shall only apply to the expression of the work, not to the abstract concept underlying it.
  4. An entity in posession of a work shall be permitted to do anything(XXX) necessary to display or make personal or private use of the work. This includes redistribution of altered forms (such as translations) to other posessors of the work when they are not otherwise readily available.
  5. Upon receiving a work an entity shall be obligated, if they wish to duplicate it (other than otherwise permitted in this law), they shall be obligated to acquire a license from the author.
  6. If an entity has possessed a work for atleast five years the author shall be obligated to provide, as an option to the licensee, a flat fee of 50% of revenues relevent to the duplication (XXX or selling of the duplication?) with no other restrictions. It is not expected that all such licenses shall have appreciable revenue.
  7. If an entity has possessed a work for atleast ten years they are no longer obligated to obtain a license and may duplicate the work without restriction.
  8. No contract shall restrict an entity from duplicating or distributing a work for more than fifteen years after they first received it. Attempts to do so shall be deemed null and void (XXX).
  9. Within fifteen years of first receiving a work an entity is obligated to accurately inform the receiver of any duplicates as to the identity of the author, to the best of their ability.
  10. XXX I need something involving advertising. Using a character to advertise your work should be protected for longer than the normal terms. [Update] First 20 years consider the work to be an endorsement of the author, and as such any use of the work that operates as endorsement require the author's consent. XXX is there a specific law I should reference here?
  11. [Update] XXX Need a way for publically distributed materials to revert to the earliest date of distribution. Maybe private as well? Is there already common law on this?
  12. [Update] XXX I think the “upon receiving the work” dates are too complicated. Too many different people with different dates. Instead I'd prefer “public dissemination” (aka publishing) or something of the sort to be the primary date, with an extra 5 years for an “upon receiving the work” fallback.
  13. [Update] XXX The identity aspects should perhaps be expanded to rewrite trademark laws as well. There's essentially two purposes to identity laws. One is to ensure a consumer gets the product they expect (this is what current trademark laws do and it's essentially to make fraud less ambiguous.) The other is allowing consumers to locate the author of a work and support them (i.e. by buying merchandise or other works.) Notably lacking is a desire to give the author fame.

2006-01-09

Floating Point and NaNs in Python

Python's current support for floating point is very poorly defined. In fact all it does is say it depends on what C does. C in turn says very little, which leaves the programmer with no practical way to reason about floating point in his program.

I believe this could be fixed, although not without significant effort, and (unavoidably) harming the performance of floating point. First though I'd like to discuss NaNs.

NaN stands for “Not a Number.” It is used when we don't know how to represent the result of the operation, or rather the form we're currently using can't represent the result. One common way to produce a NaN is to evaluate Infinity/Infinity. anything/Infinity always produces 0, Infinity/anything always produces Infinity, so Infinity/Infinity should produce both 0 and Infinity. Obviously it can't be both so we say it produces a NaN instead. Or maybe it should raise an exception, but that's not what I want to discuss. I want to discuss how to handle a NaN once it is created.

Most people “know” that IEEE Floating Point requires “any comparison involving a NaN must return False, even NaN==NaN.” However, from my digging on the web it seems this is not what IEEE requires at all! A post by DWCantrell[1] quotes:

Four mutually exclusive relations are possible: "less than," "equal," "greater than," and "unordered." The last case arises when at least one operand is NaN. Every NaN shall compare unordered with everything, including itself.

So what IEEE requires is you treat NaN as “unordered.” In C the closest that can be done is to treat all comparisons as False, which is probably why people think returning False is what IEEE requires. But in Python we can do better, by raising an exception. Those that have a sane fallback could catch the exception, but the rest of us could rest easy in the fact that our errors will be reported.

Unfortunately there's another problem. In Python identity supersedes value. “a is b” automatically implies “a == b.” We may be able to treat it as “unordered” in some cases, but not all (notably those involving containers.) But this begs the question, do we really want it to be “unordered” in all cases? We say “unordered” because we have no reasonable ways to compare by value, and because IEEE Floating Point only deals with comparisons by value, “unordered” is the only answer it gives. However, basic math does use identity comparisons! “a=a” is obviously a true statement. So why should Python limit itself to value comparisons when the identity comparison is just as valid and provides us with more information? Thus, I believe Python should not only tolerate identity comparisons involving NaN, but it should embrace them!

However, that leads us to needing to preserve the identity of NaN objects. This is actually quite easy. We simply need to create a NaN type which cannot be interned (unlike other number types), and have floating point operations return it instead. All comparisons would raise an exception unless they are comparisons with itself. This should all be the default behavior for non-number types, which is not surprising since NaN is “not a number.”

As an aside, you may wonder if this affects numeric arrays. It does not. A copied NaN will not share identity with the original object, and thus will not compare equal with the original object. Numeric needs only document that it always copies the NaN objects.

One final note on NaNs. If identity is significant then you obviously don't want a global NaN constant. Instead I propose a makeNaN() function, which would create a unique object with every call.

Okay, back to floating point in general. What I believe we need is to do is provide identical results on all platforms by default and provide an option for faster computation if less stringent results are required. Ideally it should also have a way to simulate those less stringent results even if they're not native to the platform, to allow testing and development of all platforms from a single computer.

The way I believe this should be done is using Context objects, similar (but not identical) to what decimal provides. A few notes:
  • What should the size of the output be? I believe the simplest behavior is to have the output be exactly specified by the Context, and not be affected by the size of the inputs. This makes operations such as 1/somefloat(3) produce an obvious result. [Edit] It also means Infinity can be a singleton, maybe even a separate class with no concept of size.
  • I believe Context objects should be created by a factory function and should be immutable. The reason is that altering a hardware context is actually quite expensive, and allowing attribute modification would be misleading. It's easier to swap two unchanging Context objects than it is to repeatedly compute the state of a single changing Context object. Additionally, having Contexts be immutable means there is less exposed when the user wants to create their own Context class (to produce intervals for instance.)
  • There should be sys.getfloatcontext() and sys._setfloatcontext() functions, providing access to per-thread contexts. Note that sys._setfloatcontext() has a leading underscore, indicating that it is private—only those hacking on the implementation are expected to use it, not those wanting to changing the default context (use a private context instead.)
  • How to implement this all is probably the biggest issue. The best solution I have is to use LLVM to access the hardware in a portable way while circumventing C entirely. This has the added advantage that it could be reused by the PyPy project.
  • The most common Contexts would be IEEE-like 32bit, 64bit, and 80bit sizes. They would provide exact results for all operations and functions (even transcendental functions such as sqrt()!)
  • If exact results are not necessary then we could either provide functions named loosesqrt() or provide a loose context where all functions are loose. We would then have to decide on whether the functions will be documented to provide results within a certain range, or if they're defined to provide any result at all (random()?)
  • A loose context may be permitted to arbitrarily pick the size of the objects it outputs, so long as those objects are stable once created. For instance, “c.divide(1, 3) == c.divide(1, 3)” may return one object in 32bits and the other in 80bits, thus causing the comparison to return False.
  • [Edit] It may not be obvious so I'll say so explicitly: math.sqrt() would become a wrapper for sys.getfloatcontext().sqrt()
[1] http://groups.google.ca/group/sci.math.num-analysis
/msg/912e7246f03b4185?hl=en

2006-01-06

Safe Threads

I need to explain why safe threads are so important. Basically you have three options:
  1. Honour system. The programmer uses explicit locks and is expected to do things right. Any mistakes invoke a memory model (as in Java) or undefined behavior (as in C). Either way you get bogus results. May significantly harm performance when the compiler can't reason about the locks used, as it has to fall back to the most generic code possible (that conforms to the memory model.)
  2. Compiler-enforced locks. As above, but when the compiler can't reason about a lock it emits an error instead of emitting generic code. Performance is optimal, but forces the language specification to include compiler internals.
  3. Inherently safe primitives. Inter-thread queues that only permit deeply immutable (or otherwise thread-safe, such as the queue itself) objects as contents. Atomic reference objects that allow alteration and retrieval of a single reference in an atomic (thread-safe) manor, and again requiring that reference to be deeply immutable. These primitives cannot be subverted, thus keeping the compiler easy, code reliable, and language specifications small.
The first option violates many of Python's principles:
  • Explicit is better than implicit.
  • Readability counts.
  • Errors should never pass silently.
  • In the face of ambiguity, refuse the temptation to guess.
  • If the implementation is hard to explain, it's a bad idea.
The second option, while better, still violates at least one principle:
  • If the implementation is hard to explain, it's a bad idea.
I believe the third option is the only acceptable one for python.

BTW, "deeply immutable" means that the object itself is immutable, all objects it references are immutable, all objects they reference are immutable, etc.

With the third option most of it could be kept out of the language specification, although it would need to be a core part of the implementation, not just an extension module. Unfortunately there is one aspect of the language that requires such threading, namely finalization (including weakref callbacks.) Not everybody realizes this but finalization is a form of concurrency. It causes functions to execute at unpredictable times and with no natural granularity to determine how they should interact with already executing functions.

The only reasonable way I see to handle finalization is to use safe thread mechanisms. It doesn't matter whether this is done using a single queue that the programmer must check explicitly, or by spawning a new safe thread for each finalizer function, just so long as it's entierly safe and predictable.

2006-01-01

User-defined Operators

I'm not convinced they should be added but if they are to be added they can be done fairly simply.
x = (foo oper bar)
Equivalent to:
x = oper(foo, bar)
There's a lot of notes to be made though.
  1. oper is looked up in the local (and global) namespaces, not as a method of foo or bar. If you want it to be a method you need to make the local/global function do all the lookups itself.
  2. oper can only be a single name. It cannot be an expression. The reason is that ((foo)(bar)(baz)) is currently legal, meaning (foo(bar))(baz). A limited subset of expressions could be permitted, but I feel it is better to educate users on a "no tolerance" policy.
  3. Custom operators only become really useful when Unicode variable names are permitted.
  4. There's never a question of precedence with this syntax (unlike builtin operators).
  5. Most builtin operators could be altered to use this syntax, but it would make them a bit more verbose. 1+2*3 becomes (1 + (2 * 3)). However, this bloat can be countered by making parsing aware of word boundaries and by making the parenthesis optional under most conditions. Then you return to 1+(2*3).
  6. [Edit] Perhaps the biggest drawback is requiring the user to explicitly import all operators that may be used, ie from math import ×, ≺, ≻, ≼, ≽, ⊀, ⊁, ∈, ∉, ∋, ∌, ⊂, ⊃, ⊄, ⊅, ⊆, ⊇, ⊈, ⊉, ⊊, ⊋, ∩, ∪, ∖, etc. An alternative would be defining a protocol that does use a method of one of the arguments. Unfortunately that significantly increases the complexity of my proposal, so I'll leave it as an exercise for the reader.
I think that's about it. As I said, I'm not convinced they should be added, but if they are I think this is how it should be done.