Archive | February, 2007

More PygeonJava

28 Feb

Continuation of previous post…

1) No inner classes: no classes as static members, no classes as instance members, no local classes, no anonymous classes. The advantages of these constructs are:

  • Documents that the class is used only by its enclosing class.
  • Allows access to enclosing members in certain cases.
  • More convenient than writing the class elsewhere.

My reasons for omitting inner classes anyway:

  • The concept of a class as an instance member of another class is tricky to explain.
  • The rules about outer member access are strange because of the peculiar way in which inner classes were added to the language.
  • Anonymous inner classes are particularly useful, but fitting a whitespace-sensitive construct within expressions requires strange special allowances.
  • Most importantly, inner classes are for the most part syntactical conveniences.

2) ‘break’ and ‘continue’ labels are in. The labels are declared with ‘label’ on the line preceding the ‘while’ or ‘if’ to which they apply (heh, a bit of experimenting just now told me labels can actually be used not just with loops but with ‘switch’es and ‘if’s; seems labels can’t precede an ‘else’, but they can precede an ‘if’ immediately after an ‘else’).

3) Explicit casting everywhere: no automatic widening casts, no autoboxing, no nothin’. Enforcing this will require inspection of the imported types, for otherwise, automatic casts would be accepted by the compiler. (With all this casting going on, I’m considering abbreviating the casting operator from ‘cast’ to just ‘c’).

4) No Java 5.0 features: no autoboxing, no enums, no generics, no annotations, no covariant returns, no varargs methods. Aside from autoboxing, I find the syntax and subtleties of these features strange enough myself that I can’t see learners faring well with them. Besides, they are for the most part just conveniences. Covariant returns are not allowed because this would break the explicit casting requirement. (On the other hand, learners will shortly encounter some 5.0 features in the library if they look around the docs; of course, PygeonJava has always been designed to allow learners to pick it up ASAP and then drop it ASAP, so learners should be on to real Java before they give the docs a serious look.)

5) Abstract classes are in. Even though an interface is arguably just a limited kind of abstract class, interfaces are in too because of their important role as a kind of pseudo multiple-inheritance. In interfaces, the public and static modifiers on each method must be present as a reminder to learners.

6) No assertions. On one hand, assertions are really useful because they can be toggled on and off by a runtime option, which is very useful, but on the other hand, assertions are essentially just a shortcut for if (!bla) {throw exception;}.

7) As with PygeonC, no for, enhanced for, do-while, switch.

Some PygeonJava rules

25 Feb

