Type & Alias Definitions
Modules can also be used to export definitions of Types or Aliases that are intended to be used throughout your overall program.
Exporting Type Definitions
Exporting a Type definition can be as simple as just using the same Type definition syntax that you'd use within a
.claro
source file.
For example, the std module exports the following Type:
Fig 1:
# std.claro_module_api
newtype Error<T> : T
# ...
And beyond mutability, perhaps more subtly, you should also consider whether there will be lots of downstream users directly accessing the Type's internal representation, and if so whether the representation is ever subject to any future change. If so, in the future, it may unknowingly become very hard to ever make changes to the Type's internal representation as, to do so, you would simultaneously be forced to update all of the downstream references to the Type's internal representation.
Thankfully, Claro actually has mechanisms to hide the internal representation of a Type definition from downstream consumers. Learn more in the sections on Unwrappers and Opaque Types.
Exporting Atoms
Exporting an Atom is something of a hybrid between exporting a
static value and a Type definition, as an atom defines a new type
whose only value is the Atom itself. But again, you may export Atoms from Module APIs exactly as it would be defined
within a .claro
source file.
For example, the strings module exports the following atom and several functions that reference it.
Fig 2:
# strings.claro_module_api
atom NOT_FOUND
function indexOf(s: string, of: string) -> oneof<int, NOT_FOUND>;
function indexOfFromIndex(s: string, of: string, from: int) -> oneof<int, NOT_FOUND>;
function lastIndexOf(s: string, of: string) -> oneof<int, NOT_FOUND>;
function lastIndexOfFromIndex(s: string, of: string, from: int) -> oneof<int, NOT_FOUND>;
# ...
Exporting Aliases
While Aliases largely exist to allow you to create your own convenient syntax sugar for complex types, it can sometimes
be useful for a Module to provide a standardized Alias for long or complex types that downstream usages could benefit
from having a shorthand for. Syntax for exporting an Alias in a Module API is exactly the same as the syntax for
declaring an Alias in a .claro
source file.
Fig 3:
# ex1.claro_module_api
alias ComplexStruct : struct {
fieldA: tuple<
struct {
innerA: int,
innerB: [[char]]
},
int,
float
>,
fieldB: provider<oneof<int, string>>
}
# It'll be much easier for users to be able to refer to this type using the exported
# alias rather than the type itself.
provider getRandomComplexStruct() -> ComplexStruct;
Modules Exporting Only Types/Aliases Don't Require any .claro
Source Files
In general, if your Module exclusively exports Type or Alias definitions, you actually do not need to provide any
.claro
srcs to the defining claro_module(...)
target, as the definitions themselves fully specify the Module in
their own right.
Fig 4:
# ex4.claro_module_api
alias UserLookupError : oneof<USER_NOT_FOUND, USER_DELETED, USER_SUSPENDED>
atom USER_NOT_FOUND
atom USER_DELETED
atom USER_SUSPENDED
Fig 5:
load("//:rules.bzl", "claro_module")
claro_module(
name = "user_error_types",
module_api_file = "ex4.claro_module_api",
# You could optionally, explicitly declare an empty list.
# srcs = [],
)