views:

39

answers:

6

I'm designing a syntax for templates/generics. The C++ family languages use angle brackets for this, but I'm looking at using a separator character instead. For example, where in Java you might write:

Map<String, Foo> foos = new HashMap<String, Foo>();

I'm aiming for:

.foos = hmap*string*foo

I'm trying to decide what the separator character will be; it can't literally be *, because that's already in use for multiplication (not that I couldn't overload it, but the precedence would be wrong). Looking at the available characters, some options are

.foos = hmap!string!foo
.foos = hmap$string$foo
.foos = hmap?string?foo
.foos = hmap^string^foo
.foos = hmap`string`foo
.foos = hmap|string|foo
.foos = hmap~string~foo

As this is to some extent an aesthetic question, I figured I'd take a poll: which option do you like best? Is there another option that would be preferable? (As usual with questions like this, if your answer has already been posted, please upvote rather than duplicate.)

Also, on a standard US keyboard, ` is unshifted, each of the others requires the shift key. Is there a difference on international keyboards? Are there any keyboard layouts on which any of the above candidates are particularly easy or difficult to type?

(As this is a question without a single right answer, should it be a community wiki?)

A: 

Angle brackets work better for me than your options, as they create a visual separation of the elements between them from other code.

I would try something similar with {} or (), if they are not already used.

Oded
+1  A: 

I don't think any of these symbols does really make the point clear that you apply a list of type arguments to a generic type.

Why not just some kind of bracket syntax like in most common languages, either

Map<String, Foo>

or

Map[String, Foo]

(or {}/() respectively)

Note that - like in OCaml or Haskell - whitespace can also be used quite comprehensively.

foos : int list

or

foos :: Map String Foo

Some wider theory: A simple generic type is a type with kind * -> * - Therefore it has to be seen as a function from types to types

List<Int> applies the generic type constructor List<α> on the concrete type Int, which yields another concrete type - The specialized list.

I therefore like generic specialization to somehow resemble a function application.

The use of * can make sense as it tuples types! So Map String*Foo could actually be a good option if you want to rely on operator syntax, as it distinguishes between type constructor and (tupled) type arguments.

Dario
I also think of generic specialization as being similar to function application. As in some functional languages, I'm using juxtaposition for function parameters, so f(a, b) just gets written as f a b. If I go with something like hmap!string!foo for generic specialization, I like to think of that as a variant of function application where the space is just replaced with a different character to disambiguate.
rwallace
@rwallace: Well, then `$` could be somewhat appropriate ;) But this syntax necessarily implies type currying which may be somewhat couterintuitive in a non-functional language. Therefore the tuple-approach or simple brackets are possibly better (just my opinion).
Dario
Note that any of these symbols make the single types hard to distinguish from a simply optical point of view. Whitespace or at least a symbol of much different size like ` or * are a must!
Dario
That's a good point, but I think I do want type currying. It seems to me arrays are most consistently represented as generics, so for example array!float is an array of floating point numbers (any length) and array!float!100 is an array of 100 floating point numbers.
rwallace
So are you saying $ ^ or ~ would be better than ! due to being more visually distinct?
rwallace
Yep - I definiely consider them to be better (but still not good ;)).
Dario
A: 

I think

.foos = hmap(string, foo)

is a nice syntax: allowing types to be used as "functions" which return new types.

Pindatjuh
A: 

I'm not too keen on any of them, for the reasons other people have mentioned. Brackets are just useful to denote this kind of thing, regardless of whether it is < { [ ( etc. There's 4 to choose from there.

Some specific points on the options you listed:

    .foos = hmap!string!foo - could be confused with negation.
    .foos = hmap$string$foo 
    .foos = hmap?string?foo - just doesn't look very definitive, the question mark makes me think you are saying it may be a string
    .foos = hmap^string^foo 
    .foos = hmap`string`foo - tricky to find the key!
    .foos = hmap|string|foo - could be confused with an 'or' operation
    .foos = hmap~string~foo 
Fiona Holder
The brackets are all thoroughly spoken for, alas. But you raise some good points. Okay, ? is out; and ` might be unadvisable, tempting though the unshifted symbol is. At least I'm using a keyword for not, so ! is unambiguous.
rwallace
It may be unambiguous in your langauge yes, but you have to remember that people reading the code have to be able to get a vague idea of what is going on without knowing all the finer details of the syntax. If you use a symbol that commonly represents something else in most languages, you have to be careful that its meaning is clear.
Fiona Holder
True. That's another argument for using $, which has been used elsewhere to connect parts of a compound name, an at least vaguely related usage.
rwallace
A: 

I don't like an infix operator for this in general because it raises questions about associativity.

For example is hmap*string*foo an hmap<string, foo> or an hmap<string<foo>> or maybe a foo<string<hmap>>?

ChrisH
You raise a good point about associativity. I think the answer is that it's left associative and automatically curried - see my reply to Dario about type currying.
rwallace
A: 

Maybe you should use the "of" keyword:

map = HashMap of String, Foo()

In Boo it would look like this:

map = HashMap[of string, Foo]()
justin.m.chase