In previous posts, I introduced the idea of an alternative syntax for Java designed for educational purposes, which I’m calling PygeonJava. The design is still in progress, but here are some tentative ideas, most of them attempts at reducing learner confusion about the resolving of named things (i.e. identifying what identifiers refer to).

  • Each part of a package name ends in /, e.g. java.lang is written java/lang/ . This makes packages easy to distinguish from object references and type names.
  • No static imports.
  • No default package. All files must declare their allegiance to a package.
  • No default access.
  • Importing multiple types of the same name causes a conversion error (the program that translates PygeonJava to Java I’m calling a ‘converter’). To get around this problem, fully qualify the types you need in the code with the full package name, e.g. java/io/File.
  • Similarly, it is not allowed to import a type of the same name as a type in the current package.
  • Types in java.lang are not automatically imported, even Object. (Is this extreme? Certainly, but it means learners can use a consistent process when they see a capitalized process and want to know where it comes from: if it’s not qualified by its package name and its not in the import list, then it must be in the current file or at least the same package.)
  • In the spirit of explicitness, all calls to the super constructor should be required to be explicit, so you’ll always see super() (or some overloaded variant thereof) as the first line in a constructor, even for a class extending Object.
  • Also in the spirit of explicitness, a class should be required to include at least one constructor, even if it’s just a ‘default’ constructor with nothing but a call to super().
  • All uses of instance fields and methods of the current class must be qualified by ‘this’—except instead of using the word ‘this’, ‘me’ is used instead (mostly because it is shorter but also because I remember finding the semantics of ‘this’ strangely confusing). The problem I mean for this to solve is confusion in distinguishing locals from members and rules about name collisions. Arguably, methods need not be qualified explicitly by ‘me’, but I think it’s more consistent to require it.
  • A concern is that static members are not really part of an object, so referencing statics via object references rather than explicitly via the type name seems wrong because the object is not really being used except as a representative of its type. The stringent thing to do would be to require all references to statics to be done via their type name, not via instances or with the ‘me’ reference. This comes very close to covering all cases, but it’s possible in foo.bar() that foo could refer to a thing of type X or a descendant of type X, and static method bar could have been overridden in the descendant of X. I don’t really see a solution, and I’ll just have to accept this Javaism. I think it’s sensible to advise users to reference statics via the type name as much as possible, especially in favor of referencing them via ‘me’. Seems I was wrong: statics are resolved always at compile time using the type of the reference, not at runtime using the actual type of the object referenced. So it actually is a good idea to require all statics to be referenced via the type name. Unless I’m overlooking something, I guess the reason Java doesn’t have this rule is because it was considered inconvenient.
  • Similarly, inherited members are accessed only via ‘super’, not ‘me’. This eliminates another arbitrary choice for learners, reinforces the concept, and reminds learners where the member comes from.
  • Initialization sections can be simply omitted from PygeonJava, and I’m thinking the same about field initializations. It’s generally a bad idea to write code that relies upon subtleties of the language that few of its practitioners understand, and I feel the execution order rules concerning field initializations, initialization sections, and constructors fall into this category. Better to just eliminate the whole question.
  • A class file has this enforced order: static fields, then instance fields, then constructors, then instance methods, then static methods (or maybe static methods before instance methods). This is essentially arbitrary, but it’s the most typical style I’ve encountered. (I haven’t decided whether to include named inner classes, but if so, they would go after everything else.)
  • Allowing interfaces to include static final fields is quite confusing as it distracts from the main purpose of interfaces.

I’m beginning to reconsider what I said about Java not being as much in need of an alternative syntax as C. Sure, nothing is quite as glaringly flawed as C’s declaration syntax, but things add up, and I’m beginning to remember how confusing I found parts of the language when learning it.

I’m also beginning to realize that, rather than making my converter merely produce workable Java using one-to-one translations, it may need to do some semi-sophisticated analysis to enforce some of PygeonJava’s restrictions. Additionally, it’s quite important for the converter to give good error messages of its own because learners won’t be able to comprehend javac’s messages—professional Java programmers have a hard enough time understanding compiler messages, so I doubt students only familiar with an alternative syntax of the language would fare well.

PygeonJava: is it worth bothering?

21 Feb

The need for an educational alternative syntax for Java is less compelling than for C, assuming, at least, that Java is not the first language learners are being exposed to. Not to say that Java is particularly easy—it’s gotten rather baroque rules, in particular, surrounding inheritance, access, and generics—but it’s not the syntax of these features that is really the problem.

In fact, most of the real danger areas of Java syntax are where the similarities with C’s syntax are misleading. In my own experience of learning C before learning Java, this is the case with Java’s array syntax (and it certainly didn’t help that I wasn’t really straight about C’s array and pointer syntax). Conceptually, Java’s arrays are conceptually quite different from C’s arrays, and the fact that they aren’t allocated on the stack has a number of important consequences, such as allowing array size to be specified dynamically.

Here’s my current idea for array reference syntax:

foo:a-char // declare reference to array of chars

foo:a-char (3-char) // same as above, but initialize to an actual array (note the size has to be supplied)

foo:a-char (bar-char) // size of array is given by expression (the variable bar)

foo:a-char1 // leave first dimension size inferred from number of elements

foo:aaa-Dog // declare reference to 3-dimensional array of Dogs

In the end, Java arrays are still a rather complicated matter and so real understanding is probably only gained by a thorough discussion of what’s really going on in memory.

