diff --git a/cheatsheets/gleam-for-javascript-users.md b/cheatsheets/gleam-for-javascript-users.md new file mode 100644 index 00000000..03d706e7 --- /dev/null +++ b/cheatsheets/gleam-for-javascript-users.md @@ -0,0 +1,700 @@ +--- +layout: page +title: Gleam for JavaScript users +subtitle: Hello JavaScripticians! +--- + +- [Comments](#comments) +- [Variables](#variables) + - [Variables type annotations](#variables-type-annotations) +- [Functions](#functions) + - [Function capturing with `_`](#function-capturing-with-_) + - [Exporting functions](#exporting-functions) + - [Function type annotations](#function-type-annotations) + - [Referencing functions](#referencing-function) + - [Labelled arguments](#labelled-arguments) +- [Modules](#modules) +- [Operators](#operators) +- [Constants](#constants) +- [Blocks](#blocks) +- [Data types](#data-types) + - [Strings](#strings) + - [Tuples](#tuples) + - [Lists](#lists) + - [Dicts](#dicts) +- [Flow control](#flow-control) + - [Case](#case) + - [Piping](#piping) + + +## Comments + +### JavaScript + +In JavaScript, comments are written with a `//` prefix. + +```javascript +// Hello, Joe! +``` + +Multi-line comments may be written like so: + +```javascript +/* + * Hello, Joe! + */ +``` + +In JavaScript, above `class` and `function` declarations there can be +`docblocks` like so: + +```javascript +/** + * A Bar class + */ +class Bar {} + +/** + * A quux function. + * + * @param {string} str String passed to quux + * @returns {string} An unprocessed string + */ +function quux(string) { return str; } +``` + +Documentation blocks (docblocks) are extracted into generated API +documentation. + +### Gleam + +In Gleam comments are written with a `//` prefix. + +```gleam +// Hello, Joe! +``` + +Comments starting with `///` are used to document the following statement. +Comments starting with `////` are used to document the current module. + +```gleam +//// This module is very important. + +/// The answer to life, the universe, and everything. +const answer: Int = 42 +``` + +## Variables + +In JavaScript, you can declare variables using `let`, `const`, or `var`. `let` +and `var` variables can be reassigned, and `const`s cannot. + +#### JavaScript + +```javascript +let size = 50; +size = size + 100; + +const height = 60; +height = height + 100; // Error! +``` + +#### Gleam + +Gleam has the `let` keyword before each variable assignment. Variables can't be +mutated or reassigned. If a new variable has the same name as an existing +variable, the new definition _shadows_ the old one, but the old variable is not +overwritten: + +```gleam +import gleam/io + +let size = 50 +let print_it = fn() { io.debug(size) } +let size = size + 100 +let size = 1 +io.debug(size) // prints 1 +print_it() // prints 50 +``` + +### Variables type annotations + +#### JavaScript/TypeScript + +In plain JavaScript there are no static types, but in TypeScript variables can +optionally be annotated with types. + +```typescript +const some_list: number[] = [1, 2, 3]; +``` + +#### Gleam + +In Gleam type annotations can optionally be given when binding variables. + +```gleam +let some_list: List(Int) = [1, 2, 3] +``` + +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. + +## Functions + +#### JavaScript + +In JavaScript, you can define functions with the `function` keyword, or assign +anonymous functions to variables. + +```javascript +function sum(x, y) { + return x + y; +} + +const mul = (x, y) => x * y; +``` + +#### Gleam + +Gleam's functions are declared using a syntax similar to Rust. Gleam's anonymous +functions have a similar syntax, just without the function name. + +```gleam +pub fn sum(x, y) { + x + y +} + +let mul = fn(x, y) { x * y } +mul(1, 2) +``` + +### Function capturing with `_` + +To turn a multi-parameter function into a function with just one parameter, +Gleam has a special function-capturing syntax: + +```gleam +fn add(a: Int, b: Int) -> Int { + a + b +} + +let add2 = fn(x) { add(x, 2) } +// is equivalent to: +let add2 = add(_, 2) +``` + +This is particularly useful in combination with the [pipe operator](#piping). + +### Exporting functions + +#### JavaScript + +In both JavaScript and Gleam, functions are private by default. In JavaScript, +functions can be made public with the `export` keyword. + +```javascript +// this is public +export function sum(x, y) { + return x + y; +} + +// this is private +function mul(x, y) { + return x * y; +} +``` + +#### Gleam + +In Gleam functions need the `pub` keyword to be public. + +```gleam +// this is public +pub fn sum(x, y) { + x + y +} + +// this is private +fn mul(x, y) { + x * y +} +``` + +### Function type annotations + +#### JavaScript/TypeScript + +In TypeScript, you can annotate the types of function parameters and return +values. + +```typescript +function sum(x: number, y: number): number { + return x + y; +} +``` + +The choice of whether, and exactly how to annotate a function can influence how +type-safe your code is. + +#### Gleam + +In Gleam, functions can **optionally** have their argument and return types +annotated. 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. + +```gleam +pub fn add(x: Int, y: Int) -> Int { + x + y +} + +pub fn mul(x: Int, y: Int) -> Bool { // compile error, type mismatch + x * y +} +``` + +### Referencing functions + +Referencing functions in Gleam works like in JavaScript, without any special +syntax. + +#### JavaScript +```javascript +function identity(x) { + return x +} + +function main() { + const func = identity; + func(100); +} +``` + +#### Gleam +```gleam +fn identity(x) { + x +} + +fn main() { + let func = identity + func(100) +} +``` + + +### Labelled arguments + +#### JavaScript + +JavaScript doesn't really have a syntax for passing arguments by name and in any +order, but this behaviour can be approximated using an object literal. + +```javascript +function replace({ inside: string, each: pattern, with: replacement }) { + return string.replace(new RegExp(pattern, "g"), replacement); +} +``` + +```javascript +replace({ each: ",", with: " ", inside: "A,B,C" }); +``` + +Because the arguments are stored in an object there is a small runtime +performance penalty for this pattern. + +#### Gleam + +In Gleam arguments can be given a label as well as an internal name. The name +used at the call-site does not have to match the name used for the variable +inside the function. + +```gleam +import gleam/string + +pub fn replace(inside string, each pattern, with replacement) { + string.replace(string, pattern, replacement) +} +``` + +```gleam +replace(each: ",", with: " ", inside: "A,B,C") +``` + +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. + +## Modules + +Just like in JavaScript, each Gleam file is a module, and values can only be +accessed if they are explicitly made public. Gleam doesn't have an equivalent of +JavaScript's `export default` -- all exports are named. + +### JavaScript + +In JavaScript, `export` marks values as public. Import paths are usually +relative to the current file. + +```javascript +//// my_library/tools/math.js +export function add(a, b) { + return a + b; +} + +//// my_library/other_file.js +import { add } from "./tools/math"; +import * as math from "./tools/math"; + +add(2, 2); +math.add(2, 2); +``` + +### Gleam + +In Gleam, `pub` marks values as public. Import paths are always absolute, +starting at the project root. + +```gleam +//// my_library/tools/math.gleam +pub type Color { + Red + Green + Blue +} + +pub fn add(a, b) { + a + b +} + +//// my_library/other_file.gleam +// You can import a whole module +import my_library/tools/math + +let pixel = math.Green +let sum = math.add(2, 2) + +// Or specify exactly what you want +import my_library/tools/math.{type Color, Red, add} + +let colors: Color = [Red, math.Green] +let sum = add(2, 2) +``` + +## Operators + +| Operator | JavaScript | Gleam | Notes | +| ----------------- | ---------- | ----- | ---------------------------------------------- | +| Equal | `==` | `==` | In Gleam both values must be of the same type | +| Strictly equal to | `===` | `==` | Comparison in Gleam is always strict | +| Not equal | `!==` | `!=` | In Gleam both values must be of the same type | +| Greater than | `>` | `>` | In Gleam both values must be **ints** | +| Greater than | `>` | `>.` | In Gleam both values must be **floats** | +| Greater or equal | `>=` | `>=` | In Gleam both values must be **ints** | +| Greater or equal | `>=` | `>=.` | In Gleam both values must be **floats** | +| Less than | `<` | `<` | In Gleam both values must be **ints** | +| Less than | `<` | `<.` | In Gleam both values must be **floats** | +| Less or equal | `<=` | `<=` | In Gleam both values must be **ints** | +| Less or equal | `<=` | `<=.` | In Gleam both values must be **floats** | +| Boolean and | `&&` | `&&` | In Gleam both values must be **bools** | +| Boolean or | `||` | `||` | In Gleam both values must be **bools** | +| Add | `+` | `+` | In Gleam both values must be **ints** | +| Add | `+` | `+.` | In Gleam both values must be **floats** | +| Subtract | `-` | `-` | In Gleam both values must be **ints** | +| Subtract | `-` | `-.` | In Gleam both values must be **floats** | +| Multiply | `*` | `*` | In Gleam both values must be **ints** | +| Multiply | `*` | `*.` | In Gleam both values must be **floats** | +| Divide | `/` | `/` | In Gleam both values **ints** | +| Divide | `/` | `/.` | In Gleam both values must be **floats** | +| Remainder | `%` | `%` | In Gleam both values must be **ints** | +| Concatenate | `+` | `<>` | In Gleam both values must be **strings** | +| Pipe | | `|>` | Gleam's pipe can pipe into anonymous functions | + +### Notes on operators + +- JavaScript operators are short-circuiting as in Gleam. +- Gleam's `/` operator always returns an integer. +- Chains and pipes: + - In JavaScript chaining is usually done by constructing class methods that + return an object: `foo.bar(1).quux(2)` means `bar(1)` is called as a method + of `foo` and then `quux()` is called as a method of the return value + (object) of the `bar(1)` call. + - In contrast in Gleam piping, no objects are being returned but mere data is + pushed from left to right, much like in unix tooling. + +## Constants + +#### JavaScript + +In JavaScript constants are just regular variables using the `const` keyword. + +```javascript +const THE_ANSWER = 42; + +function main() { + const justANormalVariable = "can also use the const keyword"; + return THE_ANSWER; +} +``` + +#### Gleam + +In Gleam constants are also created using the `const` keyword. The difference to +JavaScript is that Gleam constants can only live at the top-level of a module +(not inside a function), and variables defined using `let` can only live inside +functions (not at the top-level of a module). + +```gleam +const the_answer = 42 + +pub fn main() { + the_answer +} +``` + +Additionally, Gleam constants can be referenced from other modules. + +```gleam +// in file other_module.gleam +pub const the_answer: Int = 42 +``` + +```gleam +import other_module + +fn main() { + other_module.the_answer +} +``` + +## Blocks + +#### JavaScript + +In JavaScript statements can be grouped together using braces `{` `}`. These +blocks are usually associated with specific language constructs like functions, +conditionals, loops, etc. The only way to create multi-line expression blocks +like in Gleam is using an immediately-invoked function expression (IIFE). + +Parentheses `(` `)` are used to group arithmetic expressions. + +```javascript +function main() { + // x gets assigned the result of the IIFE + const x = (() => { + console.log(1); + return 2; + })(); + const y = x * (x + 10); // parentheses are used to change arithmetic operations order + return y; +} +``` + +#### Gleam + +In Gleam curly braces, `{` and `}`, are used to group expressions. + +```gleam +pub fn main() { + let x = { + print(1) + 2 + } + // Braces are used to change arithmetic operations order + let y = x * { x + 10 } + y +} +``` + +Unlike in JavaScript, 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. + +## Data types + +### Strings + +#### JavaScript + +In JavaScript strings are sequences of UTF-16 code units, and can be delimited +by single quotes `'`, double quotes `"`, or backticks `. +Strings using backticks support interpolation. + +```javascript +const world = 'world'; +"Hellø, world!" +`Hello, ${world}!` +``` + +#### Gleam + +In Gleam strings are encoded as UTF-8 binaries, and must be delimited by double +quotes. Gleam strings do not allow interpolation, yet. Gleam however offers a +`string_builder` via its standard library for performant string building. + +```gleam +"Hellø, world!" +``` + +### Tuples + +Tuples are very useful in Gleam as they're the only collection data type that +allows mixed types in the collection. + +#### JavaScript + +JavaScript doesn't have a concept of tuples, but some tuple behavior can be +imitated using arrays. + +```javascript +const myArray = ["username", "password", 10]; +const [_, password] = myArray; +console.log(password); // "password" +// Direct index access +console.log(myArray[0]); // "username" +``` + +#### Gleam + +```gleam +let my_tuple = #("username", "password", 10) +let #(_, pwd, _) = my_tuple +io.print(pwd) // "password" +// Direct index access +io.print(my_tuple.0) // "username" +``` + +### Lists + +Arrays in JavaScript are allowed to be of mixed types, but not in Gleam. + +#### JavaScript + +JavaScript arrays are delimited by square brackets `[` `]`. The `...` operator +can insert one array into another. + +```javascript +let list = [2, 3, 4]; +list = [1, ...list, 3]; +const [firstElement, secondElement, ...rest] = list; +console.log(["hello", ...list]); // works +``` + +#### Gleam + +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. + +```gleam +let list = [2, 3, 4] +let list = [1, ..list] +let [1, second_element, ..] = list +[1.0, ..list] // compile error, type mismatch +``` + +An important difference between Gleam's "cons" operator and JavaScript's `...` +is that the `..tail` can only appear as the last item between the brackets. + +```gleam +let list = [2, 3, 4] +let list = [1, ..list] // works +let list = [1, 2, ..list] // still works +let list = [1, ..list, 5] // compile error +``` + +To store items of different types in a list, you need to create a custom type: + +```gleam +type Value { + Number(Int) + Text(String) + BinaryData(BitArray) +} + +let my_list = [BinaryData(<<"hello":utf8>>), Text("👋"), Number(5)] +``` + +### Dicts + +#### JavaScript + +In JavaScript, key–value pairs are usually stored in objects, whose keys can +only be strings, numbers, or symbols. There is also the `Map` class, which +allows any type to be used for keys. In both cases, types of keys and values can +be mixed in a given map. + +```javascript +const map1 = { + key1: "value1", + key2: 5, +}; + +const actualMap = new Map([ + ["key1", "value1"], + [9, 5], +]); +``` + +#### Gleam + +In a Gleam `dict`, the type for keys and the type for values are fixed. So, for +example, you can't have a dict with some `String` values and some `Int` values, +and you can't have a dict with some `String` keys and some `Int` keys. But you +can have a dict with `String` keys and `Int` values. + +There is no dict literal syntax in Gleam, and you cannot pattern match on a +dict. Maps are generally not used much in Gleam, custom types are more common. + +(You would usually translate a TypeScript `type` or `class` to a Gleam custom +type, and only use a Gleam `Map` for arbitrary key–value pairs, equivalent to a +TypeScript `Map` or `Record`.) + +```gleam +import gleam/dict + +dict.from_list([#("key1", "value1"), #("key2", "value2")]) +dict.from_list([#("key1", "value1"), #("key2", 2)]) // Type error! +``` + +## Flow control + +### Case + +`case` is one of the most used control flow methods 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, which Gleam +doesn't have. + +### Piping + +In JavaScript, method calls are easy to chain together: + +```javascript +"hello, world".toUpperCase().repeat(2).split(',') +``` + +Since Gleam doesn't have objects with methods, the equivalent code might look +like this: + +```gleam +import gleam/string + +string.split(string.repeat(string.uppercase("hello, world"), times: 2), ",") +``` + +To make this more readable, Gleam has the pipe operator `|>` + +```gleam +import gleam/string + +"hello, world" +|> string.uppercase +|> string.repeat(2) // defaults to piping into the first argument +|> string.split(_, ",") // you can use _ to specify the argument to pipe into +``` diff --git a/cheatsheets/gleam-for-php-users.md b/cheatsheets/gleam-for-php-users.md index 6dcdbc03..23a8a4d4 100644 --- a/cheatsheets/gleam-for-php-users.md +++ b/cheatsheets/gleam-for-php-users.md @@ -55,7 +55,7 @@ Multi line comments may be written like so: */ ``` -IN PHP, above `trait`, `interface`, `class`, `member`, `function` declarations +In PHP, above `trait`, `interface`, `class`, `member`, `function` declarations there can be `docblocks` like so: ```php @@ -512,7 +512,7 @@ are fully type checked. | 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** +| Concatenate | `.` | `<>` | In Gleam both values must be **String** | | Pipe | `->` | |> | Gleam's pipe can chain function calls. See note for PHP | ### Notes on operators @@ -520,8 +520,8 @@ are fully type checked. - For bitwise operators, which exist in PHP but not in Gleam, see: . - `==` is by default comparing by value in PHP: - - Types may be autocast to be compareable. - - Two objects with the same members values will equal: + - Types may be autocast to be comparable. + - Two objects with the same members values will equal. - `===` is for comparing by strict equality in PHP: - Types will not be autocast for comparison - Two objects with the same members will not equal. Only if a variable binds @@ -765,9 +765,9 @@ between floats and integers in various ways including `rounding`, `floor`, ### Case -Case is one of the most used control flow 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. +Case is one of the most used control flow methods 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 @@ -1293,7 +1293,7 @@ pub fn main() { #### PHP PHP features ways to load arbitrary PHP code: `require`, `include` and -autoload such as `spl_autoload_register`. Once class pathes are known 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 . @@ -1443,12 +1443,12 @@ To iterate a few foundational differences: or the memory limit is exceeded. - Gleam on Erlang/BEAM allows to processes requests in a similar isolation level that PHP offers in contrast to applications running *Go* or *Ruby*. - The level of isoluation means that, very similar to PHP, if a process + The level of isolation means that, very similar to PHP, if a process crashes (in PHP read: if a request crashes) then the supervision system can restart that process or after a while or amount of tries abort repeating restarts on the process with that given input data. This means Erlang/BEAM will yield similar robustness that PHP developers are used - to and similar isolation guarantuees. + to and similar isolation guarantees. - When executing Gleam code in fact its compiled Erlang or JavaScript is executed. So in case there are runtime crashes, the crash log will show Erlang (or browser-console/NodeJS/Deno) debug information. In Gleam diff --git a/documentation.md b/documentation.md index 7617ee38..2327191e 100644 --- a/documentation.md +++ b/documentation.md @@ -47,6 +47,7 @@ layout: page - [Gleam for Elixir users](/cheatsheets/gleam-for-elixir-users) - [Gleam for Elm users](/cheatsheets/gleam-for-elm-users) - [Gleam for Erlang users](/cheatsheets/gleam-for-erlang-users) +- [Gleam for JavaScript users](/cheatsheets/gleam-for-javascript-users) - [Gleam for PHP users](/cheatsheets/gleam-for-php-users) - [Gleam for Python users](/cheatsheets/gleam-for-python-users) - [Gleam for Rust users](/cheatsheets/gleam-for-rust-users)