Gleam for PHP users
Hello Hypertext crafters!
Hello Hypertext crafters!
In PHP, comments are written with a //
prefix.
// Hello, Joe!
Multi line comments may be written like so:
/*
* Hello, Joe!
*/
In PHP, above trait
, interface
, class
, member
, function
declarations
there can be docblocks
like so:
/**
* a very special trait.
*/
trait Wibble {}
/**
* A Wabble class
*/
class Wabble {}
/**
* A wubble function.
*
* @var string $str String passed to wubble
* @return string An unprocessed string
*/
function wubble(string $str) : string { return $str; }
Documentation blocks (docblocks) are extracted into generated API documentation.
In Gleam, comments are written with a //
prefix.
// Hello, Joe!
Comments starting with ///
are used to document the following function,
constant, or type definition. Comments starting with ////
are used to
document the current module.
//// This module is very important.
/// The answer to life, the universe, and everything.
const answer: Int = 42
/// A main function
fn main() {}
/// A Dog type
type Dog {
Dog(name: String, cuteness: Int)
}
//
comments are not used while generating documentation files, while
////
and ///
will appear in them.
You can rebind variables in both languages.
$size = 50;
$size = $size + 100;
$size = 1;
In local scope PHP has no specific variable keyword. You choose a name and that’s it!
In class scope for property declaration PHP uses at least one related
modifier keyword to create properties such as: public
, private
,
protected
, static
or readonly
(var
is deprecated).
Gleam has the let
keyword before its variable names.
let size = 50
let size = size + 100
let size = 1
PHP supports basic, one directional destructuring (also called unpacking). Tuple of values can be unpacked and inner values can be assigned to left-hand variable names.
[$a, $b] = [1, 2];
// $a == 1
// $b == 2
[1 => $idx2] = ['wibble', 'wabble', 'wubble'];
// $idx2 == 'wabble'
["profession" => $job] = ['name' => 'Joe', 'profession' => 'hacker'];
// $job == 'hacker'
In Gleam, let
and =
can be used for pattern matching, but you’ll get
compile errors if there’s a type mismatch, and a runtime error if there’s
a value mismatch. For assertions, the equivalent let assert
keyword is
preferred.
let #(a, _) = #(1, 2)
// a = 1
// `_` matches 2 and is discarded
let assert [] = [1] // runtime error
let assert [y] = "Hello" // compile error, type mismatch
Asserts should be used with caution.
PHP is a dynamically typed language. Types are only checked at runtime and a variable can have different types in its lifetime.
PHP gradually introduced more and more type hints that are optional.
The type information is accessible via get_type()
at runtime.
These hints will mainly be used to inform static analysis tools like IDEs, linters, etc.
class Wibble {
private ?string $wabble;
}
PHP’s array
structure is an ordered map, as stated in the PHP manual,
and it can be treated as an array, dictionary, etc.
While creating arrays in PHP the type of its elements cannot be set explicitly
and each element can be of a different type:
$someList = [1, 2, 3];
$someTuple = [1, "a", true];
$someMap = [0 => 1, "wibble" => "wabble", true => false];
Single variables cannot be type-annotated unless they are class
or trait
members.
In Gleam type annotations can optionally be given when binding variables.
let some_list: List(Int) = [1, 2, 3]
let some_string: String = "Wibble"
Gleam will check the type annotation to ensure that it matches the type of the assigned value. It does not need annotations to type check your code, but you may find it useful to annotate variables to hint to the compiler that you want a specific type to be inferred.
In PHP, you can define functions with the function
keyword. One or many return
keywords are optional.
function hello($name = 'Joe') : string
{
if ($name == 'Joe') {
return 'Welcome back, Joe!';
}
return "Hello $name";
}
function noop()
{
// Will automatically return NULL
}
Anonymous functions returning a single expression can also be defined and be bound to variables.
$x = 2;
$phpAnonFn = function($y) use ($x) { return $x * $y; }; // Creates a new scope
$phpAnonFn(3); // 6
$phpArrowFn = fn ($y) => $x * $y; // Inherits the outer scope
$phpArrowFn(3); // 6
Gleam’s functions are declared like so:
fn sum(x, y) {
x + y
}
Gleam’s anonymous functions have the same basic syntax.
let mul = fn(x, y) { x * y }
mul(1, 2)
A difference between PHP’s and Gleam’s anonymous functions is that in PHP they create a new local scope, in Gleam they close over the local scope, aka create a copy and inherit all variables in the scope. This means that in Gleam you can shadow local variables within anonymous functions but you cannot influence the variable bindings in the outer scope. This is different for PHP’s arrow functions where they inherit the scope like Gleam does.
The only difference between module functions and anonymous functions in Gleam is that module functions heads may also feature argument labels, like so:
// In some module.gleam
pub fn distance(from x: Int, to y: Int) -> Int {
x - y |> int.absolute_value()
}
// In some other function
distance(from: 1, to: -2) // 3
In PHP, top level functions are exported by default. There is no notion of private module-level functions.
However at class level, all properties are public, by default.
class Wibble {
static $wabble = 5;
private $wubble = 6;
static function wobble() {
return "Hello Joe!";
}
private static function webble() {
return "Hello Rasmus!";
}
}
echo Wibble::$wabble; // 5
echo Wibble::$wubble; // Error
echo Wibble::wobble(); // "Hello Joe"
echo Wibble::webble(); // Error
In Gleam, functions are private by default and need the pub
keyword to be
marked as public.
// this is public
pub fn sum(x, y) {
x + y
}
// this is private
fn mul(x, y) {
x * y
}
Global functions may exist in a global scope, and to execute functions or
create objects and invoke methods at some point they have to be called from
the global scope. Usually there is one index.php
file whose global scope
acts as if it was the main()
function.
Gleam does not support a global scope. Instead Gleam code is either
representing a library, which can be required as a dependency, and/or it
represents an application having a main module, whose name must match to the
application name and within that main()
-function which will be called via
either gleam run
or when the entrypoint.sh
is executed.
In contrast to PHP, where any PHP file can contain a global scope that can be invoked by requiring the file, in Gleam only code that is within functions can be invoked.
On the Beam, Gleam code can also be invoked from other Erlang code, or it can be invoked from browser’s JavaScript, Deno or NodeJS runtime calls.
Type hints can be used to optionally annotate function arguments and return types.
Discrepancies between type hints and actual values at runtime do not prevent
interpretation of the code. Static code analysers (IDE tooling, type checkers
like phpstan
) will be required to detect those errors.
function sum(int $x, int $y) : int {
return $x + $y;
}
function mul(int $x, int $y) : bool {
# no errors from the interpreter.
return $x * $y;
}
Functions can optionally have their argument and return types annotated in Gleam. These type annotations will always be checked by the compiler and throw a compilation error if not valid. The compiler will still type check your program using type inference if annotations are omitted.
fn add(x: Int, y: Int) -> Int {
x + y
}
fn mul(x: Int, y: Int) -> Bool {
x * y // compile error, type mismatch
}
As long as functions are in scope they can be assigned to a new variable.
As methods or static functions classes, functions can be accessed via
$this->object_instance_method()
or self::static_class_function()
.
Other than that only anonymous functions can be moved around the same way as other values.
$doubleFn = function($x) { return $x + $x; };
// Some imaginary pushFunction
pushFunction($queue, $doubleFn);
However in PHP
it is not possible to pass around global, class or instance
functions as values.
Gleam has a single namespace for constants and functions within a module, so there is no need for a special syntax to assign a module function to a variable.
fn identity(x) {
x
}
fn main() {
let func = identity
func(100)
}
Both PHP and Gleam have ways to give arguments names and in any order.
When calling a function, arguments can be passed:
// Some imaginary replace function
function replace(string $each, string $with, string $inside) {
// TODO implementation
}
// Calling with positional arguments:
replace(",", " ", "A,B,C");
// Calling with named arguments:
replace(inside: "A,B,C", each: ",", with: " ");
In Gleam arguments can be given a label as well as an internal name. Contrary to PHP, the name used at the call-site does not have to match the name used for the variable inside the function.
pub fn replace(inside str, each pattern, with replacement) {
todo
}
replace(",", " ", "A,B,C")
replace(inside: "A,B,C", each: ",", with: " ")
There is no performance cost to Gleam’s labelled arguments as they are optimised to regular function calls at compile time, and all the arguments are fully type checked.
Operator | PHP | Gleam | Notes |
---|---|---|---|
Equal | == |
== |
In Gleam both values must be of the same type |
Strictly equal to | === |
== |
Comparison in Gleam is always strict. (see note for PHP) |
Reference equality | instanceof |
True only if an object is an instance of a class | |
Not equal | != |
!= |
In Gleam both values must be of the same type |
Not equal | !== |
!= |
Comparison in Gleam is always strict (see note for PHP) |
Greater than | > |
> |
In Gleam both values must be Int |
Greater than | > |
>. |
In Gleam both values must be Float |
Greater or equal | >= |
>= |
In Gleam both values must be Int |
Greater or equal | >= |
>=. |
In Gleam both values must be Float |
Less than | < |
< |
In Gleam both values must be Int |
Less than | < |
<. |
In Gleam both values must be Float |
Less or equal | <= |
<= |
In Gleam both values must be Int |
Less or equal | <= |
<=. |
In Gleam both values must be Float |
Boolean and | && |
&& |
In Gleam both values must be Bool |
Logical and | && |
Not available in Gleam | |
Boolean or | || |
|| |
In Gleam both values must be Bool |
Logical or | || |
Not available in Gleam | |
Boolean not | xor |
Not available in Gleam | |
Boolean not | ! |
! |
In Gleam both values must be Bool |
Add | + |
+ |
In Gleam both values must be Int |
Add | + |
+. |
In Gleam both values must be Float |
Subtract | - |
- |
In Gleam both values must be Int |
Subtract | - |
-. |
In Gleam both values must be Float |
Multiply | * |
* |
In Gleam both values must be Int |
Multiply | * |
*. |
In Gleam both values must be Float |
Divide | / |
/ |
In Gleam both values must be Int |
Divide | / |
/. |
In Gleam both values must be Float |
Remainder | % |
% |
In Gleam both values must be Int |
Concatenate | . |
<> |
In Gleam both values must be String |
Pipe | -> |
|> |
Gleam’s pipe can chain function calls. See note for PHP |
==
is by default comparing by value in PHP:
===
is for comparing by strict equality in PHP:
$wibble->wabble(1)->wubble(2)
means wabble(1)
is called as a method
of $wibble
and then wubble()
is called as a method of the return value
(object) of the wabble(1)
call. The objects in this chain usually
mutate to keep the changed state and carry it forward in the chain.In PHP, constants can only be defined within classes and traits.
class TheQuestion {
public const theAnswer = 42;
}
echo TheQuestion::theAnswer; // 42
In Gleam constants can be created using the const
keyword.
// the_question.gleam module
const the_answer = 42
pub fn main() {
the_answer
}
They can also be marked public via the pub
keyword and will then be
automatically exported.
PHP blocks are always associated with a function / conditional / loop or similar declaration. Blocks are limited to specific language constructs. There is no way to create multi-line expressions blocks like in Gleam.
Blocks are declared via curly braces.
function a_func() {
// A block starts here
if ($wibble) {
// A block here
} else {
// A block here
}
// Block continues
}
In Gleam curly braces, {
and }
, are used to group expressions.
pub fn main() {
let x = {
some_function(1)
2
}
// Braces are used to change precedence of arithmetic operators
let y = x * {x + 10}
y
}
Unlike in PHP, in Gleam function blocks are always expressions, so are case
blocks or arithmetic sub groups. Because they are expressions they always
return a value.
For Gleam the last value in a block’s expression is always the value being returned from an expression.
In PHP strings are stored as an array of bytes and an integer indicating the length of the buffer. PHP itself has no information about how those bytes translate to characters, leaving that task to the programmer. PHP’s standard library however features a bunch of multi-byte compatible functions and conversion functions between UTF-8, ISO-8859-1 and further encodings.
PHP strings allow interpolation.
In Gleam all strings are UTF-8 encoded binaries. Gleam strings do not allow
interpolation, yet. Gleam however offers a string_builder
via its standard
library for performant string building.
$what = 'world';
'Hellø, world!';
"Hellø, ${what}!";
"Hellø, world!"
Tuples are very useful in Gleam as they’re the only collection data type that allows mixed types in the collection.
PHP does not really support tuples, but its array type can easily be used to mimick tuples. Unpacking can be used to bind a name to a specific value of the tuple.
$myTuple = ['username', 'password', 10];
[$_, $pwd, $_] = $myTuple;
echo $pwd; // "password"
// Direct index access
echo $myTuple[0]; // "username"
let my_tuple = #("username", "password", 10)
let #(_, pwd, _) = my_tuple
io.print(pwd) // "password"
// Direct index access
io.print(my_tuple.0) // "username"
Arrays in PHP are allowed to have values of mixed types, but not in Gleam.
PHP does not feature special syntax for list handling.
$list = [2, 3, 4];
$head = array_slice($list, 0, 1)[0];
$tail = array_slice($list, 1);
# $head == 2
# $tail == [3, 4]
$arr = array_merge($tail, [1.1]);
# $arr == [3, 4, 1.1]
Gleam has a cons
operator that works for lists destructuring and
pattern matching. In Gleam lists are immutable so adding and removing elements
from the start of a list is highly efficient.
let list = [2, 3, 4]
let list = [1, ..list]
let [1, second_element, ..] = list
[1.0, ..list] // compile error, type mismatch
In PHP, the array
type can also be treated as a dictionary and can have keys of any type as long as:
null
, an int
, a string
or a bool
(some conversions
occur, such as null to ""
and false
to 0
as well as true
to 1
and "1"
to 1
. Float indexes, which are not representing integers
indexes are deprecated due to being auto downcast to integers).In Gleam, dicts can have keys and values of any type, but all keys must be of the same type in a given dict and all values must be of the same type in a given dict. The type of key and value can differ from each other.
There is no dict literal syntax in Gleam, and you cannot pattern match on a dict. Dicts are generally not used much in Gleam, custom types are more common.
["key1" => "value1", "key2" => "value2"]
["key1" => "1", "key2" => 2]
import gleam/dict
dict.from_list([#("key1", "value1"), #("key2", "value2")])
dict.from_list([#("key1", "value1"), #("key2", 2)]) // Type error!
PHP and Gleam both support Integer
and Float
. Integer and Float sizes for
both depend on the platform: 64-bit or 32-bit hardware and OS and for Gleam
JavaScript and Erlang.
While PHP differentiates between integers and floats it automatically converts floats and integers for you, removing precision or adding floating point decimals.
1 / 2 // 0.5
1 / 2 // 0
1.5 + 10 // Compile time error
You can use the gleam standard library’s int
and float
modules to convert
between floats and integers in various ways including rounding
, floor
,
ceiling
and many more.
case
is how flow control is done in Gleam. It can be seen as a switch
statement on steroids. It provides a terse way to match a value type to an
expression. It is also used to replace if
/else
statements.
PHP features 3 different expressions to achieve similar goals:
if
/else if
/else
(does not return)switch
/case
/break
/default
(does not return, does not autobreak)match
(returns)function http_error_impl_1($status) {
if ($status === 400) {
return "Bad request";
} else if ($status === 404) {
return "Not found";
} else if ($status === 418) {
return "I'm a teapot";
} else {
return "Internal Server Error";
}
}
function http_error_impl_2($status) {
switch ($status) {
case "400": // Will work because switch ($status) compares non-strict as in ==
return "Bad request";
break; // Not strictly required here, but combined with weak typing multiple cases could be executed if break is omitted
case 404:
return "Not found";
break;
case 418:
return "I'm a teapot";
break;
default:
return "Internal Server Error";
}
}
function http_error_impl_3($status) {
return match($status) { // match($status) compares strictly
400 => "Bad request",
404 => "Not found",
418 => "I'm a teapot",
default => "Internal Server Error"
};
}
The case operator is a top level construct in Gleam:
case some_number {
0 -> "Zero"
1 -> "One"
2 -> "Two"
n -> "Some other number" // This matches anything
}
As all expressions the case expression will return the matched value.
They can be used to mimick if/else or if/elseif/else, with the exception that any branch must return unlike in PHP, where it is possible to mutate a variable of the outer block/scope and not return at all.
let is_status_within_4xx = status / 400 == 1
case status {
400 -> "Bad Request"
404 -> "Not Found"
_ if is_status_within_4xx -> "4xx" // This works as of now
// status if status / 400 == 1 -> "4xx" // This will work in future versions of Gleam
_ -> "I'm not sure"
}
if/else example:
case is_admin {
True -> "allow access"
False -> "disallow access"
}
if/elseif/else example:
case True {
_ if is_admin == True -> "allow access"
_ if is_confirmed_by_mail == True -> "allow access"
_ -> "deny access"
}
Exhaustiveness checking at compile time, which is in the works, will make
certain that you must check for all possible values. A lazy and common way is
to check of expected values and have a catchall clause with a single underscore
_
:
case scale {
0 -> "none"
1 -> "one"
2 -> "pair"
_ -> "many"
}
The case operator especially coupled with destructuring to provide native pattern matching:
case xs {
[] -> "This list is empty"
[a] -> "This list has 1 element"
[a, b] -> "This list has 2 elements"
_other -> "This list has more than 2 elements"
}
The case operator supports guards:
case xs {
[a, b, c] if a >. b && a <=. c -> "ok"
_other -> "ko"
}
…and disjoint union matching:
case number {
2 | 4 | 6 | 8 -> "This is an even number"
1 | 3 | 5 | 7 -> "This is an odd number"
_ -> "I'm not sure"
}
In Gleam most functions, if not all, are data first, which means the main data value to work on is the first argument. By this convention and the ability to specify the argument to pipe into, Gleam allows writing functional, immutable code, that reads imperative-style top down, much like unix tools and piping.
PHP does not offer pipes but it can chain calls by making functions return objects which in turn ship with their list of methods.
// Imaginary PHP code
(new Session($request))
->authorize()
->setSuccessFlash('Logged in successfully!')
->setFailureFlash('Failed to login!')
->redirectToRequestedUrl();
// Imaginary Gleam code
request
|> session.new()
|> session.authorize()
|> flash.set_success_flash('Logged in successfully!')
|> flash.set_failure_flash('Failed to login!')
|> response.redirect_to_requested_url()
Despite being similar to read and comprehend, the PHP code creates a session
object, and calls the authorize method of the session object: That session
object then returns another object, say an AuthorizedUser
object - you don’t
know by looking at the code what object gets returned. However you know it must
implement a setSuccessFlash
method. At the last step of the chain redirect
is called on an object returned from setFailureFlash
.
In the Gleam code the request data is piped into session.new()
’s first
argument and that return value is piped further down. It is readability sugar
for:
response.redirect_to_requested_url(
flash.set_failure_flash(
flash.set_success_flash(
session.authorize(
session.new(request)
),
'Logged in successfully!'
),
'Failed to login!'
)
)
Error management is approached differently in PHP and Gleam.
PHP uses the notion of exceptions to interrupt the current code flow and pop up the error to the caller.
An exception is raised using the keyword throw
.
function aFunctionThatFails() {
throw new RuntimeException('an error');
}
The callee block will be able to capture any exception raised in the block
using a try/catch
set of blocks:
// callee block
try {
echo 'this line will be executed and thus printed';
aFunctionThatFails();
echo 'this line will not be executed and thus not printed';
} catch (Throwable $e) {
var_dump(['doing something with the exception', $e]);
}
In contrast in Gleam, errors are just containers with an associated value.
A common container to model an operation result is
Result(ReturnType, ErrorType)
.
A Result
is either:
Error(ErrorValue)
Ok(Data)
recordHandling errors actually means to match the return value against those two scenarios, using a case for instance:
case parse_int("123") {
Ok(i) -> io.println("We parsed the Int")
Error(e) -> io.println("That wasn't an Int")
}
In order to simplify this construct, we can use the try
keyword that will:
Ok(Something)
is matched,Error(Something)
from
the given block.let a_number = "1"
let an_error = Error("ouch")
let another_number = "3"
try int_a_number = parse_int(a_number)
try attempt_int = parse_int(an_error) // Error will be returned
try int_another_number = parse_int(another_number) // never gets executed
Ok(int_a_number + attempt_int + int_another_number) // never gets executed
Type aliases allow for easy referencing of arbitrary complex types.
PHP does not have this feature, though either regular classes or static classes
can be used to design custom types and class definitions in take can be aliased
using class_alias()
.
A simple variable can store the result of a compound set of types.
class Point {
// Can act as an opaque type and utilize Point
// Can be class_aliased to Coordinate
}
class Triangle {
// Can act as an opaque type definition and utilize Point
}
The type
keyword can be used to create aliases.
pub type Headers =
List(#(String, String))
Custom type allows you to define a collection data type with a fixed number of named fields, and the values in those fields can be of differing types.
PHP uses classes to define user-defined, record-like types. Properties are defined as class members and initial values are generally set in the constructor.
By default the constructor does not provide base initializers in the constructor so some boilerplate is needed:
class Person {
public function __construct(public string $name, public int $age) { }
}
$person = new Person(name: "Joe", age: 40);
$person->name; // Joe
Gleam’s custom types can be used as structs. At runtime, they have a tuple representation and are compatible with Erlang records (or JavaScript objects).
type Person {
Person(name: String, age: Int)
}
let person = Person(name: "Joe", age: 40)
let name = person.name
An important difference to note is there is no Java-style object-orientation in Gleam, thus methods can not be added to types. However opaque types exist, see below.
PHP generally does not support unions with a few exceptions such as:
null
Array
or Traversable
.In Gleam functions must always take and receive one type. To have a union of two different types they must be wrapped in a new custom type.
class Wibble {
public ?string $aStringOrNull;
}
type IntOrFloat {
AnInt(Int)
AFloat(Float)
}
fn int_or_float(X) {
case X {
True -> AnInt(1)
False -> AFloat(1.0)
}
}
In PHP, constructors can be marked as private and opaque types can either be modelled in an immutable way via static classes or in a mutable way via a factory pattern.
In Gleam, custom types can be defined as being opaque, which causes the constructors for the custom type not to be exported from the module. Without any constructors to import other modules can only interact with opaque types using the intended API.
class PointObject
{
private function __construct(public int $x, public int $y) {
}
public static function spawn(int $x, int $y) {
if ($x >= 0 && $x <= 99 && $y >= 0 && $y <= 99) {
return new self($x, $y);
}
return false;
}
}
PointObject::spawn(1, 2); // Returns a Point object
This requires mutation, but prohibits direct property changes.
PHP allows to skip object mutation by using static classes:
class PointStruct
{
public static function spawn(int $x, int $y) {
if ($x >= 0 && $x <= 99 && $y >= 0 && $y <= 99) {
return compact('x', 'y') + ['struct' => __CLASS__];
}
return false;
}
}
PointStruct::spawn(1, 2); // Returns an array managed by PointStruct
However PHP will in this case not prohibit the direct alteration the returned structure, like Gleam’s custom types can.
// In the point.gleam opaque type module:
pub opaque type Point {
Point(x: Int, y: Int)
}
pub fn spawn(x: Int, y: Int) -> Result(Point, Nil) {
case x >= 0 && x <= 99 && y >= 0 && y <= 99 {
True -> Ok(Point(x: x, y: y))
False -> Error(Nil)
}
}
// In the main.gleam module
pub fn main() {
assert Ok(point) = Point.spawn(1, 2)
point
}
PHP does not feature modules, but many other containers such as classes, traits and interfaces. Historically a single file can contain many classes, traits and interfaces one after another, though it is best practise to only contain one such declaration per file.
Using PHP namespaces, these can be placed in a registry that does not need to map to the source code file system hierarchy, but by convention should.
In src/Wibble/Wabble.php
:
// Anything declared in this file will be inside namespace Wibble
namespace Wibble;
// Creation of (static) class Wabble in Wibble, thus as Wibble/Wabble
class Wabble {
public static function identity($x) {
return $x;
}
}
Making the static class available in the local scope and calling the function
index.php
(aka PHP’s main function):
// After auto-loading has happened
use Wibble\Wabble;
Wabble::identity(1); // 1
Coming from PHP the closest thing PHP has that are similar to Gleam’s modules are static classes: Collections of functions and constants grouped into a static class.
In comparison Gleam modules can also contain custom types.
A gleam module name corresponds to its file name and path.
Since there is no special syntax to create a module, there can be only one module in a file and since there is no way name the module the filename always matches the module name which keeps things simple and transparent.
In /src/wibble/wabble.gleam
:
// Creation of module function identity
// in module wabble
pub fn identity(x) {
x
}
Importing the wabble
module and calling a module function:
// In src/main.gleam
import wibble/wabble // if wibble was in a directory called `lib` the import would be `lib/wibble/wabble`.
pub fn main() {
wabble.identity(1) // 1
}
PHP features ways to load arbitrary PHP code: require
, include
and
autoload such as spl_autoload_register
. Once class paths are known and
registered for autoloading, they can brought into the scope of a file by using
the use
statement which is part of PHP’s namespacing.
Also see https://www.php-fig.org/psr/psr-4/.
Inside src/Nasa/MoonBase.php
// Makes available src/nasa/RocketShip.php
use Nasa\RocketShip;
class MoonBase {
public static function exploreSpace() {
RocketShip::launch();
}
}
Imports are relative to the app src
folder.
Modules in the same directory will need to reference the entire path from src
for the target module, even if the target module is in the same folder.
Inside module src/nasa/moon_base.gleam
:
// imports module src/nasa/rocket_ship.gleam
import nasa/rocket_ship
pub fn explore_space() {
rocket_ship.launch()
}
PHP features namespaces which can be used to rename classes when they clash:
// Source files must first be added to the auto-loader
use Unix\Cat;
use Animal\Cat as Kitty;
// Cat and Kitty are available
Gleam has as similar feature:
import unix/cat
import animal/cat as kitty
// cat and kitty are available
This may be useful to differentiate between multiple modules that would have the same default name when imported.
use Animal\Cat\{
Cat,
function stroke
};
$kitty = new Cat(name: "Nubi");
stroke($kitty);
import animal/cat.{
Cat,
stroke
}
pub fn main() {
let kitty = Cat(name: "Nubi")
stroke(kitty)
}
Importing common types such as gleam/order.{Lt, Eq, Gt}
or
gleam/option.{Some,None}
can be very helpful.
To iterate a few foundational differences:
reset()
or end()
or array_pop()
.resource
to a Date
in terms of order.
Gleam’s comparison operators are very strict and limited, any other
comparisons and conversions must happen via function calls.Result
type. There can however be other errors, such as miss-behavior due
accidental to division by 0, crashes on RAM or storage limits, hardware
failures, etc. In these cases on the BEAM there are ways to manage these
via BEAM’s supervision trees.