The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Code clarity question

Hi -

If you saw a C style declaration like

int foo (int bar)

it's pretty clear what's going on, but if you saw a variant style declaration like:

int foo (int:(0-3) bar)

is it clear that bar is limited to the range of 0 to 3?
Are there better ways to declare this that come to mind?  Like:

int foo (int bar:(0-3))

Is that better or worse?  Does the presence of the colon help or hinder?

(I know very little about Contracts - design by, or design with - but I suspect this is similar.)
Aaron F Stanton Send private email
Monday, November 22, 2004

Why don't you just *tell* everyone that you're writing your own language and compiler, and you want everyone's best ideas on everything.

It's all about grammar...

I guess int:(0-3) is going to be a subtype of 'int', right? So that means that "int:(0-3)" will be parsed as a type-expression in your language, hence I'd be inclined to keep it all together, rather than inserting a declaration name "bar" in the middle of it all, which, since you haven't asked, is something I've wanted to "fix" in C for a while.

So, how about sorting out the whole damn thing:

int foo (int bar:(0-3))

becoming the highly controversial:

foo: ( bar:int(0-3) ) -> int

...where identifier-names are consistently stated to the LEFT of the type-expression, and I've moved the return type-expression to the right-hand end of the function type, just like where my old university supervisor liked it to be in the fictitious languages he used to devise.

For that matter, let's allow multiple return values. You can do it in C++ but you're forced to express the return value as a struct or class first:

foo: ( bar:int(0-3) ) -> (int, string)

