Skip to main content

Name rules and conflicts

The Hybrix language allows you to reuse the same name for different definitions. Consider the identifier button in the code below:

class button
end class

class page
# This member variable "self.button" has type "class button":
var button: button

func initialize()
# This local variable "button" also has type "class button":
var button: button

# In "new button()", the word "button" is naming the class constructor.
new button() -> button

# Assign the local variable "button" to the "self.button" member
button -> .button
end func
end class

How many things can be called "button" before the compiler reports a naming conflict? The rules below answer this question.

Name scopes

The Hybrix language defines name scopes. If two definitions belong to the same scope, they cannot have the same name.

Name scopeExample of declaration in this scope
Names of modulesmodule example
Names of typestype example
class example
primitive types (int, byte[], etc.)
Namespace members
(accessed using ::)
var inside a module
const inside a module or class*
func inside a module or class*
Instance members
(accessed using .)
var inside a class
func inside a class
Func parameters and localsvar example inside a func body
func example()

* Most class members are accessed using ., however constants and member function pointers aren't associated with any particular instance.

If the same name is reused within the same scope, the compiler reports an error:

Conflict: names of modules

module m
end module

# ⚠️ ERROR: There is already a module named "m"
module m
end module

Conflict: names of types

type t is int

# ⚠️ ERROR: There is already a type named "t"
class t
end class

Conflict: namespace members

module m
var v: int

# ⚠️ ERROR: There is already a module member named "v"
func v()
end func
end module

Conflict: class members

class my_base
var f: int
end class

class my_child extends my_base
# ⚠️ ERROR: There is already a class member named "f"
func f()
end func
end class

Note that a child class inherits members from its base class, and these names share a single scope.

Conflict: func parameters and locals

module m
func f(p: int)
# ⚠️ ERROR: There is already a parameter named "p"
var p: int
end func
end module

Name qualifiers

It's generally okay to reuse the same name in different name scopes. The Hybrix language syntax uses explicit qualifiers such as :: and . to distinguish scopes, which prevents any ambiguity about what a name is referring to.

Name scopeExample syntaxes referencing this scope
Names of modulesmodule_name::x
Names of typesvar v: type_name
v as type_name
Namespace membersmy_module::member_name
Instance membersmy_object.member_name
.member_name
Func parameters and localsvar_name without any prefix

Counterparts

When a top-level type has the same name as a module, we call them counterparts. There are two possibilities: type alias and module counterparts, and class and module counterparts.

Note that type alias and class counterparts are not allowed, because class and type definitions both belong to the same name scope.

Type alias and module

The example below shows type alias and module counterparts named vector:

type vector is int[size 3]

module vector
func get_length_squared(vector: vector): int
return vector[0]*vector[0] + vector[1]*vector[1] + vector[2]*vector[2]
end func
end module

module main
func start()
var vector: vector
new int[size 3]() -> vector

var result: int
result <- vector::get_length_squared(vector)
end func
end module

Important points:

  • The module members are accessed using :: as usual, for example vector::get_length_squared().
  • The type alias simply coexists alongside the module.

Class and module

The example below shows class and module counterparts named cat:

class animal
var name: string

constructor(name: string)
.name <- name
end constructor
end class

class cat extends animal
func meow()
end func
end class

module cat
var best_cat: cat

func init()
cat::best_cat <- new cat("Mittens")
end func
end module

Important points:

  • The module members are accessed using :: as usual, for example cat::best_cat.
  • The class's object members are accessed using ., for example .name.
  • The module and class must not reuse the same name for any member. This includes inherited members.

Continuing the above example, defining module cat like this would produce an error:

module cat
var best_cat: cat

func init()
cat::best_cat <- new cat("Mittens")
end func

# ⚠️ ERROR: The class already has a member called "name" (inherited from animal)
func name()
end func
end module

Keeping the rules simple

When we say a module and class must not reuse the same name for any member, that policy is stricter than technically necessary. Consider this example:

class counterparts_example
var x: int # some_instance.x
end class

module counterparts_example
# ⚠️ Disallowed only because it would be confusing
var x: int # counterparts_example::x
end module

There is actually no confusion between these two x variables; we can always distinguish them by :: versus ., and if you think carefully you can find even more interesting examples:

class c
const y is 123 # c::y
# ⚠️ Disallowed only because it would be confusing
var y: int # some_instance.y
end class

The language treats these as naming conflicts for two reasons:

  1. It's difficult to explain when it would be allowed (for example, function pointer syntax puts class member functions into both the namespace scope and instance scope).
  2. It's not particularly useful. Your code will be easier to read without this kind of name reuse.