# Modules and Namespacing Puck has a rich module system, inspired by such expressive systems in the ML family of languages, most notably including Rust. Unlike these systems, however, opening modules i.e. unqualified imports is *encouraged* - even in the global scope - which at first would appear to run contrary to the point of a module system. Puck cleans up such "namespace pollution" by **type-based disambiguation**. ```puck import std/[ascii, unicode] todo ``` Type-based disambiguation allows for the simultaneous seamless existence of functions with the same name and different type signature: and further, the usage of multiple functions with identical signatures and multiple types with identical names through explicit disambiguation. This is nothing fancy and follows some simple scoping rules: - A module cannot define two constants with the same name. - A module cannot define two functions with the same type signature. - A module cannot define two types with the same name and generic parameter count. - A module cannot define a constant and a zero-parameter function with the same name. - A module cannot define an enum and a function with the same name. (note: to be weakened) These unqualified imports by default may seem like madness to a Python or C programmer: but because Puck is *statically typed*, there is no ambiguity, and because of *uniform function call syntax*, it is usually clear what overloaded function is being called. Puck is also written with the modern tooling approach of language servers in mind: and so, navigating to the function declaration, in any IDE, should be two clicks or a keystroke away. ```puck ``` Multiple modules can be imported in the same scope, of course, and so conflicts may arise on imports. In the pursuit of *some* explicitness, no attempt is made to guess the proper identifier from usage. These must be disambiguated by prefixing the module name, followed by a dot. This disambiguation breaks uniform function call syntax on functions: yet because functions only conflict when both their name and entire function signature overlap, this is a rare occurrence. If so desired, an import followed by `/[]` will force full qualification of all identifiers in the module - yet this is an antipattern and not recommended. Unrelated to the module system - but note that functions only differing in return type are allowed, albeit discouraged. Extra type annotations may be needed for the compiler to properly infer in such cases. ```puck ``` This document has talked quite a lot about modules so far - with no mention so far of what modules actually *are*. At their basic level: modules are a scope, defined by the `module` keyword followed by a label, a colon, and the module body. As such, modules may contain other modules, and further identifiers within a nested module are not imported by importing the outer module by default. The `module` keyword itself, however, will often go fairly unused: because an implicit form of modules are **files** in the **filesystem structure**. The file structure forms an implicit, internal API, addressable by import statements using `..` and the previously-seen `module/submodule` syntax. Paths may be relative with `..` or absolute from the project root with `/`. Moving and renaming files will break imports, of course, so some care must be taken while refactoring. This will hopefully be a non-issue with the aid of lightweight tooling. ```puck ``` When linked as a library, however, the file structure is not visible - and so one must define an "external API", in the main file of the library. This is aided by the `export` statement which can be used to re-export imported modules as submodules of the existing module. Such an external API allows for significant flexibility in defining the structure of a library. Exports may be of a full module (`export module`), effectively merging it with the current module, or of individual items within the external module `export module/[one, two, three]`. Such exports may be placed within a new `module` scope for even more flexibility. Modules do not export everything in their scope: indeed, all identifiers within a module are **private by default**, and must be explicitly marked public by use of the `pub` keyword. In support of encapsulation, fields of types, too, are considered separate from the type itself and also must be `pub` to be accessible. Identifiers from imported modules, of course, are not considered part of the current module unless explicitly exported. Modules and identifiers from modules may be imported and exported `as` a different name. ```puck ``` todo: explore the relation between module systems and typeclasses. are hot-swappable types in modules (i.e. self Self) worth it, or would such a thing be better accomplished by `int`, `string`, etc being typeclasses?