The "brackety expressions" (int, string) effectively introduce an anonymous struct on-the-fly. They could be type-compatible based on their shape, too (since they don't have names). So how about allowing it more generally wherever type expressions are expected?

MyFunction: (a:int, b:int) -> (int, string)
  var MyLocalVariable: (int, string);

Maybe these could be useful as a shorthand syntax for situations where the programmer doesn't want to introduce a named struct just for a short piece.

Boy am I gonna get flamed for this, but then I challence the status quo occasionally.

Sorry, you were talking about ranges...
Jonathan Send private email
Monday, November 22, 2004
personally i think (0-3) looks ambiguous.  i'd much rather see something like (0:3) or (0,3).  but maybe i'm crazy.
Glen Rosenblatt Send private email
Monday, November 22, 2004
"personally i think (0-3) looks ambiguous."

Indeed.  Also:

-3 to 3 is (-3-3)  ?
3 to -3 is (3--3)  ?
-6 to -3 is (-6--3) ?

These constructs are pretty ugly.
Almost Anonymous Send private email
Monday, November 22, 2004
"int foo (int:(0-3) bar)"


"int foo (int bar:(0-3))"


But I would prefer

"int foo(int bar (0 to 3))"

or perhaps even

"int foo(int (0 to 3) bar)"

But then I'm a VB programmer.  :)  Maybe brackets or braces instead of parens, though:

"int foo(int [0 to 3] bar)"


"int foo(int {0 to 3} bar)"

I think the brackets look better.
Kyralessa Send private email
Monday, November 22, 2004
How about:

ranged_int<0, 3>

This says exactly what you want. This is actually easy to do in C++ and even easier in Heron. I don't know about Java 1.5.
Christopher Diggins Send private email
Monday, November 22, 2004
They're all too confusing.  This is much better:

int foo(int {0,1,2,3} bar) {

That way, you can do away with unsigned ints, too:

int foo(int {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, blah blah blah, 4294967295} bar) {

You won't have to hassle with shorts, chars, or whatever.  You'll have one data type, and it's a number.

Just wait till you see strings!
Art Send private email
Monday, November 22, 2004
After the variable name, whether with a colon, with a dash or ... or something else. The colon is good, actually, it says "hey, there's something unusual here, Mr Programmer, look!"

Tuesday, November 23, 2004
Regarding how to express the range, it might be useful to consider what happens when you want to show a range of floating point numbers. For example, if bar is a double greater than 2.0 and less than or equal to 5.0, do you really want to write something like this:

double(2.0 + epsilon, 5.0)

or would you rather write something like this:

double(2.0, 5.0]

The first is sprawling, while the second is somewhat unclear, and so they are both annoying in their own way. But what do you do if you need different kinds of restrictions:

fact(bar int:[0, INF))

You're going to need a convenient way to express infinity. Another special case that could come up is passing an argument "n" that must be a power of two. I don't know what you'd do with your current syntax. You'd need something more general.

You'd have to add in a lot of syntax to handle all those sorts of restrictions, so either you are stuck with a weak solution, lots of extra syntax, or you can use a general solution that already exists (like the template solution mentioned earlier, or just documenting it and returning an error code or throwing an exception if the argument is invalid).
Tuesday, November 23, 2004
@Art: Your suggestion will be highly problematic with floating values ;-)

@Aaron: For you are designing your own language and mentioned Design by Contract. Did you take a look at the "D" programming language? They have a neat concept for contract based programming:

Second, I wonder if you have a concept for overloading functions like virtual void foo (int(0:3) bar)? Will virtual void foo(int(-3:3) bar) be valid?
Gerd Riesselmann
Tuesday, November 23, 2004
I want to clarify my comment so that I don't come off too negatively. A weak solution is fine so long as it's enough most of the time, and a bunch of syntax is fine as long as it gets enough use to justify its existence, both IMO.
Tuesday, November 23, 2004
Jonathan -

I haven't done much more than begin the process of finding out what works, what doesn't, comparing and contrasting language styles.  It hardly counts as designing a new language at that point, but it is the long term goal.  I want something readable and maintainable, much more user friendly than compiler friendly.  That said, the variations you suggest are interesting.

Glen and Almost -

Point taken.  The colon or comma for a range is indeed much better than a hyphen.

Kyralessa -

From a VB perspective, not a bad thing.  I like the brackets better than the curly brackets, too.  Maybe for the next language.  ;)

Christopher -

That might be a possibility.  I do plan on including generics, so maybe that's how it will get done.

Art -

Be funny or be useful.  That was neither.  (Strings will be handled by regex's, thank you very much.)

masharpe -

I hadn't gotten to considering floats yet, nor the issues they entail.  It does indeed complicate matters a bit.  More thought will be needed.  The bit about "must be powers of two" is one that could come up, but to handle it properly it would just be better to introduce named validation functions, which could be done but I think complicates things quite a bit.  (Though it does introduce some nice elements of reusability I hadn't considered until now.)

Gerd -

The function overloading for what you describe is something I actually had thought of and hadn't decided on just yet.
Aaron F Stanton Send private email
Tuesday, November 23, 2004
> You're going to need a convenient way to express infinity

You can't do that in an int. Or a long. Or a...

Wednesday, November 24, 2004
How is int:(1-3) related to int? Can you cast from int:(1-3) to int? How about vice-versa? If you can cast, what happens if it's out of range? Is addition modulo? If it isn't, what happens when you add 2 and 2?
Mr Jack
Wednesday, November 24, 2004
I actually wasn't originally thinking of this in terms of a subtype, rather as a simple validation function - an assertion that throws some sort of exception if it falls outside the required range.  I realize you could have very sophisticated validation if you use an explicitly named function, but I wanted a design that was pretty streamlined.  The simplest case is that most often you want a number to be in a given range.  The analogous case for strings is to use a regex.  Anything more complicated than that, well, there's no good way that I can see to inject that into a function declaration.
Aaron F Stanton Send private email
Wednesday, November 24, 2004
Ah, right. I see. Sorry.
Mr Jack
Wednesday, November 24, 2004
Not a problem.  I actually have been thinking about declaring variables with a required range, and throw an exception if the variable ever goes outside that range, but the code needed to do that would slow things down a lot.  I think just doing validation when a function is called should suffice.

Might also do it on the return value, too.  Something like:

int:(0,3) foo (int:(0,3) bar)

could be an option.  Not sure yet.
Aaron F Stanton Send private email
Wednesday, November 24, 2004
"and throw an exception if the variable ever goes outside that range, but the code needed to do that would slow things down a lot"

Except, of course, if you knew you didn't have to check it. Maybe you can infer that the check would never raise a failure at run-time.

This is another example of the array-index boundary checking problem, which is particularly of interest to the likes of .NET languages and JAVA, but might be hard to do to good effect. I'd be interested to hear comments on this general theme.

I'm gonna be controversial here, and suggest that the motivation for C#'s "foreach" had less to do with giving programmers a whizzy new "for" loop statement, and more to do with allowing iteration over arrays with the efficiency of C without endangering C#'s "safety" semantics and without Microsoft's engineers having to use too many brain cells to do this. ;)


Well, I like to be controversial once in a while.
Jonathan Send private email
Wednesday, November 24, 2004
> > You're going to need a convenient way to express infinity

> You can't do that in an int. Or a long. Or a...

Er, yeah, when I look back on that it was a stupid thing to say. What I really meant is that there needs to be a way to have it unbounded in one direction, but it makes more sense to do something like:



> I actually wasn't originally thinking of this in terms of a
> subtype, rather as a simple validation function - an
> assertion that throws some sort of exception if it falls
> outside the required range.

You could maybe allow an optional syntax for using any expression:

func(b:int where is_prime(b))

With that syntax,


would be a short form of

func(b:int where b>=0 && b<=3)

That way callers could see what they have to pass by just looking at the signature. It also allows easy checking of pointers:

func(p:int* where p!=NULL)

"where" is ugly and I think I'm messing up the syntax, but hopefully I'm communicating the idea.
Sunday, November 28, 2004
I dont think

int foo(int {0,1,2,3} bar) {

will work since you have have float implementation.


int(limit -40 to 43) foo=33;  which limit to the value if outside the range.


int (limit -40 to -10, -1 to 5, 50 to 200) foo = 45;  where the 66 is outside the range, hence the nearest is the 50 and therefore foo=50.

int(range -40 to 43) foo;  which display out of range error message, useful for diagnostic. Perhap, this may be optionally disabled via complier configuration.

float(limit -30.334 to 43.889) foo;

I don't know about the string, it much too diverse and better to use dedictaed method to deal with it.


Apress:- Beginning C# Object is bloody good book for OOP course!
Riscy Send private email
Sunday, November 28, 2004

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
Powered by FogBugz