3
$\begingroup$

In some C-based languages (including C/C++ themselves and Rust), taking pointers/references and dereferencing is done with prefix operators & and *. I've noticed that this often entails extraneous parentheses when the (de-)referenced expression is passed to a postfix operator, such as array subscript ([]) or attribute resolution (./::).

I believe using postfix operators for (de-)referencing could solve this issue (similar to Rust's postfix .await). However, simply reusing the same operators in a postfix form results in syntactic ambiguities. For instance, is a*-b multiplication or subtraction?

My question is, what other syntax do such operations have in other languages? Specifically, I'm interested in approaches that avoid ambiguity and, preferably, also preserve some familiarity to C programmers.

$\endgroup$
5
  • $\begingroup$ “What are some possible syntaxes?” questions are off-topic. You could narrow this question by making it explicitly about syntaxes used in existing, popular languages, though. $\endgroup$
    – Alexis King
    Commented Aug 13, 2023 at 20:49
  • $\begingroup$ @AlexisKing okay, thanks. Although it's already a bit more narrow, since I'm asking for specifically ones preserving C-likeness. But I'll edit regardless $\endgroup$
    – abel1502
    Commented Aug 13, 2023 at 20:53
  • $\begingroup$ Thanks! We’ve been trying to move away from open-ended language design questions on this site. See also Moving towards more focused language design questions. $\endgroup$
    – Alexis King
    Commented Aug 13, 2023 at 20:55
  • 1
    $\begingroup$ "this often entails extraneous parentheses" — a common trick in C is to remember that a[b] is *⁠(a+b), which is *⁠(b+a), which is b[a], so rather than (&x)[y] one can write y[&x]. $\endgroup$ Commented Aug 14, 2023 at 3:46
  • 1
    $\begingroup$ @RayButterworth -- you can, and then 90% of programmers reading your code will go "huh? y isn't an array..." $\endgroup$
    – occipita
    Commented Aug 15, 2023 at 17:40

4 Answers 4

4
$\begingroup$

A few more examples.

Pascal

program pointer_example;  
var i  : Integer;  
    ip : ^Integer;     (* Pointer to integer; ^ is prefix *)
begin
    i := 0;
    (* Neither ISO 7185 Standard Pascal nor ISO 10206 Extended
       Pascal describe a way to get a pointer to a variable, but
       this is a common extension. See comments below. *)
    ip := @i;          (* Get a pointer to an integer; @ is prefix *)
    ip^ := ip^ xor 1;  (* Dereference is a postfix operator. *)
                       (* Also note that ^ is not used for any other purpose. *)
end.

Fortran

program pointerExample
implicit none
   integer, pointer :: p1  ! p1 is a pointer to integer
   integer, target :: t1   ! t1 is an integer target

   ! You can only take the address of a variable that has the "target"
   ! attribute. Fortran has very strict aliasing rules, and this signals
   ! that the variable may be aliased.
   !
   ! This is kind of the opposite of the (now deprecated) "register"
   ! keyword in C, which is a signal that you cannot take the address
   ! of it, and it will never be the alias of another variable.

   t1 = 1
   p1=>t1                  ! p1 points to t1
   p1 = 2                  ! Fortran has no pointer arithmetic, so there
                           ! is no need for special "dereference" syntax.
end program pointerExample

BLISS

! BLISS is unusual in that the name of a variable denotes
! a pointer to it. If this sounds weird to you, consider that in
! C, the name of an array denotes a pointer to its first member.
!
! Note that BLISS only really has one "type": the machine word.
MODULE POINTERS (MAIN = CTRL) =
BEGIN
FORWARD ROUTINE
    TEST;
ROUTINE TEST =
    BEGIN
        LOCAL
            X,
            Y;
        ! This assigns 1 to the variable X.
        X = 1;
        ! This assigns the address of the variable X to the
        ! variable Y, with no explicit "reference" operator.
        Y = X;
        ! If you want to assign the value in X to the variable Y,
        ! you need to explicitly dereference X.
        X = .Y;
        ! Dereference on the lhs means assigning through a pointer.
        ! Store 2 in the memory location stored in Y.
        .Y = 2;
    END;
ELUDOM
$\endgroup$
2
  • 1
    $\begingroup$ Note that the @ operator is not standard Pascal, but a common extension. Some Pascal implementations either don't support the operation (only allowing pointers to come into existence through an invocation of new) or handle it differently (e.g. Solaris's Pascal compiler has a psuedo-function called addr that achieves the same thing). $\endgroup$
    – occipita
    Commented Aug 15, 2023 at 17:48
  • $\begingroup$ Thank you for that; I will edit accordingly. $\endgroup$
    – Pseudonym
    Commented Aug 16, 2023 at 0:58
3
$\begingroup$

This is almost certainly why the -> operator exists in C and C++, since a->b is merely syntactic sugar for (*a).b*.

C++ doesn't have an equivalent postfix for overloaded subscript or call, but you could easily create something like a->[index] in your own language. You could also allow the operator to be chained for more layers of indirection (so (**a).b == a->->b), although that may be pushing it.

Alternatively, you could make dereferencing itself postfix. The main downsides are deviation from the norm and potentially slightly more difficulty parsing (a* and a& look like the beginning of a multiplication and bitwise AND respectively, though a*., a&,, a&), a*;, and probably all other valid syntax using dereference are obviously not operations).

* ...except for this special case where a C++ class provides 2 semantically different overloads, but that kind of garbage is like #define true false

$\endgroup$
2
$\begingroup$

Rust has a postfix syntax for acquiring shared and mutable references ─ it's nothing special, though. You just call the .borrow() or .borrow_mut() method, as long as the Borrow or BorrowMut trait is in scope, respectively.

Example (playground link):

struct Foo;
fn accepts_shared_ref(_x: &Foo) {}
fn accepts_mut_ref(_x: &mut Foo) {}

fn main() {
    use std::borrow::{Borrow, BorrowMut};
    
    let mut x = Foo;
    accepts_shared_ref(x.borrow());
    accepts_mut_ref(x.borrow_mut());
}

For dereferencing, there are also the .deref() and .deref_mut() methods, but these don't have a blanket implementation for every type because that would violate the language's ownership model.

$\endgroup$
1
  • $\begingroup$ That's a good point, but I feel that references are significant enough in my language to deserve concise syntax. Rust still has &, and it's subject to the flaws I've described. I'd like to adress them from the start in my language, rather than through a retrofiited alternative in the standard library $\endgroup$
    – abel1502
    Commented Aug 13, 2023 at 21:35
2
$\begingroup$

Pascal uses postfix or ^ for dereference.

As I understand it, standard Pascal doesn't have an address operator (you can only form pointers to dynamically allocated objects). However some implementations provide this as an extension, using prefix @.

Confusingly, ISO Pascal apparently allows @ as an alternative token for , on an implementation-defined basis. If so then postfix @ would also be a dereference.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .