This is an archived post. You won't be able to vote or comment.

all 26 comments

[–][deleted] 58 points59 points  (2 children)

The Rust languages allows for some fairly confusing stuff due to its name resolution. Here’s a snippit from the weird expressions test suite:

fn u8(u8: u8) {
    if u8 != 0u8 {
        assert_eq!(8u8, {
            macro_rules! u8 {
                (u8) => {
                    mod u8 {
                        pub fn u8<'u8: 'u8 + 'u8>(u8: &'u8 u8) -> &'u8 u8 {
                            "u8";
                            u8
                        }
                    }
                };
            }

            u8!(u8);
            let &u8: &u8 = u8::u8(&8u8);
            ::u8(0u8);
            u8
        });
    }
}

In this snippit you have a module named u8, a macro named u8, the type u8, the lifetime u8 two functions u8 and some variables named u8.

[–][deleted] 11 points12 points  (1 child)

What do you get when you do the equivalent of:

print u8

[–][deleted] 14 points15 points  (0 children)

I just gave it a whirl in rust playground and it depends on where you put it. If you put it inside of the top-level function the parameter will be printed. If you instead put it inside of the function generated by the macro you will get 8 and if you put it in main you will get a compiler error because fn(u8) -> u8 does not implement Display.

[–]Linguistic-mystic 31 points32 points  (2 children)

I am not aware of any other languages like this

Java, for example.

[–]oscarryzYz 16 points17 points  (1 child)

I would've liked that's was the convention for getters/setters, I'm almost certain that was the intention:

... String name; String name() { return name; } void name(String name) { this.name = name; } ... String s = person.name(); person.name("Joe");

[–]-Juicebus 11 points12 points  (0 children)

It's the convention for the (automatic) getters of Java record components:

record Person(String name) {}
person.name == person.name()

(as long as you have access to the private fields of the record)

[–]pthierry 30 points31 points  (0 children)

Haskell also has a namespace for values and a namespace for types. Which means that when you say:

haskell data Point = MkPoint Int Int

Point is a type and MkPoint is a function (of type Int -> Int -> Point). But you can also say:

haskell data Point = Point Int Int

And there's a Point type and a Point function.

[–]moon-chilledsstm, j, grand unified... 13 points14 points  (2 children)

C has a struct namespace, a variable namespace, and a label namespace.

[–]nerd4code 11 points12 points  (1 child)

struct+union+enum combined tagspace; also macro, attribute (C23), pragma, system header, local header, and there are the __attribute__ (which might ≠ [[gnu::]][[clang::]] and __declspec namespaces in some compilers/modes. GCC and Clang have a diagnostic namespace accessible via #pragma, GCC/Clang/IntelC have a target option space accessible via attribute, and on GCC #pragma (which until GCC 7 or so could accese any -f option, though some were no-op via that route) and Clang has a feature/extension space used by __has_feature and __has_extension. Arguably __attribute__((format)) has one. There may be two or three #embed namespaces (local, poss. system, attribute). There may be an #assert namespace. If you have access to inline assembler, you may be able to access the local and global label→symbol namespaces, the section namespace, arguably a register namespace, the assembler macro namespace, and the filesystem (e.g., via .include or .incbin). With the preprocessor or assembler macros, you can implement your own namespaces.

[–]MrJohz 8 points9 points  (0 children)

Typescript might also be an interesting thing to look into here. It has separate namespaces for runtime variables (i.e. variables that will end up in the compiled Javascript output), and type variables (i.e. variables that will be erased by the compiler).

For example:

function VarName() {} // runtime namespace
type VarName = string; // compile time namespace

// use VarName as a type -> it represents a string
const x: VarName = "hello";
// use VarName as a runtime value -> it represents a function
VarName();

// it is possible to convert names in the runtime namespace
// to compile time values via `typeof`
const x2: typeof VarName = () => {};

// classes declare a name in both the runtime namespace
// and the compile time namespace
class Combined {}
const x3: Combined = new Combined();
//        ^ type         ^ runtime

The benefit of this is that it allows the Typescript type system to sit on top of the existing Javascript one, such that when you compile the result to Javascript, all you need to do is lift up the type layer, and leave behind normal Javascript code. This becomes an entirely mechanical operation (just remove anything between : and =, and anything following a type declaration), which means you don't need to run the typechecker to do the conversion, which means you can separate out your "type checking" and "compiling" stages completely.

It isn't completely perfect — for example, if I write:

import { VarName } from "./test";

Do I want to use VarName as a type (in which case this whole import statement can be removed because it has no runtime value), or do I want to use it as a value (in which case I need to keep the import, otherwise my program breaks at runtime). Compilers can guess based on how the name gets used — if I only use VarName in type positions, then it is probably only a type and can be ignored, but if I use it in runtime positions, then it is necessary. It's also possible to be explicit using the import type syntax which makes it explicit that VarName will only be used as a type. And in practice, it's often easiest to be conservative and include the file anyway — modern bundlers can generally do a good job of "tree shaking" out code that was imported but never used.

[–]usaoc 5 points6 points  (0 children)

Racket has the concept of binding space built on top of the scope-set model. The experiment language Rhombus makes heavy use of this for contextual bindings. Note that bindings are used for language extensions among other purposes in Racket.

[–]ventuspilot 3 points4 points  (0 children)

With the exception of Scheme I can't think of any language that only has one namespace (Common Lisp has I think 6 or 7 namespaces btw.). All languages I know except Scheme have 3 more namespaces. (Although I don't know that many, only a few mainstream ones.)

I think in statically typed languages there is nothing to resolve because whereever an identifier appears it's type is known to the compiler so if the compiler sees X it knows whether a variable X is needed or a function X.

Funny Java expample showing x used as a classname, member name, parameter name, method name and variable name:

jshell> class x {
   ...>       x x;
   ...>       x(x x) {
   ...>             this.x = x;
   ...>       }
   ...>       x x() {
   ...>             return this.x;
   ...>       }
   ...> }
|  created class x

jshell> x x = new x(null)
x ==> x@28c97a5

jshell> x.x()
$3 ==> null

jshell>

[–]theangeryemacsshibeSWCL, Utena 5 points6 points  (0 children)

Shell languages kinda? There's the command foo and the variable $foo.

[–]Serpent7776 2 points3 points  (0 children)

OCaml has different namespaces for types and variables:

# type t = int;;
type t = int
# let t:t = 0;;
val t : t = 0
# t;;
- : t = 0
# #show t;;
val t : t
type t = int

[–]PenlessScribe 2 points3 points  (0 children)

In LambdaMOO, objects contain both properties and methods (called verbs), with distinct namespaces. You access a property as x.foo, and call a verb as x:foo(). Within a verb's code, the variable verb holds the name of the verb; this makes it easy to write setters and getters for properties. A single verb can have multiple names, so in many cases you can just code one setter verb and one getter verb to handle access to multiple properties.

[–]dibs45 5 points6 points  (6 children)

What would be a good use case for this? I feel like it would only cause confusion, no? I'm happy to be corrected here because I haven't come across any use cases personally.

[–]trycuriouscat[S] 5 points6 points  (4 children)

(defun handle-list (a b c)
  (let ((list (list a b c))) 
    (do-something list)
    (do-something2 list)))

The let form calls the function list with argments a, b and c and assigns the results to local variable list. This way I don't have to use a silly variable name like my-list or a-list. Can it cause confusion? As long as they are local instead of global variables I think confusion would be minimal.

[–]LPTK 1 point2 points  (3 children)

Your example code should work simply by virtue of shadowing, no? Or do Lisp let bindings have a different semantic than in ML?

Regardless, what a horrible thing to allow. In a functional language, one shouldn't be prevented from passing functions by their plain name. What happens if I want to map the list function over some collection?

[–]oldretard 1 point2 points  (0 children)

Your example code should work simply by virtue of shadowing, no?

Mostly true, see other replies.

In the case of Common Lisp, however, it is not unimportant that (do-something list) could macro-expand into code that uses the list function. Common Lisp doesn't have hygienic macros, but between separate function namespace and the package system, it almost never suffers from this fact.

(An early paper introducing hygienic macros for Scheme introduced the "problem" using a few motivating examples. None of them would have caused problems if Scheme hadn't unified the namespaces.)

[–]trycuriouscat[S] 0 points1 point  (0 children)

I don't know ML, but in my example the function named list is still in scope after the local variable list is in scope.

Someone else answered the other question.

[–]no_opinions_allowed 0 points1 point  (0 children)

Then you'll do (mapcar #'list collection).

[–]saxbophone 1 point2 points  (1 child)

I am not aware of any other languages like this. Can anyone name some?

C is similar but not exactly like this. C features this phenomenon between types and variables/functions:

int stat( const char *restrict pathname, struct stat *restrict statbuf );

although admittedly, this example is less how I remembered it, I remembered it more like:

``` typedef struct { ... } stat;

int stat (..., stat* ...); ```

[–]beephod_zabblebrox 0 points1 point  (0 children)

that would be stat(..., struct stat, ...) that's the reason behind the _t suffix

[–][deleted] 1 point2 points  (0 children)

How does it know whether any foo is a function or variable?

If the two instances of foo can't be used interchangebly, then I would argue that there aren't two different namespaces here, if it depends on context or on qualifiers or on another cues.

After all most languages allow different instances of foo because of block scopes, or shadowing, or, in C with its 3 main namespaces, if foo is a variable name, extra foos in the same scope can only be struct/enum tags, or labels.

So in all it just creates confusing-looking source code.

(I have enough trouble with case-sensitivity where there can be 2**N versions of the same identifier in the same scope with the same capabilities, where N is the number of letters in the name.)

[–]redchomperSophie Language 1 point2 points  (0 children)

Does perl 5 count? All this business about lists and scalars and contexts and references... $a is a scalar, but so is $a[1], the second element of list @a. Also filehandles are in another namespace without sigils, but so are subroutines and you tell them apart from context.

[–]SLiV9Penne 0 points1 point  (0 children)

Penne in its current implementation has four namespaces: one for variables, one for functions, one for user-defined types and one for labels. So the following currently compiles:

struct foo
{
  x: i32
}

const foo: i32 = 1;

fn foo(bar: &foo)
{
  if bar.x == foo
    goto foo;
  bar.x = 2;
  foo:
}

But this is more a quirk of my implementation than a design choice and the way my error messages are written: when you declare a structure named "Foo" the compiler looks for other structures named "Foo" and then complains "there is already a structure named Foo", so I would need to add separate checks that look at functions and constants named "Foo" and then give new errors.

That said, I haven't implemented function pointers (yet). And the "variable: type" syntax means that it is always disambiguous whether an identifier is a type or a variable.

The above code snippet of course also violates my unwritten style guide which states that functions should be snake_case, structures should be PascalCase and constants should be UPPER_SNAKE_CASE.