If you could change the C or C++ or Java syntax, what would you like different?

J

Joshua Maurice

[...]> However, nothing requires that size_t is a typedef name. However
again, the recommended practice according to the C standard is that
size_t is defined as a typedef, see 7.17 Common definitions
<stddef.h> / 4.

[...]

I've read 7.17p4 several times.  This is the second time you've
made this claim, and it just isn't true

(It's worth noting that that paragraph does not exist in the C99
standard; it was added by one of the Technical Corrigenda, and
appears in N1256, which is close enough.)

But that paragraph does not mention "typedef", nor does the word
"typedef" appear anywhere in 7.17.  (In fact, the *only* occurrences
of the word "typedef" in section 7 are in 7.18, (<stdint.h>),
and in 7.26.8, which refers to <stdint.h>.)

Here's 7.17p4:
    The types used for size_t and ptrdiff_t should not have an
    integer conversion rank greater than that of signed long int
    unless the implementation supports objects large enough to make
    this necessary.

That's just about *which* types should be used; it neither says or
implies anything about using typedef that wasn't already implied
by the previous paragraphs.

7.17p1 says "The following types ... are defined", and 6.7.7, which
describes typedef, is titled "Type definitions".  And if that's not
enough to imply the use of typedef, there is no other construct in
standard C that could define wchar_t, ptrdiff_t, and size_t in a way
that would satisfy the requirements.  (#define wouldn't do the job;
strictly conforming code may use those identifiers in inner scopes.)

Yes, the types "defined" in <stddef.h> are almost certainly typedefs.
No, paragraph 4 doesn't recommend that.

Ok. I can go with that.
 
M

Mark Wooding

August Karlstrom said:
Pascal, Modula-2, Oberon, Ada and Eiffel comes to mind.

Those are all related, which weakens the argument somewhat. Going
further afield, I can also think of Lisp, Smalltalk, Haskell, ML,
Prolog, and Tcl. Learn more languages: it's fun /and/ educational. ;-)

-- [mdw]
 
M

Mark Wooding

Ben Bacarisse said:
"Let x = y + 1" is often not a claim by the usual meaning of the word
(sometimes it's a claim -- let x = p/r for integer p and q might start a
proof by contradiction -- but it is not always a claim) but I really
don't want get into a fight about the definition of a very slippery
symbol.

The `=' sign denotes a (trivial) diagonal equivalence relation: for
every set S, =_S is { (x, x) | x in S }, but we drop the subscript
because it's not usually very interesting.

We often write something like `let xyz = pqr' as a shorthand for `let
the previously undefined variables in the expressions xyz and pqr be
such that xyz = pqr'. Often, discovering the necessary values is easy,
for example, when pqr is fully defined and xyz is a simple variable;
sometimes a nontrivial proof is necessary to show that a satisfying
assignment is possible.
Except that is not done. If it were the Kronecker delta function would
be obsolete.

If only! Iverson's bracket is much more useful. (\delta_{ij} means the
same as [i = j], only the latter doesn't squidge the two things together
in tiny subscripts with nothing between, it's readily expressible using
plain ASCII rather than TeX, and it easily extends to more elaborate
conditions.) (Oh, add APL to the list of languages not using `=' for
assignment.)

Iverson's bracket notation is, of course, an acknowledgement that we
don't, in mathematics, simply assign a numerical value to such an
assertion, and the bracket serves to assocate numerical values with
assertions. But, unlike Kronecker's delta (it's not actually a function
-- what's its domain?), it works for any kind of assertion,
substantially bridging the gap. I think the bracket's existence
suggests that there's value in assigning numeric values to assertions.
Yes. In fact I've agreed with this in the past. I was only commenting
on '==' which I am happy to have as distinct from the mathematical
symbol '='.

Mathematics doesn't /have/ an assignment operator. Colons are sometimes
used to make definitions: some authors write `x := z + 2' as a way of
defining the quantity x, but writing `x := x + 1' is meaningless (unless
something pathological happens: maybe left-cancellation fails, or the
ring is trivial).

As an aside, I usually use `==' when writing maths using ASCII to mean
`equivalent to', or `congruent to' -- the three-lined symbol denoted
\equiv in TeX.

I don't know where all of this leaves me on C's use of `=' for
assignment. I actually get very few bugs as a result of this syntax
choice, and I'm willing to accept that C is just its own notation,
similar to but different from other notations I'm used to -- so I guess
I don't really care much. I don't think that justifying C's notational
choice by comparing it with mathematical usage is especially convincing,
but C needed a convenient assignment operator and `=' doesn't seem to be
the worst possible choice. I am surprised that `<-' doesn't see more
use.

(Oh, add Icon to the list of languages using `:=' for assignment too.)

-- [mdw]
 
M

Mark Wooding

Joshua Maurice said:
Now you have me curious. In what other programming contexts does
"define a type" carry a different meaning besides "introduce a new
type to the compiler which is distinct from all other types in the
current 'translation unit' (quote unquote) - distinct according to the
type system and the type checker"?

Common Lisp immediately springs to mind as an example. (Common Lisp
allows explicit type declarations, and good compilers will check them
and emit warnings if they detect type errors.) The macro DEFTYPE allows
users to define new names for types -- and, indeed, to do more elaborate
things.

Some background may be useful. A Common Lisp type specifier is a symbol
or a list. The first thing in a type-specifier list is a symbol; the
remaining things are arguments. For example, the type (INTEGER -15 79)
is the type of all integers between -15 and 79 (inclusive). The type
(INTEGER 0 *) is the type of positive integers; and type (INTEGER * *)
-- or simply INTEGER -- is the type of all integers. (Common Lisp
doesn't impose a limit on the size of an integer, so it really is /all/
integers.)

The DEFTYPE macro lets you define a new type-specifier symbol together
with a rule, expressed in Lisp, for producing a new type specifier given
some arguments. It therefore doesn't let you say anything that you
couldn't say without DEFTYPE: it's just convenient.

What's interesting is that I'm not completely sure whether new, distinct
types are /ever/ introduced in Common Lisp. The forms DEFSTRUCT and
DEFCLASS seem to be able to do this sort of thing. Certainly, both
forms introduce new classes (a STRUCTURE-CLASS and a STANDARD-CLASS
respectively). Since classes can be used as argument specializers for
methods, you can easily write a simple generic function to distinguish
instances of your new class:

(defgeneric foop (thing)
:)method ((thing foo)) t)
:)method ((thing t)) nil))

One of the (scarier) type-specifier symbols is SATISFIES, which takes a
Lisp function name as an argument. We could therefore write