[...]

  1. add bar 3)-char) // size of array is given by expression (sum of bar and 3)

    foo:a-char (a-char ‘a’ ‘b’ ‘c’) // actual values are supplied and so size of array is inferred from number of elements

    foo:aa-int // two-dimensional array of ints

    foo:aa-int (4-a-int) // create two-dimensional array of ints, specifying the first dimension

    foo:aa-int (aa-int (a-int 3 5) null (4-int []

Why icons suck

18 Feb

Here’s the first of a series of posts I’ll write about interfaces.

The use of icons in GUI’s is usually a misapplication of the truism that, ‘simply having users point at the very thing they want is the simplest and most intuitive kind of selection’. The problems are that:

  1. Pictographs do not scale as well as text because you can’t alphabetize or do searches on images.
  2. As you add more icons to the user’s view, the visual distinctiveness of each icon quickly gets blurry and ambiguous, thus defeating the sole true virtue of icons.
  3. Icons are generally not “the very thing” that users are looking for. A pictograph typically provides hints about the thing it represents but is not synonymous with the thing itself.
  4. Worst of all, interpreting pictographs is more mentally taxing than reading a word or two, especially when the semantic content is even mildly abstract.

The crux here is that it is far easier for most people to recall the general qualities of a picture—its dominant colors and overall shape—than it is to recall its precise details. Compared to abstract images, images of recognizable objects are much easier to recall details of because we can mentally fill in the blank spots with our assumptions of what such objects look like (so this kind of recall is actually partially false, but it generally works well enough). This explains why most icons in software are so bad: most icons found in software have historically been small, indistinguishable messes that most users just come to think of as abstract shapes even though their designers tried to make them recognizable objects.

Now suppose I know what I want my software to do but don’t remember at all how the interface designers decided to label that function, either with text or an icon. If I’m looking for a label, I have to figure out what words the designers chose to describe it, which often requires consulting my mental thesaurus. In contrast, if I’m looking for an icon, I have to figure out what words the designers chose to describe it and then figure out how the designers chose to represent it as an image. While the number of synonyms for a particular concept can be frustratingly many and elusive, the number of visual representations for a concept are innumerable: even if you narrow down the concrete object(s) being depicted, there are still the variables of perspective, composition, style, and color (sure many real-life objects only come in one color, but many don’t; in fact, looking over the icons in a few applications, I notice a strong majority have basically random color assignments either because of the nature of what they depict or because of the need to make them stand in contrast to their neighbors).

So I’ll posit a few rules about when and how to use pictures/icons:

  1. All but the most frequently encountered icons should be labeled by text. Many applications omit text labels because small, unlabeled icons allow for buttons that minimize space use (see Photoshop). This is a poor trade off. First of all, for the issue of image recall outlined above, but also because even the best designed icons rarely communicate their function as clearly as a word or two of text . In fact, the real utility of an icon is that its shape and color makes it noticeable to peripheral vision or visual scanning, so they help users find points of focus and do an initial culling of their possible options; at that stage, however, users have only narrowed their options and prefer the relative precision of words to help them make their final selection.
  2. Icons should be simple in shape, distinct in silhouette, have contrasting interior lines, and never use more than two colors.
  3. Icons should be as big as necessary to make them conform to rule 2.
  4. The number of icons that it is acceptable to use is proportional to how large and distinct they are, vis-a-vis rules 2 and 3. The array of icons found in today’s typical complex apps, like word processors and Photoshop, is too many by a factor of about three.

The next post about interfaces will talk about how the World of Warcraft interface could be applied to the desktop.

PygeonC function declarations/definitions and function pointers

11 Feb

I considered many candidates for function declaration syntax. Imagine we want to declare a function named ‘foo’ that returns int and has three parameters, x, y, and z. Here are some of the candidates I considered:

func foo:int x:int y:short z:char

func foo:int params x:int y:short z:char

func foo returntype int params x:int y:short z:char

func foo rtype int params x:int y:short z:char

func foo int params x:int y:short z:char

func foo int x:int y:short z:char

func foo x:int y:short z:char rtype int

func foo x:int y:short z:char returntype int

[hey, just found out you can preserve coloring and basic formatting when copying from OpenOffice into the WordPress text box. Neat trick.]

My current thinking is to use the last form, for a few reasons:

  • Return types are a new concept, as are statically typed parameters. While experienced C/Java programmers should have no trouble thinking of the return type as the type of the function and hence understand the foo:int syntax, I ultimately concluded it’s important to keep the concepts of return type and parameter type syntactically distinct.
  • I considered abbreviating Pygeon’s ‘parameters’ keyword to ‘params’, but because it’s assumed students have learned Pygeon before attempting PygeonC, it makes sense to just get rid of it at this point, as it’s unnecessary extra typing. Besides, I don’t like how it makes functions with parameters look too different from functions without; such non-uniformity makes it harder to identify functions when quickly scrolling past them.
  • On the other hand, return type, being a new concept, could stand to stand out, hence announcing it with a keyword and placing it at the end.
  • I’m thinking that ‘returntype’ is too much to type, but it may still be the best option. I considered ‘returns’, but that’s confusingly close to ‘return’, and I always find imitating English sentences in syntax to be misguided, not a virtue. Abbreviating to ‘rt’ is too cryptic. My concern with ‘rtype’ is that “ARR-TYPE” might enter into the learner’s vocabulary; I don’t like the idea of polluting the already confused lexicon of programming.
  • I’ve argued elsewhere that superfluous syntax is usually just confusing to learners, which suggests ‘returntype’ should be omitted, leaving the return type to just be inferred as the last type given. Like with having the ‘parameters’ keyword in Pygeon, though, ‘returntype’ may just help hammer the vocabulary term into learners’ heads. As long as it’s pointed out to students that the syntax could easily ditch ‘returntype’, I think this is a fair trade off.

(Note the type keywords are highlighted in a different color than the normal blue color. In fact, I’m thinking all type specifications should be highlighted uniformly whether they are built-in type keywords or not.)

Whatever the chosen function definition syntax, the prototype syntax is basically the same but with ‘proto’ in place of ‘func’ and with just parameter types, not names:

proto foo int short char returntype int

In fact, PygeonC doesn’t let you specify the parameter names in a prototype, for having optional syntax that doesn’t even do anything is frankly just confusing, and I consider it a core concept of C prototypes the fact that the compiler disregards any parameter names present. Besides, the utility of including parameter names in C prototypes is having self-documented code, something which is not really a concern for learners at this stage.

You might argue that prototypes could simply start with ‘func’, just like functions, but I think this is precisely the mistake C makes. Sure, function declaration and function definition are conceptually related, but giving them only subtly distinct syntaxes, as C does, makes them initially hard to discern and blurs the conceptual differences in the minds of learners. Generally speaking, subtlety is not a virtue for making things grokable.

The definition/declaration blur is another example of a misguided attempt at conceptual unity in C. I think what’s going on with such misguided conceptual unities is that designers spend their time juggling many parts in their head, mentally banging them together to see which parts fit with which and which overlay the others; the best part of designing comes when you have those ‘A ha!’ moments, where you see how parts you were thinking of as separate can be neatly overlayed, interlocked, or even dissolved into one, greatly simplifying the design. A small minority of the time, these revelatory moments really pan out just like they seem they will in that initial flash of recognition. However, most of these moments come to nothing when it later turns out, upon further reflection, that the idea doesn’t really make sense or fit consistently with the rest of the design. Other times, refactoring the rest of the design to fit the revelatory idea actually makes the whole more complicated. Other times, the idea can be accommodated just fine, but the gain is just an illusion: the designer, unhappy with some trade off, finds a solution that seems to dissolve that trade off, but euphoria blinds the designer to some side-effect introduced by his solution; on any other day, this new problem would displease the designer just as much as the problem he’s just solved, but he just isn’t thinking about it at the moment—maybe he’ll notice in a week or two.

The aspects of C that displease me so, the misguided attempts at conceptual unity, I actually believe these are partly examples of success: after all, the conceptual unities of C ‘work’ in the sense that (obviously) they comprise a real working language and the conceptual unities do in fact reduce the syntax (e.g. pointer and array declaration syntax mirrors the syntax for dereferencing and array indexing). However, these design choices also exhibit the designer ‘euphoria blindness’ I described: aspects of the design have been simplified, but only by incurring disregarded costs elsewhere. In this case, the costs are to language transparency. The design of a language is a kind of design where the transparency of the design is almost as important as its functionality, but the misguided conceptual unities in C are difficult to convey to outsiders because they really only make sense to people who already understand them. Having read many accounts of the C language, I’ve come to the conclusion that many of the traditional stories and vocabulary which C programmers use to talk about the language to each other simply fail to account for what is really going on in the language. Really, this is an unfortunate fact of any area of expertise: the experts are already cognizant of what’s really going on, so it’s fine for communication amongst themselves if their explanations and vocabulary abridge or misrepresent to untrained ears what they’re actually saying.

Anyway, on to function pointers. Let’s say we wish to declare a pointer named ‘bar’ for the function ‘foo’, above. Here’s the syntax I currently have in mind:

bar:[pfunc int short char returntype int]

(Notice the whole type declaration is in a single-color highlighting. Again, ‘pfunc’ and ‘returntype’ could be omitted, leaving the return type to be inferred as the last type specified in the brackets, but I think it’s best to leave those training wheels on.)

Any occurrence of [] always denotes a function pointer type. You can create derived types from function pointers using the usual scheme, e.g. if you wanted a 3-element array of pointers to a function pointer, it would look like so:

bar:3-p-[pfunc int short char returntype int]

Function pointer syntax requires start and end delimiters of course because you might need a pointer to a function that has a function pointer parameter:

bar:[pfunc int [pfunc char returntype p-char] returntype int]

(Above, ‘bar’ is a pointer to a function that takes a function pointer as its second argument.)

I’ve yet to read an introductory C book other than K&R that even mentions function pointers, and I think for good reason considering the crazy way C denotes them. Hopefully the above syntax can correct this.

UPDATE: Upon further consideration, ‘rt’ is the right choice. Also, ‘pfunc’ should just be omitted, as it’s enough to say that every pair of brackets is a function pointer signature. So now function definitions, prototypes, and function pointer declarations should look like, respectively:

func foo x:int y:short z:char rt int

proto foo int short char rt int

bar:[int short char rt int]

Another issue is casting. My first notion was to reuse the declaration syntax, like so:

foo:p-void // cast foo to pointer-to-a-void

…but then I realized this looks odd when applied to a parenthetical expression:

(bar a b):p-void

I also dislike how it gave colons two subtley related purposes. If casting is an operation, best treat it consistently with other operations and have a casting operator:

(cast p-void foo) // cast foo to pointer-to-a-void

What’s the matter with C?

6 Feb

First off, I’ve made a couple more decisions about Pygeon syntax:

  • The assignment operator is the keyword a (standing for ‘assign’), not the symbol = . So an assignment looks like (a foo 3) // assign 3 to variable ‘foo’ . The = symbol in math is not actually an operator, and so it is one part of mathematical notation that confuses the whole issue of expressions. It’s better to sidestep the whole distinction by never associating assignment, the making of equality, with what students are used to thinking of as the declaration of equality. Besides, assignment expressions already bring with them a confusing concept: assignment is a unique operator in that the target operand has to be a so-called ‘lvalue’ (‘left value’, a term coined in C), and in this sense the target operand doesn’t get evaluated as operands normally do.
  • The membership operator (the Pygeon equivalent of both . and [ ]) is the keyword m (standing for ‘member’), e.g. (m foo 3) // return the 4th index of ‘foo’ .

Continuing with the discussion of PygeonC syntax (see previous post):

The biggest pieces of C missing from Pygeon are syntaxes for variable declarations and structure definitions, and these happen to be areas where C could greatly stand to improve, for the sake of both learners and proficient users of C.

The worst decision made in the whole of C syntax is the way that the [ ] and * operators have opposite—but not precisely opposite—meanings in the context of a declaration as they do in an expression, e.g. in a declaration, [] makes something which is not an array into an array, while, in an expression, [ ] makes something which is an array into a constituent value of that array. Worse, the fact that * is overloaded to be both the binary multiplication operator and the unary * dereference operator is just confusing. To begin with, the unary/binary operator distinction is hard for most students to get used to because the only unary operators familiar to them are the – and + signs, which seem like special cases because their meaning as unary operators is logically related to their meaning as binary operators: students generally think of such signs as part of the number, not operators; this is not the case with unary * in C, which actually does something entirely differently from the binary * operator.

Quickly determining whether a * is a dereference or a multiplication is hard enough, but then * might also be indicating a pointer in a declaration, which raises another problem: learning to distinguish C’s declaration statements from expression statements is hard at first. The simple declaration statements—the ones beginning with one of the type keywords—are generally easy enough to quickly identify, but things get ugly in more realistic examples of C code, where typedefs, *, and [ ] are used frequently. Beyond just recognizing declarations, writing and parsing them is tricky: the base type can be modified with a panoply of keywords before or after the base type and the *, (), and [ ] operators using a totally unintuitive precedence. It’s in these complicated declarations that the decision to mix a prefix operator (*) with two postfix operators ( [ ] () ) is shown to be just egregious. In fact, I think it demonstrates that the whole idea of using the idiom of expressions in declarations makes absolutely no sense. I’ve read Dennis Ritchie justify this choice as an attempt at conceptual unity—complex types should be declared like how they are used—but this doesn’t really hold water: in a declaration, nothing is being evaluated. Instructors of C have long noted the trouble students have with C’s complex types, but I suspect the primary fault here lies with the strange syntax which obscures the underlying simplicity. (As much as I dislike this syntax, it severely annoys me how few C tutorials and books fail to emphasize—or point out at all, in some cases—that these are in fact operators with the base type as their operand. K&R is the only book I know that emphasizes this at all, which I think is one key reason it’s still so highly valued.)

So what does PygeonC do about declarations and the reference/dereference syntax?

  • & is replaced with ref (or maybe just r), and dereference * is replaced with dref (or maybe just d). E.g. (dref foo) is equivalent to C’s *foo. Verbose, surely, but clear and consistent with the established syntax.
  • There is no [ ] indexing operator as it is really just a convenience: x[3] is equivalent to *(x + 3). E.g. PygeonC would express x[3] as (dref (add x 3)). Again, this is verbose, but better to lay bare what is really going on. (On second thought, perhaps dref can optionally take a third operand to specify an offset before dereferencing, e.g. (dref x 3) .)
  • Rather than having signed and unsigned modifiers, there should simply be keywords for the unsigned variants, e.g. ‘ulong’, ‘ushort, ‘uint’, etc.
  • My current thinking for the declaration syntax looks like name:type . (I’m not sure about doing the reverse from C, putting the name before the type, but it just appeals to me at the moment.) Allocation modifiers go before the type separated by another colon, e.g. name:static:type .
  • An array modifier is simply the number of elements in the array. A pointer modifier is simply the letter p. The array and pointer modifiers go before the type, separated from it by hyphens, e.g. foo:static:3-p-p-int //foo is a 3-element array of pointers to pointers to ints . (Hyphens must separate all p’s and numbers; it would be perfectly understandable to omit hyphens between adjacent p’s, but for simplicity and consistency, it is not allowed.)
  • Notice that the declaration syntax makes a declaration all one token without whitespace. This means there is no separator necessary between the listed parameters in a function definition and it makes the casting syntax simpler. A cast is simply done by using the type as an operator, e.g. (p-int foo) // cast value of foo to a pointer to ints

(I haven’t even really discussed the problems with function pointer syntax; that will have to wait, as a better solution is not something I’ve cracked yet).

Here are some additional thoughts:

  • The membership operator is used just with structs, not dynamic associative arrays, so its argument is always known either to exist or not exist at compile-time. Because these names are always resolved at compile-time, the member is specified not as a string but as the name directly, e.g. (m foo bar), not (m foo “bar”). Using (m foo “bar”) would imply there is some string “bar” somewhere in the program text, but this is just a name used by the compiler.
  • It occurs to me that students should be made aware of an important conceptual difference difference between operators and functions in PygeonC that doesn’t exist in Pygeon. In C, some operators take a range of types for their operand(s) rather than just one and only one type; this is aside from the implicit casting of the core types, e.g. there is no implicit casting between integers and pointers, yet the + operator works with both integer and pointer operands. In other words, operators can be made to work with a range of types that you just can’t do with functions. Just as important, the type returned by an operator may vary whereas the type returned from a function never can.