From e04af86491d97b297406cc4cd0d77fbbfc3a94c4 Mon Sep 17 00:00:00 2001 From: JJ Date: Thu, 16 May 2024 17:40:34 -0700 Subject: docs: update website --- docs/book/MODULES.html | 66 +++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 27 deletions(-) (limited to 'docs/book/MODULES.html') diff --git a/docs/book/MODULES.html b/docs/book/MODULES.html index 4ee1fe5..738ee93 100644 --- a/docs/book/MODULES.html +++ b/docs/book/MODULES.html @@ -7,7 +7,7 @@ - + @@ -178,18 +178,46 @@
! This section is incomplete. Proceed with caution.
Puck has a first-class module system, inspired by such expressive designs in the ML family.
-
-Modules package up code for use by others. Identifiers known at compile time may be part of a module signature: these being constants, functions, macros, types, and other modules themselves. They may be made accessible to external users by prefixing them with the pub
keyword. Files are modules, named with their filename. The mod
keyword followed by an identifier and an indented block of code explicitly defines a module, inside of the current module. Modules are first class: they may be bound to constants (having the type : mod
) and publicly exported, or bound to local variables and passed into functions for who knows what purpose.
The use
keyword lets you use other modules. The use
keyword imports public symbols from the specified module into the current scope unqualified. This runs contrary to expectations coming from most other languages: from Python to Standard ML, the standard notion of an "import" usually puts the imported symbols behind another symbol to avoid "polluting the namespace". As Puck is strongly typed and allows overloading, however, the author sees no reason for namespace pollution to be of concern. These unqualified imports have the added benefit of making uniform function call syntax more widely accessible. It is inevitable that identifier conflicts will exist on occasion, of course: when this happens, the compiler will force qualification (this then does restrict uniform function call syntax).
+pub mod stack =
+ pub type Stack[T] = class
+ init(static type Self): Stack[T]
+ push(mut Self, val: T)
+ pop(mut Self): T?
+ peek(lent Self): lent T?
+
+ pub mod list =
+ type ListStack[T] = list[T]
+
+ pub func init[T](self: static type ListStack[T]): Stack[T] = []
+ pub func push[T](self: mut ListStack[T], val: T) = self.push(T)
+ pub func pop[T](self: mut ListStack[T]): T? = self.pop
+ pub func peek[T](self: lent ListStack[T]): lent T? =
+ if self.len == 0 then None else Some(self.last)
+
+use stack.list
+
+let a = ListStack[int].init
+print a.len # error: unable to access method on private type outside its module
+
+a.push(5)
+print a.pop # Some(5)
+
+Modules package up code for use by others. Identifiers known at compile time may be part of a module: these being constants, functions, macros, types, and other modules themselves. Such identifiers may be made accessible outside of the module by prefixing them with the pub
keyword.
Importantly, files are implicitly modules, public and named with their filename. The mod
keyword followed by an identifier and an indented block of code explicitly defines a module, inside of the current module. Modules are first class: they may be bound to constants (having the type : mod
) and publicly exported, or bound to local variables and passed into functions for who knows what purpose.
The use
keyword lets you use other modules.
The use
keyword imports public symbols from the specified module into the current scope unqualified. This runs contrary to expectations coming from most other languages: from Python to Standard ML, the standard notion of an "import" puts the imported symbols behind another symbol to avoid "polluting the namespace". As Puck is strongly typed and allows overloading, however, we see no reason for namespace pollution to be of concern. These unqualified imports have the added benefit of making uniform function call syntax more widely accessible. It is inevitable that identifier conflicts will exist on occasion, of course: when this happens, the compiler will force qualification (this then does restrict uniform function call syntax). We discuss this more later.
Nonetheless, if qualification of imports is so desired, an alternative approach is available - binding a module to a constant. Both the standard library and external libraries are available behind identifiers without use of use
: std
and lib
, respectively. (FFI and local modules will likely use more identifiers, but this is not hammered out yet.) A submodule - for example, std.net
- may be bound in a constant as const net = std.net
, providing all of the modules' public identifiers for use, as fields of the constant net
. We will see this construction to be extraordinarily helpful in crafting high-level public APIs for libraries later on.
Multiple modules can be imported at once, i.e. use std.[logs, tests]
, use lib.crypto, lib.http
. The standard namespaces (std
, lib
) deserve more than a passing mention. There are several of these: std
for the standard library, lib
for all external libraries, crate
for the top-level namespace of a project (subject to change), this
for the current containing module (subject to change)... In addition: there are a suite of language namespaces, for FFI - rust
, nim
, and swift
preliminarily - that give access to libraries from other languages. Recall that imports are unqualified - so use std
will allow use of the standard library without the std
qualifier (not recommended: several modules have common names), and use lib
will dump every library it can find into the global namespace (even less recommended).
use std.[logs, test]
+use lib.crypto, lib.http
+
+Multiple modules can be imported at once. The standard namespaces deserve more than a passing mention. There are several of these: std
for the standard library, lib
for all external libraries, pkg
for the top-level namespace of a project, this
for the current containing module... In addition: there are a suite of language namespaces, for FFI - rust
, nim
, and swift
preliminarily - that give access to libraries from other languages. Recall that imports are unqualified - so use std
will allow use of the standard library without the std
qualifier (not recommended: several modules have common names), and use lib
will dump the name of every library it can find into the global namespace (even less recommended).
A major goal of Puck's module system is to allow the same level of expressiveness as the ML family, while cutting down on the extraneous syntax and boilerplate needed to do so. As such, access modifiers are written directly inline with their declaration, and the file system structure is reused to form an implicit module system for internal use. This - particularly the former - limits the structure a module can expose at first glance, but we will see later that interfaces recoup much of this lost specificity.
-We mentioned that the filesystem forms an implicit module structure. This begets a couple of design choices. Module names must be lowercase, for compatibility with case-insensitive filesystems. Both a file and a folder with the same name can exist. Files within the aforementioned folder are treated as submodules of the aforementioned file. This again restricts the sorts of module structures we can build, but we will again see later that this restriction can be bypassed.
-The this
and crate
modules are useful for this implicit structure...
A major goal of Puck's module system is to allow the same level of expressiveness as the ML family, while cutting down on the extraneous syntax and boilerplate needed to do so. As such, access modifiers are written directly inline with their declaration, and the file system structure is reused to form an implicit module system for internal use. This - particularly the former - limits the structure a module can expose at first glance, but we will see later that classes recoup much of this lost specificity.
+We mentioned that the filesystem forms an implicit module structure. This begets a couple of design choices. Module names must be lowercase, for compatibility with case-insensitive filesystems. Both a file and a folder with the same name can exist. Files within the aforementioned folder are treated as submodules of the aforementioned file. This again restricts the sorts of module structures we can build, but we will see later that this restriction can be bypassed.
+The this
and pkg
modules are useful for this implicit structure...
...
The filesystem provides an implicit module structure, but it may not be the one you want to expose to users.
@@ -224,22 +252,6 @@ - - -- cgit v1.2.3-70-g09d2