(deftype foo () '(satisfies foop))

in order to introduce our class FOO into the type system, if this wasn't
magically done for us already.

What's interesting is that we can define this type /before/ the class
FOO exists!

(defgeneric foop (thing)
:)method ((thing t)) nil))
(deftype foo () '(satisfies foop))

;;; ...

(defclass foo #| ... |#)
(defmethod foop ((thing foo)) t)

(This means, obviously, that Common Lisp's type system is unusually
fluid.) Clearly, before we add the extra method to FOOP, the type FOO
is uninhabited

-- [mdw]
 
M

Mark Wooding

Seebs said:
You raise an interesting point here. I think there may well be cases
like this in which typedef is used to simplify spelling something out,
rather than to create a logically distinct type. That said, I'm not
sure I've seen any code which uses such typedefs but only uses them
sometimes, such that it would be broken if a fully-spelled-out
declaration of baz() were incompatible with the bar_t * form.

I've seen many headers which defined convenient names for function
and/or pointer-to-function types, which are then used in further
function prototypes. To give a vaguely familiar example, I'm pretty
sure I've seen genuine system headers say something like

typedef int sighandler(int signo);
extern sighandler *signal(int signo, sighandler *handler);

(modulo lots of underscores). I think it'd be unfortunate if one had to
hack the arguments and return values of signal just to placate a nominal
typing discipline here.

My contribution to the quagmire: C provides some built-in types, gives
them names (often more than one), and provides some operators for
building more types (e.g., prefix `*', postfix `[...]'). (The syntax
for these operators is absurd, but that doesn't matter.) There's an
equivalence relation (`structural equivalence') on types which is
compatible with the operators: if T and U are equivalent, then T * and U
* are equivalent. There are some other operators, named `struct' and
`union', which build new types, but these aren't compatible with the
equivalence relation: indeed, they're very definitely generative. (For
the most part, this turns out to be useful rather than annoying.) All
that `typedef' does is attach a name to a type that you built using
existing names and operators. I think that `defining' is what you do to
when you give a meaning to a name, so `typedef' doesn't seem like a
completely silly description.

(There. I managed all of that without saying `declare' or `define'.)

-- [mdw]
 
K

Keith Thompson

Seebs said:
You raise an interesting point here. I think there may well be cases like
this in which typedef is used to simplify spelling something out, rather
than to create a logically distinct type. That said, I'm not sure I've
seen any code which uses such typedefs but only uses them sometimes, such
that it would be broken if a fully-spelled-out declaration of baz() were
incompatible with the bar_t * form.

But this does suggest a category of usage where it might be an issue;
conceivably, there could be code inside a library which uses the raw
declaration for which FILE is an alias, for instance.

typedef struct node {
int data;
struct node *next;
} node;

Code that uses this type would very likely break if typedef created
a new and incompatible type. (I wouldn't use the typedef myself,
but it's a common idiom.)
 
T

Tim Rentsch

Joshua Maurice said:
[snip]

My points have always been simple:

Firstly, "To define a type" has a well accepted meaning in any
programming language with a type system. That meaning is when a
programmer specifying the "definition" or "contents" of the type to
the compiler and/or type system checker. Every type is distinct, and
the type checker will tell you that (though what exactly that means
depends on the specifics of the type system and the type checker). In
C, this means that if "x = y" is valid code, then the type of x is the
same type as y, or there is an implicit conversion from the type of y
to the type of x. [snip]

This characterization of type systems is simplistic and naive. It's
not really accurate even for C, and it's not even approximately right
for Algol 68, Russell, the ML-based languages, Typed Smalltalk, or
Scala (all of which are staticly type checked), to name just a few.
 
J

Joshua Maurice

Joshua Maurice said:
My points have always been simple:
Firstly, "To define a type" has a well accepted meaning in any
programming language with a type system. That meaning is when a
programmer specifying the "definition" or "contents" of the type to
the compiler and/or type system checker. Every type is distinct, and
the type checker will tell you that (though what exactly that means
depends on the specifics of the type system and the type checker). In
C, this means that if "x = y" is valid code, then the type of x is the
same type as y, or there is an implicit conversion from the type of y
to the type of x.  [snip]

This characterization of type systems is simplistic and naive.  It's
not really accurate even for C, and it's not even approximately right
for Algol 68, Russell, the ML-based languages, Typed Smalltalk, or
Scala (all of which are staticly type checked), to name just a few.

I really don't mean to re-ignite this dead end conversation, but I am
genuinely curious as to specific claims. I admit that "to define a
type" is apparently controversial in this audience. I shall not pursue
that any more. However, how is anything else which I've said
ambiguous, wrong, simplistic, or naive? For C or for any other typed
programming language (which basically includes all non-trivial non-
assembly languages IIRC)?

I'm sorry that I'm just shooting it back at you, but it's hard to
respond to your claim without a more specific version of it.
 
T

Tim Rentsch

Joshua Maurice said:
Joshua Maurice said:
My points have always been simple:
Firstly, "To define a type" has a well accepted meaning in any
programming language with a type system. That meaning is when a
programmer specifying the "definition" or "contents" of the type to
the compiler and/or type system checker. Every type is distinct, and
the type checker will tell you that (though what exactly that means
depends on the specifics of the type system and the type checker). In
C, this means that if "x = y" is valid code, then the type of x is the
same type as y, or there is an implicit conversion from the type of y
to the type of x. [snip]

This characterization of type systems is simplistic and naive. It's
not really accurate even for C, and it's not even approximately right
for Algol 68, Russell, the ML-based languages, Typed Smalltalk, or
Scala (all of which are staticly type checked), to name just a few.

I really don't mean to re-ignite this dead end conversation, but I am
genuinely curious as to specific claims. I admit that "to define a
type" is apparently controversial in this audience. I shall not pursue
that any more. However, how is anything else which I've said
ambiguous, wrong, simplistic, or naive? For C or for any other typed
programming language (which basically includes all non-trivial non-
assembly languages IIRC)?

I'm sorry that I'm just shooting it back at you, but it's hard to
respond to your claim without a more specific version of it.

The rules of C don't exactly match the "same type" characterization.
For example, C has a similar notion, that of compatible type, but
being a compatible type is not an equivalence relation. I don't
say the "same type" rule is wrong for C, but it is a simplification
of C's actual rules.

For the other languages, probably the best thing would be to get
descriptions of these languages as done by the actual authors of the
various languages, and see how they describe what types are and what
static checking means in the different languages. Even the notion
of what a "type" is varies enormously between different languages.
 
J

Joshua Maurice

Joshua Maurice said:
[snip]
My points have always been simple:
Firstly, "To define a type" has a well accepted meaning in any
programming language with a type system. That meaning is when a
programmer specifying the "definition" or "contents" of the type to
the compiler and/or type system checker. Every type is distinct, and
the type checker will tell you that (though what exactly that means
depends on the specifics of the type system and the type checker). In
C, this means that if "x = y" is valid code, then the type of x is the
same type as y, or there is an implicit conversion from the type of y
to the type of x.  [snip]
This characterization of type systems is simplistic and naive.  It's
not really accurate even for C, and it's not even approximately right
for Algol 68, Russell, the ML-based languages, Typed Smalltalk, or
Scala (all of which are staticly type checked), to name just a few.
I really don't mean to re-ignite this dead end conversation, but I am
genuinely curious as to specific claims. I admit that "to define a
type" is apparently controversial in this audience. I shall not pursue
that any more. However, how is anything else which I've said
ambiguous, wrong, simplistic, or naive? For C or for any other typed
programming language (which basically includes all non-trivial non-
assembly languages IIRC)?
I'm sorry that I'm just shooting it back at you, but it's hard to
respond to your claim without a more specific version of it.

The rules of C don't exactly match the "same type" characterization.
For example, C has a similar notion, that of compatible type, but
being a compatible type is not an equivalence relation.  I don't
say the "same type" rule is wrong for C, but it is a simplification
of C's actual rules.

True. It is a simplification, but a relatively minor one. If I
understand the relevant rules correctly, they handle only some corner-
case pathological situations. I assume this is just legacy from the
days of a much "type weaker" K&R, something no average programmer
should worry himself about.

(And yes, I sense the irony that I'm speaking in a very similar way as
Seebs was. Let me apologize again for my bad attitude. [I still think
that he was wrong on when he used this same reasoning.] In this case,
I admit that he is right and that "compatible" types in C might not be
an equivalence relation. I'd need to review those pesky rules about
incomplete type declarations, ?composite types?, and such.)
For the other languages, probably the best thing would be to get
descriptions of these languages as done by the actual authors of the
various languages, and see how they describe what types are and what
static checking means in the different languages.  Even the notion
of what a "type" is varies enormously between different languages.

Of course. I think I was appropriately vague in my description of
that. I didn't even suggest static type checking was required for a
type system. I merely stated that C has static type checking. I was
quite good in that regard I believe.
 
B

Ben Bacarisse

Joshua Maurice said:
On Nov 2, 5:19 pm, Tim Rentsch <[email protected]> wrote:
The rules of C don't exactly match the "same type" characterization.
For example, C has a similar notion, that of compatible type, but
being a compatible type is not an equivalence relation.  I don't
say the "same type" rule is wrong for C, but it is a simplification
of C's actual rules.

True. It is a simplification, but a relatively minor one. If I
understand the relevant rules correctly, they handle only some corner-
case pathological situations. I assume this is just legacy from the
days of a much "type weaker" K&R, something no average programmer
should worry himself about.

(And yes, I sense the irony that I'm speaking in a very similar way as
Seebs was. Let me apologize again for my bad attitude. [I still think
that he was wrong on when he used this same reasoning.] In this case,
I admit that he is right and that "compatible" types in C might not be
an equivalence relation. I'd need to review those pesky rules about
incomplete type declarations, ?composite types?, and such.)

Did Seebs bring up the fact that compatibility is not an equivalence
relation? I thought it was Tim Rentsch (but I admit to not having paid
as much attention as I might have to this discussion).

Anyway, I can't see the connection with incomplete types or composite
types. Compatibility is reflexive by definition (as you have said) and
it is symmetric because the wording of all the rules is symmetric ("two
types are compatible if..." rather than "the first is compatible with
the second if..."). However it fails to be a transitive relation in a
few corner cases. The only one that comes to mind relates to the almost
universal compatibility of the types induced by old-style function
declarations:

#ifdef D1
void f1(a) long a; {}
#endif

#ifdef D2
void f1();
#endif

#ifdef D3
void f1(int);
#endif

This is constraint-free if D1 and D2 are simultaneously defined, and
also if D2 and D3 are defined, but a diagnostic is required if D1 and D3
are defined at the same time.

<snip>
 
I

Ike Naar

Anyway, I can't see the connection with incomplete types or composite
types. Compatibility is reflexive by definition (as you have said) and
it is symmetric because the wording of all the rules is symmetric ("two
types are compatible if..." rather than "the first is compatible with
the second if..."). However it fails to be a transitive relation in a
few corner cases. The only one that comes to mind relates to the almost
universal compatibility of the types induced by old-style function
declarations:

#ifdef D1
void f1(a) long a; {}
#endif

#ifdef D2
void f1();
#endif

#ifdef D3
void f1(int);
#endif

This is constraint-free if D1 and D2 are simultaneously defined, and
also if D2 and D3 are defined, but a diagnostic is required if D1 and D3
are defined at the same time.

Perhaps a simpler case:
(long*) is compatible with (void*), (void*) is compatible with (double*),
but (long*) is incompatible with (double*).
 
B

Ben Bacarisse

Ike Naar said:
Perhaps a simpler case:
(long*) is compatible with (void*), (void*) is compatible with (double*),
but (long*) is incompatible with (double*).

None of these are pair-wise compatible. Are you confusing compatible
with "assignable" (not a term in the standard but one that would be a
very handy to have)?
 
J

Joshua Maurice

Joshua Maurice said:
On Nov 2, 5:19 pm, Tim Rentsch <[email protected]> wrote:
True. It is a simplification, but a relatively minor one. If I
understand the relevant rules correctly, they handle only some corner-
case pathological situations. I assume this is just legacy from the
days of a much "type weaker" K&R, something no average programmer
should worry himself about.
(And yes, I sense the irony that I'm speaking in a very similar way as
Seebs was. Let me apologize again for my bad attitude. [I still think
that he was wrong on when he used this same reasoning.] In this case,
I admit that he is right and that "compatible" types in C might not be
an equivalence relation. I'd need to review those pesky rules about
incomplete type declarations, ?composite types?, and such.)

Did Seebs bring up the fact that compatibility is not an equivalence
relation?  I thought it was Tim Rentsch (but I admit to not having paid
as much attention as I might have to this discussion).

Sorry, ambiguous "he" pronoun. Tim Rentsch is correct that C's
compatible type relation is not an equivalence relation. I admit that
I did a simplification in a similar argument to that of Seebs.
Anyway, I can't see the connection with incomplete types or composite
types.  Compatibility is reflexive by definition (as you have said) and
it is symmetric because the wording of all the rules is symmetric ("two
types are compatible if..." rather than "the first is compatible with
the second if...").  However it fails to be a transitive relation in a
few corner cases.  The only one that comes to mind relates to the almost
universal compatibility of the types induced by old-style function
declarations:

  #ifdef D1
  void f1(a) long a; {}
  #endif

  #ifdef D2
  void f1();
  #endif

  #ifdef D3
  void f1(int);
  #endif

This is constraint-free if D1 and D2 are simultaneously defined, and
also if D2 and D3 are defined, but a diagnostic is required if D1 and D3
are defined at the same time.

Yes. This is actually an excellent example of a non-pathological case.
When I brought up composite types, I was referring to
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
6.2.7 Compatible type and composite type
Specifically to:
6.2.7 Compatible type and composite type / 5
5 EXAMPLE Given the following two file scope declarations:
int f(int (*)(), double (*)[3]);
int f(int (*)(char *), double (*)[]);
The resulting composite type for the function is:
int f(int (*)(char *), double (*)[3]);

I'm not sure offhand if this peculiarity is restricted to only
function declarations, but I think the above is clearly pathological,
whereas your example of:
void f1();
void f1(int);
is not.
 
B

Ben Bacarisse

Joshua Maurice said:
On Nov 3, 6:04 am, Ben Bacarisse <[email protected]> wrote:

Yes. This is actually an excellent example of a non-pathological case.

I'd call it pathological -- at least it seems very unlikely to come up
in practise. I don't think I've ever seen an example of the
non-transitivity of type compatibility in a real program.
When I brought up composite types, I was referring to
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
6.2.7 Compatible type and composite type
Specifically to:
6.2.7 Compatible type and composite type / 5
5 EXAMPLE Given the following two file scope declarations:
int f(int (*)(), double (*)[3]);
int f(int (*)(char *), double (*)[]);
The resulting composite type for the function is:
int f(int (*)(char *), double (*)[3]);

I'm not sure offhand if this peculiarity is restricted to only
function declarations, but I think the above is clearly pathological,
whereas your example of:
void f1();
void f1(int);
is not.

It's a simpler example of forming the composite type but the standard
would be poorer if all the examples were simple ones!

The notion of composite type is only used in two places: to determine
the final type that results from more than one (compatible) declaration
being in scope at once, and for determining the type of some conditional
expressions. You could program in C for a lifetime without ever having
to worry about it
 
T

Thomas G. Marshall

I don't like the way people start flame wars about something that doesn't
amount to a hill of beans in this world.

[...snip...]

Why the ire over this?

1. Even if this *were* a flame war, a flame war is as easy to side
step as it is any other discussion you have no interest in. Pretend
it says "basket weaving" and don't click on it. It'll look like a
single line and you can relax.

2. It's a good thread basis. As a software engineer since the 80's I
have always in that time found discussions regarding what people view
to be mistakes in a language to be very informative. You often get a
collection of the pitfalls of disparate language implementations. A
combination of seeing what works and what doesn't work for others and
why. Plus, the arguments *against* any particular complaint is often
very valuable as well.
 
T

Tim Rentsch

Joshua Maurice said:
Joshua Maurice said:
My points have always been simple:
Firstly, "To define a type" has a well accepted meaning in any
programming language with a type system. That meaning is when a
programmer specifying the "definition" or "contents" of the type to
the compiler and/or type system checker. Every type is distinct, and
the type checker will tell you that (though what exactly that means
depends on the specifics of the type system and the type checker). In
C, this means that if "x = y" is valid code, then the type of x is the
same type as y, or there is an implicit conversion from the type of y
to the type of x. [snip]
This characterization of type systems is simplistic and naive. It's
not really accurate even for C, and it's not even approximately right
for Algol 68, Russell, the ML-based languages, Typed Smalltalk, or
Scala (all of which are staticly type checked), to name just a few.
I really don't mean to re-ignite this dead end conversation, but I am
genuinely curious as to specific claims. I admit that "to define a
type" is apparently controversial in this audience. I shall not pursue
that any more. However, how is anything else which I've said
ambiguous, wrong, simplistic, or naive? For C or for any other typed
programming language (which basically includes all non-trivial non-
assembly languages IIRC)?
[snip]

For the other languages, probably the best thing would be to get
descriptions of these languages as done by the actual authors of the
various languages, and see how they describe what types are and what
static checking means in the different languages. Even the notion
of what a "type" is varies enormously between different languages.

Of course. I think I was appropriately vague in my description of
that.

This comment seems incongruous with the paragraph above starting
"Firstly". Do you mean to make a definite statement, or only an
approximate one?
I didn't even suggest static type checking was required for a
type system. I merely stated that C has static type checking. I was
quite good in that regard I believe.

My impression all along has been that the discussion has been
directed towards staticly checked languages. All of my comments
have been about languages that are staticly checked.
 
T

Tim Rentsch

Richard said:
There is nothing at all "tongue in cheek" about

'to a generally acknowledged clc "guru-in-residence"'

As it was my comment, I can assure you with absolute confidence
that the author intended it to be tongue-in-cheek, and that it
be taken that way.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,079
Messages
2,570,575
Members
47,207
Latest member
HelenaCani

Latest Threads

Top