Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Can you elaborate?

My point is that the concepts "having a datatype, that stores an address" and "reading (typed) data stored at a specific address" are different, but share the same keyword (which is in this case just one symbol - maybe because C programmers have to use it often).

E.g. assuming C99's bool (0|1) datatype, you might define the "negate" functionality, which is often done by '!'. So we have something like that:

  // init a new bool
  bool success = 1;
  ...
  // negate the bool
  if (!success) {
    ...
  }
For me that makes sense. However if we take the pointer approach (as it is implemented), it would look like this:

  // init a new bool
  bool success = 1;
  ...
  // negate the bool
  if (bool success) {
    ...
  }
I hope this makes sense. For an experienced programmer * 's role is obvious from the context, but for me that was the most troubling concept when starting with C's pointers.


> My point is that the concepts "having a datatype, that stores an address" and "reading (typed) data stored at a specific address"

They are different things, but C's syntax was deliberately designed to imply the former from the latter. "Declaration reflects use" is the term they used for it.

The idea is that in something like:

    int *i;
You don't read it like, "Declare a variable 'i' whose type is 'int star', which is a pointer to an int". You read it like "declare variable 'i' whose type is such that taking 'star i' (i.e. doing a dereference) would give you an int". The type in this case is a pointer to an int.

This is always why function pointer syntax is so totally bizarre in C.

"Declaration reflects use" was a neat idea, but I think it practice it ended up causing more confusion than it solved. At the time, maybe they thought users would be tripped up by compound type expressions and thought it would help if they focused on the operations performed on the variable being declared.

In practice, it turns out that composed types don't seem to be that hard.


Wow this is really interesting, I did not know that, and it explains their choices. And C's function pointer types are indeed written in a bizarre way.

I definitely think of (int * i) as (i :: Ptr Integer) and (* i) in an expression like 42 + * i as (* :: Ptr Integer -> Integer).


Yeah, as long as we’re mixing notations, it’s really:

    *i :: Integer
From which you can conclude i :: Ptr Integer.

And the dereference operator does have the type you specified, but not in an lvalue—there the operator is really a mixfix one:

    (*_ = _) :: Ptr a -> a -> a
This isn’t specified directly by the standard, but follows from the rules about how an assignment operator must examine the structure of its first operand.


> In practice, it turns out that composed types don't seem to be that hard.

Depends on what you mean by hard. Hard to read or hard to reason about?

* -> * and * -> * -> * are easy to read and reason about, i.e. Int -> Char -> Int. But what about types of types?

(* -> * ) -> * is confusing to reason about IMO. i.e. Applicative Functors:

(<$>) :: Functor f => (a -> b) -> f a -> f b

Not really intuitive at all, the only reason I can understand it is that I know what functors and Monads are.


I'm not talking about higher-kinded types, just type annotations that are more than just a single bare identifier. Stuff like:

    List<int>
    (Some, Tuple, Type)
    Matrix[4][4]
    (Para, Meter) -> ReturnType


I'm probably misunderstanding you, but can I rephrase your comment as saying 'int i is the natural way of declaring a pointer to an int, and not int i'? Because if so, I don't recall that from the K&R (and I've read it front to back several times, not that that means much - I can't even remember if they use int* i or int *i, and I don't have my copy at hand here).


Alas, HN mangled your asterisks.

> I don't recall that from the K&R

I don't recall either, but this Wikipedia mention of "declaration reflects use" cites K&R:

https://en.wikipedia.org/wiki/C_%28programming_language%29#C...


Uh sorry, the first one had the asterisk next to the variable name, the other next to the type.

Yes I agree about the 'declaration reflects use'; what I meant was: does that imply that the declaration should consider the asterisk (the 'make this a pointer' part) to be 'part of', and thus right next to, the variable name or the type?

I'm strongly in the 'type' camp myself, and therefore I think that int *p; is nonsense; only to be used out of necessity when declaring multiple pointer variables on one line. So I'm wondering if 'declaration reflects use' reaffirms that, or contradicts it.


I don't see how the bool example follows; in what way is negating a boolean like dereferencing a pointer w/r/t types? ! is bool->bool; * is * P -> P.

The reason the same symbol is used is because of the way C types are read:

    int x; // (x) is an int
    int *x; // (*x) is an int
    int **x; // (**x) is an int
    int x(double); // (x(1.)) is an int


Yeah, sorry, my example sucks.

However, I am still believing a different operator would make more sense.


And what of [a-zA-Z0-9_]? Why should we allow them in both types and variables and functions? That's really confusing.


My elaboration is a bit late ... but.

    int i, *p;
Tells me that i is an int. And so is *p.


This had a way of always confusing me. For clarity, I'd declare them as:

  int i;
  int* p;


Think of it from the perspective of types:

    int *p; /* What is the type of p? Pointer to int */
    int a = *p; /* What is the type of *p? int */




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: