├── .github └── workflows │ └── test.yml ├── .gitignore ├── CNAME ├── LICENCE ├── README.md ├── gleam.toml ├── manifest.toml ├── posts ├── auto-imports-and-tolerant-expressions.djot ├── context-aware-compilation.djot ├── convenient-code-actions.djot ├── developer-survey-2022-results.djot ├── developer-survey-2024-results.djot ├── developer-survey-2024.djot ├── fault-tolerant-gleam.djot ├── gleam-gets-rename-variable.djot ├── gleam-javascript-gets-30-percent-faster.djot ├── gleam-v0.10-released.djot ├── gleam-v0.11-released.djot ├── gleam-v0.12-and-gleam-otp-v0.1-released.djot ├── gleam-v0.13-released.djot ├── gleam-v0.14-released.djot ├── gleam-v0.15-released.djot ├── gleam-v0.17-released.djot ├── gleam-v0.18-released.djot ├── gleam-v0.19-released.djot ├── gleam-v0.20-released.djot ├── gleam-v0.22-released.djot ├── gleam-v0.23-released.djot ├── gleam-v0.24-released.djot ├── gleam-v0.3-released.djot ├── gleam-v0.4-released.djot ├── gleam-v0.5-released.djot ├── gleam-v0.6-released.djot ├── gleam-v0.7-released.djot ├── gleam-v0.8-released.djot ├── gleam-v0.9-released.djot ├── gleam-v1.1.djot ├── gleam-version-1.djot ├── gleams-new-interactive-language-tour.djot ├── global-rename-and-find-references.djot ├── hello-echo-hello-git.djot ├── hello-gleam.djot ├── improved-performance-and-publishing.djot ├── introducing-the-gleam-package-index.djot ├── introducing-the-gleam-roadmap.djot ├── supercharged-labels.djot ├── v0.16-gleam-compiles-to-javascript.djot ├── v0.21-introducing-the-gleam-language-server.djot ├── v0.25-introducing-use-expressions.djot ├── v0.26-incremental-compilation-and-deno.djot ├── v0.27-hello-panic-goodbye-try.djot ├── v0.28-monorepos-fast-maps-and-more.djot ├── v0.29-gleam-gets-autocompletion.djot ├── v0.30-local-dependencies-and-enhanced-externals.djot ├── v0.31-keeping-dependencies-explicit.djot ├── v0.32-polishing-syntax-for-stability.djot ├── v0.33-exhaustive-gleam.djot ├── v0.34-multi-target-projects.djot └── welcome-lambda.djot ├── priv ├── fonts │ ├── CascadiaMono.woff2 │ ├── Lexend-700.woff2 │ ├── Lexend.woff2 │ └── Outfit.woff ├── funding.json ├── images │ ├── community │ │ ├── discord.svg │ │ └── github.svg │ ├── date-icon.svg │ ├── gleam-charcoal.svg │ ├── gleam-pink.svg │ ├── lucy-charcoal-2.svg │ ├── lucy-charcoal-3.svg │ ├── lucy-charcoal-5.svg │ ├── lucy-charcoal-6.svg │ ├── lucy-charcoal.svg │ ├── lucy-circle.svg │ ├── lucy-pink-2.svg │ ├── lucy-pink-3.svg │ ├── lucy-pink.svg │ ├── lucy-ponder.svg │ ├── lucy │ │ ├── lucy.svg │ │ ├── lucyace.svg │ │ ├── lucybi.svg │ │ ├── lucydebugfail.svg │ │ ├── lucydunno.svg │ │ ├── lucyelixir.svg │ │ ├── lucyerl.svg │ │ ├── lucyflake.svg │ │ ├── lucygay.svg │ │ ├── lucyglow.svg │ │ ├── lucygodot.svg │ │ ├── lucyhappy.svg │ │ ├── lucyjs.svg │ │ ├── lucylesbian.svg │ │ ├── lucymail.svg │ │ ├── lucynix.svg │ │ ├── lucynull.svg │ │ ├── lucypan.svg │ │ ├── lucypinkbg.svg │ │ ├── lucypride.svg │ │ ├── lucypridebg.svg │ │ ├── lucyrust.svg │ │ ├── lucysapphic.svg │ │ ├── lucysleep.svg │ │ ├── lucytiny-plain.svg │ │ ├── lucytiny.svg │ │ ├── lucytrans.svg │ │ ├── lucywasm.svg │ │ └── superlucy.svg │ ├── news │ │ ├── gleam-v0.14-released │ │ │ └── night-mode.png │ │ ├── gleam-v0.20-released │ │ │ └── github-syntax-highlighting.png │ │ ├── gleam-v0.21-released │ │ │ ├── compile.mp4 │ │ │ ├── compile.webm │ │ │ ├── formatting.mp4 │ │ │ ├── formatting.webm │ │ │ ├── goto-definition.mp4 │ │ │ ├── goto-definition.webm │ │ │ ├── hover.mp4 │ │ │ ├── hover.webm │ │ │ └── lsp-languages-editors.png │ │ ├── gleam-v0.23-released │ │ │ ├── docs-search.mp4 │ │ │ └── docs-search.webm │ │ ├── gleam-v0.26-released │ │ │ └── deno_hr_circle.svg │ │ ├── gleam-v0.28-released │ │ │ └── docs-hover.png │ │ ├── gleam-v0.29-released │ │ │ └── autocompletion.png │ │ ├── gleam-v0.34-released │ │ │ ├── goto-docs.png │ │ │ └── multi-target.gif │ │ ├── gleam-v0.7-released │ │ │ ├── import-cycle.png │ │ │ ├── incorrect-number-of-patterns.png │ │ │ └── type-error.png │ │ ├── gleam-v1.1-released │ │ │ ├── imports.png │ │ │ ├── spread.png │ │ │ ├── tuple.png │ │ │ └── unused.png │ │ ├── gleam-v1.4-released │ │ │ └── help.mp4 │ │ ├── gleams-new-interactive-language-tour │ │ │ └── browser.png │ │ └── welcome-lambda │ │ │ └── champions.mp4 │ ├── pride-wall.svg │ ├── return-icon.svg │ ├── share-icon.svg │ ├── social-image.png │ ├── sponsor-heart.svg │ ├── sponsors │ │ ├── alembic.svg │ │ ├── fly-invert.svg │ │ ├── fly.svg │ │ ├── lambda-class-black.png │ │ ├── lambda-class-white.png │ │ ├── memo.png │ │ └── nine-fx.png │ ├── user-icon.svg │ └── waves.svg ├── img │ ├── gleam-0-13-docs-link.jpg │ ├── gleam-0-13-docs-version.jpg │ ├── gleam-charcoal.svg │ ├── gleam-pink.svg │ ├── lucy-charcoal-2.svg │ ├── lucy-charcoal-3.svg │ ├── lucy-charcoal-5.svg │ ├── lucy-charcoal-6.svg │ ├── lucy-charcoal.svg │ ├── lucy-pink-3.svg │ └── lucy-pink.svg ├── javascript │ ├── main.js │ └── plausible.js └── styles │ └── main.css ├── src ├── website.gleam └── website │ ├── atom_feed.gleam │ ├── cheatsheet.gleam │ ├── command_line_reference.gleam │ ├── fs.gleam │ ├── language_server.gleam │ ├── news.gleam │ ├── page.gleam │ ├── roadmap.gleam │ ├── site.gleam │ └── sponsor.gleam └── test └── website_test.gleam /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: erlef/setup-beam@v1 15 | with: 16 | otp-version: "27.1.2" 17 | gleam-version: "1.9.1" 18 | rebar3-version: "3" 19 | - run: gleam deps download 20 | - run: gleam test 21 | - run: gleam run 22 | - run: gleam format --check src test 23 | - uses: actions/upload-pages-artifact@v3 24 | with: 25 | path: ./dist 26 | if: github.ref == 'refs/heads/main' 27 | 28 | deploy: 29 | if: github.ref == 'refs/heads/main' 30 | needs: build 31 | runs-on: ubuntu-latest 32 | permissions: 33 | contents: read 34 | pages: write 35 | id-token: write 36 | environment: 37 | name: github-pages 38 | steps: 39 | - name: Deploy to GitHub Pages 40 | id: deployment 41 | uses: actions/deploy-pages@v4 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.beam 2 | *.ez 3 | /build 4 | _site 5 | dist 6 | erl_crash.dump 7 | secrets.env 8 | .envrc 9 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | gleam.run 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Gleam website 2 | 3 | https://gleam.run 4 | 5 | ```sh 6 | # Build the website to the `dist` directory 7 | gleam run 8 | ``` 9 | -------------------------------------------------------------------------------- /gleam.toml: -------------------------------------------------------------------------------- 1 | name = "website" 2 | version = "1.0.0" 3 | gleam = ">= 1.9.0" 4 | target = "erlang" 5 | 6 | [dependencies] 7 | gleam_stdlib = ">= 0.44.0 and < 2.0.0" 8 | gleam_crypto = ">= 1.4.0 and < 2.0.0" 9 | gleam_time = ">= 1.1.0 and < 2.0.0" 10 | simplifile = ">= 2.2.0 and < 3.0.0" 11 | filepath = ">= 1.1.1 and < 2.0.0" 12 | contour = ">= 1.1.1 and < 2.0.0" 13 | lustre = ">= 4.6.4 and < 5.0.0" 14 | gleave = ">= 1.0.0 and < 2.0.0" 15 | snag = ">= 1.1.0 and < 2.0.0" 16 | jot = ">= 4.0.0 and < 5.0.0" 17 | atomb = ">= 1.0.0 and < 2.0.0" 18 | argv = ">= 1.0.2 and < 2.0.0" 19 | envoy = ">= 1.0.2 and < 2.0.0" 20 | gleam_http = ">= 4.0.0 and < 5.0.0" 21 | gleam_json = ">= 2.3.0 and < 3.0.0" 22 | gleam_httpc = ">= 4.1.0 and < 5.0.0" 23 | just = ">= 1.1.0 and < 2.0.0" 24 | 25 | [dev-dependencies] 26 | gleeunit = ">= 1.0.0 and < 2.0.0" 27 | -------------------------------------------------------------------------------- /posts/developer-survey-2024.djot: -------------------------------------------------------------------------------- 1 | [survey]: https://developer-survey.gleam.run/ 2 | [2022]: https://gleam.run/news/developer-survey-2022-results/ 3 | 4 | The [Gleam Developer Survey 2024][survey] is here! 5 | 6 | The last one was run in 2022 (see the results [here][2022]), but a lot has 7 | happened since then, so it's high-time for another one. 8 | 9 | As well as being fun to learn about the community, the results of this survey 10 | will shape what features and improvements the core team will work on next year. 11 | This year the focus was on the language server, and that has been a tremendous 12 | success, so be sure to fill in the survey and share it with your friends so we 13 | can have have an equally fantastic 2025. 14 | 15 | You don't need to be an active Gleam user to fill in the survey. If you're not 16 | yet found the time to try Gleam we still want to hear from you! 17 | 18 | ``` =html 19 |
20 | 21 | The Gleam Developer Survey 22 | 23 |
24 | ``` 25 | -------------------------------------------------------------------------------- /posts/gleam-v0.3-released.djot: -------------------------------------------------------------------------------- 1 | [Gleam](https://github.com/lpil/gleam) has reached v0.3! Let's take a look at 2 | some of the new features introduced since v0.1. 3 | 4 | ## Module namespaces 5 | 6 | Modules can now be namespaced, allowing use of short, convenient module names 7 | without the risk of collisions with other Gleam or Erlang modules. 8 | 9 | To use a namespace create a directory with the name of your namespace inside 10 | the `src/` directory and put your modules in there. As an example, if I'm 11 | making the hot new food delivery app `snackr` I might choose to have a 12 | directory structure like this: 13 | 14 | ``` 15 | src/ 16 | └── snackr 17 |    ├── customer.gleam 18 |    ├── payment.gleam 19 |    └── food 20 |       ├── falafel.gleam 21 |       ├── pizza.gleam 22 |       └── salad.gleam 23 | ``` 24 | 25 | Once created these modules can be brought into scope using the `import` 26 | statement. 27 | 28 | ```gleam 29 | import snackr/customer 30 | import snackr/food/falafel 31 | 32 | fn eat_lunch(louis) { 33 | customer:eat(louis, falafel:new()) 34 | } 35 | ``` 36 | 37 | It's now possible to use multiple modules with the same name, so for 38 | convenience modules can be given a name when imported. 39 | 40 | ```gleam 41 | import space/rocket as spaceship 42 | import salad/rocket as leafy_green 43 | ``` 44 | 45 | 46 | ## Type annotations 47 | 48 | Gleam's type inference doesn't require any annotations to understand the types 49 | of all your code and provide full type safety, but we might still want to add 50 | some for documentation, or to specify more restrictive types then Gleam might 51 | otherwise infer. 52 | 53 | It looks a little something like this: 54 | 55 | ```gleam 56 | fn is_empty(list: List(a)) -> Bool { 57 | list == [] 58 | } 59 | ``` 60 | 61 | 62 | ## Easier installation 63 | 64 | Precompiled Gleam binaries for Linux and OSX can now be downloaded from the 65 | [Gleam GitHub release page](https://github.com/lpil/gleam/releases) so it's no 66 | longer required to install Rust on your machine and build the compiler from 67 | source. 68 | 69 | Victor Borja has kindly created an `asdf` version manager [plugin for 70 | Gleam](https://github.com/vic/asdf-gleam), so for asdf users installing Gleam 71 | is as easy as `asdf install gleam v0.3.0`. 72 | 73 | For Docker fans the Gleam compiler has been packaged as a Docker image tagged 74 | as [`lpil/gleam`](https://hub.docker.com/r/lpil/gleam). 75 | 76 | 77 | ## Easier project creation 78 | 79 | Previously Gleam projects were created using a rebar3 plugin. This worked but 80 | was one more thing to install, and there was to good way of ensuring that you 81 | have an up to date version of the plugin to match your Gleam installation. 82 | 83 | Now project creation is handled by the `gleam` binary itself, so to get 84 | started with Gleam run `gleam new my_cool_project` and it'll generate all 85 | that's required. 86 | 87 | If you've got `rebar_gleam` installed it's safe to delete it from your 88 | `~/.config/rebar3/rebar.config` as it's no longer used. 89 | 90 | 91 | ## The rest 92 | 93 | In addition to these features there's been a number of other improvements to 94 | the quality of the error messages, the generated code, and a handful of bug 95 | fixes. 😊 96 | 97 | If you want to try out the new version of Gleam head over to the [installation 98 | page](https://gleam.run/getting-started/installing-gleam.html). I'd love to 99 | hear how you find it and get your feedback so Gleam can continue to improve. 100 | 101 | 102 | ## Code BEAM lite Berlin 103 | 104 | On the 11th of October 2019 I'll be speaking at [Code BEAM lite 105 | Berlin](https://codesync.global/conferences/code-beam-lite-berlin-2019/) about 106 | Gleam. If you're attending (or are in Berlin but not attending) say Hello! 107 | -------------------------------------------------------------------------------- /posts/gleam-v0.4-released.djot: -------------------------------------------------------------------------------- 1 | [Gleam](https://github.com/lpil/gleam) has reached v0.4! This version has some 2 | new features, bug fixes, and some breaking changes. Let's take a look. 3 | 4 | 5 | ## Structs 6 | 7 | Gleam's map and tuple type has been removed and replaced with structs, which 8 | have many of the benefits of both. 9 | 10 | Like maps structs are collections of named values, each with their own type. 11 | Unlike maps they are named, so two structs are not the same type just because 12 | they have the same fields by coincidence, making Gleam's type system a little 13 | stricter by default. 14 | 15 | Like tuples structs have constant access time, they do not need to be linearly 16 | scanned in order to find a field to access it. 17 | 18 | Unlike either struct types are declared up-front by the `struct` keyword. 19 | 20 | ```gleam 21 | pub struct Cat { 22 | name: String 23 | cuteness: Int 24 | } 25 | ``` 26 | 27 | Once declared the newly defined constructor can be used to create instances of 28 | the struct. 29 | 30 | ```gleam 31 | pub fn cats() { 32 | let cat1 = Cat(name: "Nubi", cuteness: 2001) 33 | let cat2 = Cat(cuteness: 1805, name: "Biffy") 34 | 35 | // Alternatively fields can be given without labels 36 | let cat3 = Cat("Ginny", 1950) 37 | 38 | [cat1, cat2, cat3] 39 | } 40 | ``` 41 | 42 | One downside to structs is that they are less convenient for returning more 43 | than one value from a function than tuples were, as struct types need to be 44 | pre-declared. To negate this inconvenience the standard library declares `Pair` 45 | and `Triple` types in the `gleam/pair` and `gleam/triple` modules. If you wish 46 | to return more than 3 values from a function it is recommended to create a 47 | struct and name the values to make it more clear what they are. 48 | 49 | 50 | ## Comparison operators 51 | 52 | The next breaking change is to how the comparison operators `>`, `>=`, `<`, 53 | and `<=` work. Previously these operators would take any two values of the 54 | same type and determine which is larger according to Erlang's ordering of 55 | values. This is convenience but may result in surprising behaviour when used 56 | with custom types such as enums. 57 | 58 | For example, with the `Order` enum we would expect `Gt` (greater than) to be 59 | larger than `Lt` (less than), but according to Erlang's value ordering this is 60 | not the case. 61 | 62 | ```gleam 63 | enum Order = 64 | | Gt 65 | | Eq 66 | | Lt 67 | ``` 68 | ```gleam 69 | Gt > Lt // => False 70 | ``` 71 | 72 | From this version on `>`, `>=`, `<`, and `<=` only accept Ints as arguments 73 | to avoid this surprising behaviour, and the `>.` `>=.` `<.` and `<=.` 74 | comparison operators have been added for comparing Floats. 75 | 76 | For ordering other types comparison functions can be found in the standard 77 | library, such as `order.compare` and `bool.compare`. 78 | 79 | 80 | ## Second class modules 81 | 82 | Modules are no longer first class values in Gleam, meaning they can no longer 83 | be assigned to variables or used as arguments to functions. 84 | 85 | First class modules were inspired by OCaml and intended to be a way to work 86 | with Erlang _behaviours_ such as `gen_stage` from within Gleam. However after 87 | several months of using modules in Gleam it became clear that taking OCaml's 88 | first class modules but not their functor module system resulted in Gleam's 89 | modules being far less useful. 90 | 91 | First class functions combined with Gleam's other data structures is 92 | sufficient to use Erlang behaviours, while being easier to construct and 93 | compose, so first class modules were determined not to be the ideal solution 94 | here either. 95 | 96 | With first class modules removed we no longer need a special syntax for 97 | accessing a field on a module, so the `:` operator has been removed and 98 | replaced with the `.` operator, making it the universal operator for accessing 99 | a named field on a module or container data type in Gleam. 100 | 101 | ```gleam 102 | import gleam/list 103 | 104 | pub fn main() { 105 | list.reverse([1, 2, 3]) 106 | } 107 | ``` 108 | 109 | With the removal of modules and maps Gleam's type system has become simpler, 110 | less structural and more nominal in style. This puts us in a good position to 111 | do research into new forms of polymorphism such as type classes or traits! 112 | 113 | 114 | ## The rest 115 | 116 | In addition to these features there's been a number of other improvements to 117 | the quality of the error messages, the generated code, and a handful of bug 118 | fixes. 😊 119 | 120 | If you want to try out the new version of Gleam head over to the [installation 121 | page](https://gleam.run/getting-started/installing-gleam.html). I'd love to 122 | hear how you find it and get your feedback so Gleam can continue to improve. 123 | 124 | 125 | ## Code BEAM lite Berlin 126 | 127 | On the 11th of October 2019 I'll be speaking at [Code BEAM lite 128 | Berlin](https://codesync.global/conferences/code-beam-lite-berlin-2019/) about 129 | Gleam. If you're attending (or want to recommend to me good things to do on a 130 | weekend in Berlin) say Hello! 131 | 132 | 133 | ## Thanks 134 | 135 | Lastly, a huge thank you to the code contributors to this release! 136 | 137 | - [Al Dee](https://github.com/scripttease) 138 | - [Benjamin Reynolds](https://github.com/benreyn) 139 | - [Dirk Gadsden](https://github.com/dirk) 140 | - [epj009](https://github.com/epj009) 141 | -------------------------------------------------------------------------------- /posts/gleam-v0.6-released.djot: -------------------------------------------------------------------------------- 1 | Merry Christmas! Following in the Ruby tradition we're having a release on 2 | Christmas day, and so [Gleam](https://github.com/gleam-lang/gleam) v0.6 is here! 3 | 4 | This release has one big change to how we define data structures in Gleam. To 5 | understand it lets take a look at how structs and enums have worked up until 6 | this point. 7 | 8 | ```gleam 9 | struct Cat { 10 | name: String 11 | age: Int 12 | } 13 | ``` 14 | 15 | Structs are collections of named values. Under the hood they are represented 16 | as Erlang tuples, so `Cat("Nubi", 2)` would be `{<<"Nubi">>, 2}` in Erlang. 17 | 18 | ```gleam 19 | enum SchoolPerson { 20 | Teacher(String, Subject) 21 | Student(String) 22 | } 23 | ``` 24 | 25 | Enum are also collections of values, only this time the values are not named. 26 | One way in which enums are more capable than structs is that they can have 27 | more than one constructor, so for example a SchoolPerson enum type can be 28 | either a Teacher or a Student. 29 | 30 | Like structs enums are tuples at runtime, only they have their constructor 31 | name as the first element to help distinguish them, so `Student("Louis")` 32 | would be `{student, <<"Louis">>}` in Erlang. This atom tag makes enums 33 | compatible with Erlang records, allowing easier use within projects written in 34 | Erlang or Elixir. 35 | 36 | So the differences between structs and enums were: 37 | 38 | 1. Struct fields can have names. 39 | 2. Enums are Erlang record compatible. 40 | 3. Enums can have more than one constructor. 41 | 42 | In v0.5 things changed a little. 43 | 44 | It turned out that being able to label fields so they have names was useful, 45 | so enums gained labelled arguments (as did functions). 46 | 47 | It turns out that being Erlang record compatible with a tag atom was useful 48 | for Erlang interop and for runtime debugging, so structs got this too. 49 | 50 | At which point the only difference between enums and structs are the number of 51 | constructors, and there's nothing stopping someone from defining an enum with 52 | a single constructor. 53 | 54 | Structs became a subset of enums, so why have both? 55 | 56 | 57 | ## Custom types 58 | 59 | With v0.6 rather than structs and enums we have _custom types_. Here's how the 60 | two examples above would be written using the new syntax: 61 | 62 | ```gleam 63 | type Cat { 64 | Cat(name: String, age: Int) 65 | } 66 | ``` 67 | Cat is a custom type has a single constructor called Cat. 68 | 69 | ```gleam 70 | type SchoolPerson { 71 | Teacher(name: String, class: Subject) 72 | Student(name: String) 73 | } 74 | ``` 75 | 76 | SchoolPerson is a custom type has a Teacher constructor and a Student constructor. 77 | 78 | ```gleam 79 | let louis = Student(name: "Louis") 80 | ``` 81 | 82 | In keeping with Erlang and Elixir an instance of one of these custom types is 83 | called a record. The variable `louis` is a Student record, and it has the type 84 | SchoolPerson. 85 | 86 | This change makes the languages simpler (both to the user and within the 87 | compiler), and hopefully it'll make Gleam a little easier to learn too. :) 88 | 89 | 90 | ## Tuples 91 | 92 | After this change `struct` is no longer a keyword, and it doesn't make much 93 | sense to have a concept of anonymous structs without named structs, so 94 | anonymous structs have been renamed _tuples_. 95 | 96 | ```gleam 97 | let pair = tuple(1, "two") 98 | ``` 99 | 100 | 101 | ## The rest 102 | 103 | There's been a number a smattering of bug fixes in this release, see the 104 | [changelog](https://github.com/gleam-lang/gleam/blob/master/CHANGELOG.md) for 105 | further details. 106 | 107 | If you want to try out the new version of Gleam head over to the [installation 108 | page][installation]. I'd love to hear how you find it and get your feedback so 109 | Gleam can continue to improve. 110 | 111 | Want to view some existing Gleam projects? Head on over to the 112 | [awesome-gleam][awesome-gleam] list. Looking for something to build in 113 | Gleam? Check out [the suggestions tracker][suggestions]. 114 | 115 | [awesome-gleam]: https://github.com/gleam-lang/awesome-gleam 116 | [suggestions]: https://github.com/gleam-lang/suggestions/issues 117 | [installation]: https://gleam.run/getting-started/installing-gleam.html 118 | 119 | 120 | ## Thanks 121 | 122 | Lastly, a huge thank you to the contributors to and sponsors of Gleam since 123 | last release! 124 | 125 | - [Adam Brodzinski](https://github.com/AdamBrodzinski) 126 | - [Christian Wesselhoeft](https://github.com/xtian) 127 | - [Devon Estes](https://github.com/devonestes) 128 | - [John Palgut](https://github.com/Jwsonic) 129 | - [Matt Widmann](https://github.com/mgwidmann) 130 | - [Michał Łępicki](https://github.com/michallepicki) 131 | - [OvermindDL1](https://github.com/OvermindDL1) 132 | - [RJ Dellecese](https://github.com/rjdellecese) 133 | - [Sebastian Porto](https://github.com/sporto) 134 | - [Stefan Hagen](https://github.com/sthagen) 135 | - [ontofractal](https://github.com/ontofractal) 136 | - [Štefan Ľupták](https://github.com/EskiMag) 137 | 138 | 139 | If you would like to help make strongly typed programming on the Erlang 140 | virtual machine a production-ready reality please consider [sponsoring 141 | Gleam][sponsor] via the GitHub Sponsors program. 142 | 143 | Thank you! And have a fantastic new year! 💜 144 | 145 | [sponsor]: https://github.com/sponsors/lpil 146 | -------------------------------------------------------------------------------- /posts/gleam-v0.7-released.djot: -------------------------------------------------------------------------------- 1 | It has been two months since the last one, so now it's time for another Gleam 2 | release! Let's see what's new this time: 3 | 4 | ## Improved error messages 5 | 6 | Since the last release we've put a lot of work into the error messages emitted 7 | by the compiler. The most common and arguably the most important type of error 8 | is the type error, so we want it to be as clear and as friendly as possible. 9 | 10 | Here's what it looks like with Gleam v0.7: 11 | 12 | ``` =html 13 | A terminal showing a type error message 14 | ``` 15 | 16 | This is a big improvement over previous versions which would struggle with 17 | highlighting multi-line code sections, and would state the problem as being 18 | `Int != Float` rather than `List(Int) != List(Float)`. 19 | 20 | Other improved error messages include the import cycle error message: 21 | 22 | ``` =html 23 | A terminal showing an import cycle error message 24 | ``` 25 | 26 | Error messages for invalid patterns in case expressions: 27 | 28 | ``` =html 29 | A terminal showing a pattern matching error message 30 | ``` 31 | 32 | And a slew of error messages relating to errors when trying to read or write 33 | files in a Gleam project. 34 | 35 | Gleam aims to be an exceptionally friendly and productive language, so error 36 | messages are important. If there are any errors that you find confusing or 37 | unclear please get in touch and let us know! 38 | 39 | In adding these new error messages we discovered a bug in rebar3 where unicode 40 | characters would be printed incorrectly in some circumstances. This bug is now 41 | fixed now but if you find your errors look incorrect please try upgrading 42 | your rebar3 version by running `rebar3 local upgrade`. 43 | 44 | 45 | ## Alternative patterns 46 | 47 | Sometimes when writing case expressions we want multiple different clauses to 48 | be able to trigger the same clause with different shaped data. To meet this 49 | need we're introducing alternative patterns, separated by the `|` operator. 50 | 51 | In this case expression the first clause will match if the variable `number` 52 | holds 2, 4, 6 or 8. 53 | 54 | ```gleam 55 | case number { 56 | 2 | 4 | 6 | 8 -> "This is an even number" 57 | 1 | 3 | 5 | 7 -> "This is an odd number" 58 | _ -> "I'm not sure" 59 | } 60 | ``` 61 | 62 | 63 | ## Clause guards 64 | 65 | Sometimes when pattern matching we want to assert that two variables have the 66 | same value. Before this release we'd need to nested case expressions, now we 67 | have clause guards. 68 | 69 | ```gleam 70 | case xs { 71 | [a, b] if a == b -> "Two equal elements" 72 | [a, b] if a != b -> "Two unequal elements" 73 | _other -> "Something else" 74 | } 75 | ``` 76 | 77 | 78 | ## Named sub-patterns 79 | 80 | Sometimes when pattern matching we want to assign a name to a value while 81 | specifying it's shape at the same time. With this release we can now do this 82 | using the `as` keyword. 83 | 84 | ```gleam 85 | case xs { 86 | [[_, _] as inner_list] -> inner_list 87 | other -> [] 88 | } 89 | ``` 90 | 91 | 92 | ## Type aliases 93 | 94 | Sometimes it can be tiring writing long type names in function annotations 95 | such as `Result(List(tuple(String, String)), Nil)`, often we'd rather write a 96 | short alias instead such as `Option(Headers)`. With type aliases this is now 97 | possible. 98 | 99 | 100 | ```gleam 101 | pub type Option(value) = 102 | Result(value, Nil) 103 | 104 | pub type Headers = 105 | List(tuple(String, String)) 106 | 107 | pub fn headers() -> Option(Headers) { 108 | Ok([tuple("x-served-by", "Gleam")]) 109 | } 110 | ``` 111 | 112 | The `Option(value)` alias above is often pretty useful! If you want to use it 113 | it is defined in the `gleam/result` module of the Gleam standard library. 114 | 115 | 116 | ## Windows support 117 | 118 | The Gleam compiler's test suite is now running successfully on Windows, and a 119 | Windows binary is being compiled for new releases. If you're developing on 120 | Windows please do give it a try and let us know of any problems you encounter. 121 | 122 | 123 | ## The rest 124 | 125 | In addition to these changes there's been a bunch of bug fixes, performance 126 | improvements, and refactorings to the compiler. Please see the 127 | [changelog](https://github.com/gleam-lang/gleam/blob/master/CHANGELOG.md) for 128 | more detail. 129 | 130 | If you want to try out the new version of Gleam head over to the [installation 131 | page][installation]. I'd love to hear how you find it and get your feedback so 132 | Gleam can continue to improve. 133 | 134 | Want to view some existing Gleam projects? Head on over to the 135 | [awesome-gleam][awesome-gleam] list. Looking for something to build in 136 | Gleam? Check out [the suggestions tracker][suggestions]. 137 | 138 | [awesome-gleam]: https://github.com/gleam-lang/awesome-gleam 139 | [suggestions]: https://github.com/gleam-lang/suggestions/issues 140 | [installation]: https://gleam.run/getting-started/installing-gleam.html 141 | 142 | 143 | ## Thanks 144 | 145 | Lastly, a huge thank you to the contributors to and sponsors of Gleam since 146 | last release! 147 | 148 | - [Ahmad Sattar](https://github.com/thehabbos007) 149 | - [Anthony Bullard](https://github.com/gamebox) 150 | - [BSKY](https://github.com/imbsky) 151 | - [Christian Wesselhoeft](https://github.com/xtian) 152 | - [Guilherme Pasqualino](https://github.com/ggpasqualino) 153 | - [JHZheng](https://github.com/zjhmale) 154 | - [John Palgut](https://github.com/Jwsonic) 155 | - [RJ Dellecese](https://github.com/rjdellecese) 156 | - [Stefan Hagen](https://github.com/sthagen) 157 | - [ontofractal](https://github.com/ontofractal) 158 | - [szTheory](https://github.com/szTheory) 159 | 160 | 161 | If you would like to help make strongly typed programming on the Erlang 162 | virtual machine a production-ready reality please consider [sponsoring 163 | Gleam][sponsor] via the GitHub Sponsors program. 164 | 165 | Thanks for reading! Have fun! 💜 166 | 167 | [sponsor]: https://github.com/sponsors/lpil 168 | -------------------------------------------------------------------------------- /posts/gleams-new-interactive-language-tour.djot: -------------------------------------------------------------------------------- 1 | Gleam is a type safe and scalable language for the Erlang virtual machine and 2 | JavaScript runtimes. Today Gleam's new language tour has been launched, a way to 3 | try and to learn Gleam without having to install anything on your computer. 4 | Check it out, it looks like this: 5 | 6 | ``` =html 7 | A Gleam project being created and compiled with dependencies from both targets 8 | ``` 9 | 10 | The language tour guides you through the language, from the basics to the most 11 | advanced features, introducing each concept in a way that builds on what has 12 | come before. Gleam is a small and consistent language, designed to be as easy to 13 | learn and predictable as possible, so the tour manages to cover the entire 14 | language while still being a relatively short read. Once you've completed the 15 | tour you'll know everything you need to know to write your own Gleam programs, 16 | or make contributions to existing ones. 17 | 18 | The examples and any code you write are compiled and execute entirely 19 | within the browser, rather sending the code to a build server for processing. 20 | This coupled with Gleam's fast compilation speed means that the tour is 21 | super-snappy. You can experiment and try things without getting bogged down 22 | waiting for the results to come back. 23 | 24 | We're super happy with how this work has turned out, and we're hoping that this 25 | will make it easier than ever for people to go from "Gleam sounds interesting" 26 | to "I can write code in Gleam!". 27 | 28 | Sound good? Find it at [https://tour.gleam.run/](https://tour.gleam.run/) ✨ 29 | 30 | 31 | ## How does it work? 32 | 33 | Gleam's [compiler][compiler] is written in Rust, and all input/output is 34 | provided to it via dependency injection. This means that it's possible to 35 | replace the command-line interface that reads and writes to files with an 36 | in-memory one which can be compiled to [WebAssembly][wasm] and run in the 37 | browser. 38 | 39 | [compiler]: https://github.com/gleam-lang/gleam/ 40 | [wasm]: https://webassembly.org/ 41 | 42 | Gleam compiles to either Erlang or JavaScript, so in the language tour 43 | JavaScript is used as the target, and this compiled code can be executed 44 | directly in the browser. Executing the code is a little tricky as Gleam outputs 45 | esmodules, which cannot run using JavaScript's `eval` function. 46 | Instead the compiled code is base64 encoded and dynamically imported as a data 47 | URL, after which the `main` function can be called. 48 | 49 | ```javascript 50 | const encoded = btoa(unescape(encodeURIComponent(code))); 51 | const module = await import("data:text/javascript;base64," + encoded); 52 | module.main(); 53 | ``` 54 | 55 | The other tricky part is that we need the code to be able to import and use 56 | modules from the standard library. Compiled Gleam code uses relative paths to 57 | import other Gleam modules. These paths get edited slightly in the tour to point 58 | to where we have a precompiled copy of the standard library, which the browser can 59 | download as needed. Another approach here would have been to include a 60 | JavaScript bundler in the browser, but this more lightweight approach works well 61 | for our needs. 62 | 63 | The compiler is fast, but it's important that the website and the browser 64 | remain responsive even on slower hardware, or if compilation is slow for some 65 | other unexpected reason. To avoid this problem compilation and execution is done 66 | in a different thread to the browser's UI using JavaScript [Web Workers][workers]. 67 | 68 | [workers]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers 69 | 70 | The [CodeFlask][codeflask] library is used to provide the code editor and syntax 71 | highlighting. This was selected as it's a lightweight and easy to use text 72 | editor that has just the features we need, and no more. 73 | 74 | [codeflask]: https://kazzkiq.github.io/CodeFlask 75 | 76 | The website is a static site that is deployed to [GitHub Pages][pages]. Rather 77 | than using any specific static site generator it is built using a small Gleam 78 | program that can be found [here](https://github.com/gleam-lang/language-tour). 79 | 80 | [pages]: https://pages.github.com/ 81 | 82 | 83 | ## All the thanks 84 | 85 | Thanks to [John Doneth](https://github.com/JohnDoneth) for his work getting the 86 | compiler to work within the browser, and to 87 | [Enderchief](https://github.com/Enderchief) for taking this further still. 88 | 89 | Thanks to [Hayleigh Thompson](https://github.com/hayleigh-dot-dev/) for her 90 | JavaScript witchcraft, including but not limited to helping us figure out what 91 | our options are for executing esmodules in the browser. 92 | 93 | Thanks to [Hazel Bachrach](https://github.com/hibachrach/) for testing the tour 94 | and suggesting use of a second thread, to keep the experience good for folks on 95 | all devices. 96 | 97 | Thanks to [Dillon Mulroy](https://github.com/dmmulroy), [Giacomo Cavalieri](https://github.com/giacomocavalieri/), [inoas](https://github.com/inoas/), and [jimpjorps](https://github.com/hunkyjimpjorps/) 98 | for giving feedback on the content of the tour, as well as unpicking my many 99 | typos and grammatical errors. 100 | 101 | This has been a long project, so I'm sure I've missed some important people. I'm 102 | very grateful for everyone's help, thank you. 103 | 104 | ## Support Gleam development 105 | 106 | If you like Gleam consider becoming a sponsor or asking your employer to 107 | [sponsor Gleam development](https://github.com/sponsors/lpil). I work full time 108 | on Gleam and your kind sponsorship is how I pay my bills. 109 | 110 | Thanks for reading! Happy hacking! 💜 111 | -------------------------------------------------------------------------------- /posts/hello-gleam.djot: -------------------------------------------------------------------------------- 1 | Gleam has reached v0.1! Happy first release-day Gleam! 2 | 3 | ## What's Gleam? 4 | 5 | Gleam is a functional programming language for writing maintainable and 6 | scalable concurrent systems. If you enjoy the actor based concurrency model 7 | and durable runtime of Erlang and Elixir, and the sound type system of 8 | OCaml, ReasonML, or Elm then I hope you will enjoy Gleam. 9 | 10 | It looks something like this: 11 | 12 | ```gleam 13 | pub enum User = 14 | | LoggedIn(String) 15 | | Guest 16 | 17 | pub fn check(user) { 18 | case user { 19 | | LoggedIn("Al") -> "Hi Al!" 20 | | LoggedIn(name) -> "Welcome back!" 21 | | Guest -> "Hello! Please log in" 22 | } 23 | } 24 | ``` 25 | 26 | ## What is it like? 27 | 28 | Gleam supports generics, algebraic data types, modules as a first-class 29 | data-type, and row typed maps to enable flexible and permissive function 30 | interfaces. 31 | 32 | The type system aims to provide compile time safety and easy refactoring 33 | without burdening the programmer with verbose annotations or boilerplate. 34 | Types are completely inferred, though future releases will allow programmers 35 | to add optional type annotations if desired. 36 | 37 | Gleam compiles to Erlang and runs on the battle proven 38 | Erlang virtual machine, making it suitable for writing massively concurrent 39 | systems that are fault tolerant and relatively easy to reason about. 40 | 41 | The Erlang ecosystem is full of great libraries, languages, and applications, 42 | so Gleam makes it straightforward to import functions written in other BEAM 43 | languages and use them in your program. Gleam also aims to be a good citizen, 44 | so any library or application written in Gleam can be used by any other BEAM 45 | language without fuss or performance overhead. 46 | 47 | 48 | ## What is it for? 49 | 50 | Thanks to its Erlang heritage Gleam excels at low latency, high concurrency, 51 | networked applications such as web application backends, databases, or message 52 | brokers. Erlang and Elixir are also highly suited for embedded applications, 53 | so perhaps Gleam will be useful there too. 54 | 55 | Gleam's type system gives it an edge in rapidly evolving problem spaces as it 56 | helps the programmer refactor quickly and safely by supplying precise and 57 | useful error messages until the change is fully applied. 58 | 59 | 60 | ## What is it not for? 61 | 62 | It doesn't run in a browser or on a mobile device, and doesn't have a graphics 63 | library, so it's not suited for GUI applications. It doesn't have the 64 | near-instant boot time and easy distribution of a native binary so it's not 65 | the best for command line applications. It isn't fast at crunching numbers so 66 | you won't use it for statistical analysis. 67 | 68 | 69 | ## Is it good? 70 | 71 | Yes, I think so. But then it would be silly if I say otherwise, wouldn't it? 72 | 73 | Perhaps don't put it into production just yet, but it's usable enough for pet 74 | projects and toy applications. 🚀 75 | 76 | 77 | ## Sounds interesting. What now? 78 | 79 | Check out [the website](http://gleam.run) for more information, and check out 80 | the project on [GitHub](https://github.com/lpil/gleam). 81 | 82 | If you've any questions or want to get involved join the IRC channel 83 | `#gleam-lang` on Freenode. 84 | 85 | Thanks for reading! :) 86 | -------------------------------------------------------------------------------- /posts/introducing-the-gleam-package-index.djot: -------------------------------------------------------------------------------- 1 | Gleam is a member of the BEAM family of languages, alongside Erlang, Elixir, and 2 | others. As such Gleam packages are distributed on [Hex.pm][hex], the 3 | package manager for the BEAM ecosystem. Hex provides an excellent experience, 4 | but up until now there has been one small problem: discovering Gleam packages. 5 | 6 | There are thousands of packages on Hex, and while Gleam projects can use 7 | packages written in Erlang or Elixir, most of the time we want to use packages 8 | that are either written in Gleam or already have Gleam bindings. Hex does not 9 | currently provide a way to search by language, so finding these packages is more 10 | difficult than it should be. 11 | 12 | To solve this problem we have created the [Gleam package index][packages], a 13 | website for exploring the Gleam subset of the Hex repository. ✨ 14 | 15 | Today it provides a list of all the Gleam packages on Hex as well as a text 16 | search, and future we could add fancy Gleam specific features such as 17 | [searching by type information][elm-search], or [language server integration][lsp] 18 | so packages can be discovered directly from your editor. 19 | 20 | [hex]: https://hex.pm/ 21 | [packages]: https://packages.gleam.run/ 22 | [elm-search]: https://klaftertief.github.io/elm-search/ 23 | [lsp]: /news/v0.21-introducing-the-gleam-language-server/ 24 | 25 | ## How does it work? 26 | 27 | The package index is a web application written using the 28 | [`gleam_http`][gleam_http] package. It talks to a PostgreSQL database which it 29 | populates with Gleam packages found by crawling the Hex JSON API. All of this is 30 | deployed to [Fly][fly], who kindly sponsor Gleam's development. 31 | 32 | [gleam_http]: https://github.com/gleam-lang/http 33 | [fly]: https://fly.io/ 34 | 35 | In `gleam_http` a web service is a simple function that takes the `Request` type 36 | as an argument and returns the `Response` type. We want some extra data (a 37 | database connection) alongside the request when responding, so we create a 38 | `Context` type that can hold the request and any other data we need. 39 | 40 | ```gleam 41 | pub type Context { 42 | Context(db: pgo.Connection, request: Request(BitString)) 43 | } 44 | 45 | /// This function takes a database connection and returns as 46 | /// service function that will response to individual requests 47 | /// by calling `handle_request`. 48 | /// 49 | pub fn make_service(db: pgo.Connection) { 50 | fn(request) { 51 | let context = Context(db, request) 52 | handle_request(context) 53 | } 54 | } 55 | ``` 56 | 57 | The `handle_request` function calls the appropriate function for the requested 58 | path, passing the context along with it. Thanks to Gleam's pattern matching we 59 | don't need a complex and slow router abstraction, we can use the super fast and 60 | familiar `case` expression, like any other Gleam code. 61 | 62 | ```gleam 63 | pub fn handle_request(context: Context) { 64 | let path = request.path_segments(context.request) 65 | case path { 66 | [] -> search(context) 67 | ["styles.css"] -> stylesheet() 68 | _ -> redirect(to: "/") 69 | } 70 | } 71 | ``` 72 | 73 | The handler functions are where the logic of responding to requests lives. In 74 | the `search` handler we get the search term (if any) from the request, search 75 | the database for appropriate packages, render HTML from the list of packages, 76 | and return a response with this HTML to the user. 77 | 78 | ```gleam 79 | fn search(context: Context) -> Response(String) { 80 | // Search in the database for packages 81 | let term = get_search_parameter(context.request) 82 | let assert Ok(packages) = database.search(context.db, term) 83 | 84 | // Render HTML to show the results 85 | let html = packages_page(packages, term) 86 | 87 | // Return a response 88 | response.new(200) 89 | |> response.set_header("content-type", "text/html; charset=utf-8") 90 | |> response.set_body(html) 91 | } 92 | ``` 93 | 94 | For rendering HTML we are using [Nakai][nakai], a package for writing HTML on 95 | the server (or anywhere) in a somewhat similar style to Elm or React, giving us 96 | fully type checked HTML within Gleam. 97 | 98 | [nakai]: https://nakaixo.github.io/ 99 | 100 | ```gleam 101 | fn packages_page(packages: List(Package), term: String) -> String { 102 | html.Html([], [ 103 | html.head([ 104 | meta([charset("utf-8")]), 105 | meta([name("viewport"), content("width=device-width, initial-scale=1")]), 106 | link([rel("stylesheet"), href("/styles.css")]), 107 | title_text([], "Gleam Packages"), 108 | ]), 109 | // ... etc 110 | ``` 111 | 112 | Once the service is written we can serve it using [Mist][mist], Alex Manning's 113 | pure Gleam web server. Mist is a fantastic demonstration of Gleam's maturity and 114 | what the language is capable of, and [in benchmarks][benchmarks] it is 115 | comfortably faster than Cowboy, the most commonly used Erlang web server. 116 | 117 | [mist]: https://github.com/rawhat/mist 118 | [benchmarks]: https://github.com/rawhat/http-benchmarks 119 | 120 | ```gleam 121 | pub fn main() { 122 | let assert Ok(key) = os.get_env("HEX_API_KEY") 123 | let db = index.connect() 124 | 125 | // Start syncing new releases every 60 seconds 126 | let sync = fn() { sync_new_gleam_releases(key, db) } 127 | let assert Ok(_) = periodically(sync, waiting: 60 * 1000) 128 | 129 | // Start the web server 130 | let service = web.make_service(db) 131 | let assert Ok(_) = mist.run_service(3000, service, max_body_limit: 4_000_000) 132 | io.println("Started listening on http://localhost:3000 ✨") 133 | 134 | // Put the main process to sleep while the web server handles traffic 135 | process.sleep_forever() 136 | } 137 | ``` 138 | 139 | Here in the main function we start two actors, one that periodically syncs new 140 | releases from the Hex API, and one that runs the web server. I won't go into 141 | detail of the syncing process here, but in short it queries the Hex API for 142 | packages ordered by time of last update, and iterates across pages until it has 143 | found all the packages that have been updated since the last time it ran. 144 | 145 | If you'd like to see more of how this project works the source code is available 146 | [on GitHub][source]. I hope you enjoy using the package index, and do let us 147 | know if you have any ideas or suggestions! 148 | 149 | [source]: https://github.com/gleam-lang/packages 150 | 151 | 152 | Happy hacking! 💖 153 | -------------------------------------------------------------------------------- /posts/introducing-the-gleam-roadmap.djot: -------------------------------------------------------------------------------- 1 | Often people ask "Hey Louis! What's coming next in Gleam?" 2 | 3 | It's a tricky question to answer. Gleam is a community project rather than a 4 | corporate one, so often many things are happening at once. As the project leader 5 | I have my own priorities, but it's up to individual contributors to decide what 6 | they find valuable and fun enough to work on. Often the most useful way to 7 | respond is to give an overview of the upcoming features that I think are most 8 | exciting or impactful, and directing them to the changelog and the issue tracker 9 | if they want to learn more. 10 | 11 | To help with this, and to give folks a quicker way to access this information 12 | than waiting for me to reply on the Gleam discord server, I've created the 13 | Gleam development roadmap. It lists the most impactful features, past, 14 | present, and future, and I'll update it with each new Gleam release. I hope you 15 | find it useful! 16 | 17 | ``` =html 18 |
19 | View the roadmap 20 |
21 | ``` 22 | 23 | Thanks for reading, happy hacking! 💜 24 | -------------------------------------------------------------------------------- /posts/welcome-lambda.djot: -------------------------------------------------------------------------------- 1 | ``` =html 2 | 5 | ``` 6 | 7 | Gleam is a type safe and scalable language for the Erlang virtual machine and 8 | JavaScript runtimes. Unlike most programming languages, Gleam does not come from 9 | any particular tech corporation or academic institution; Instead it is a truly 10 | open-source community project and is funded entirely by sponsors, both 11 | individuals and corporations. 12 | 13 | Today we welcome and thank [Lambda](https://lambdaclass.com/), Gleam's new primary sponsor. 14 | 15 | Lambda operates in a wide array of fields, from machine learning, to distributed 16 | systems, crypto infrastructure, video games, animation, and more. They 17 | often use other BEAM languages such as Elixir and Erlang, as well as 18 | strongly-typed languages such as Rust, and now they are expanding their 19 | technology stack to include Gleam. 20 | 21 | Lambda has a broad portfolio of impactful open-source projects, such as 22 | [lambdaworks](https://github.com/lambdaclass/lambdaworks), a collection of 23 | efficient cryptographic primitives for proving systems; 24 | [Cairo-VM](https://github.com/lambdaclass/cairo-vm), the Rust-based virtual 25 | machine for the Cairo language; and 26 | [Erlings](https://github.com/lambdaclass/erlings), a comprehensive set of 27 | exercises for mastering Erlang's fundamentals and advanced topics. 28 | 29 | ``` =html 30 | 34 | ``` 35 | 36 | One of their latest exciting projects is the upcoming mobile game Champions of 37 | Mirra, a fast-paced multiplayer mobile game launching soon in Argentina. 38 | 39 | The game program that runs on your phone is made using the popular Unity game 40 | engine. Being a competitive multiplayer game, they needed to pair this game 41 | program with a backend that is reliable, scalable, and perhaps most importantly 42 | low latency, making it a perfect fit for the Erlang virtual machine. 43 | 44 | The current version of the backend has recently been published as open-source 45 | software which can be [viewed on GitHub](https://github.com/lambdaclass/mirra_backend), 46 | and Lambda intends to use Gleam for future iterations of the backend, gaining 47 | both the reliable performance and resilience of the virtual machine, and the 48 | practical and precise development experience of Gleam and its strong static 49 | types. 50 | 51 | Lambda's work isn't limited to only software development! They are also working 52 | on the groundbreaking cyberpunk anime _Electro Andes_, set to release next year. 53 | 54 | ``` =html 55 | 56 | ``` 57 | 58 | Lambda's investment in Gleam, both financially and in making it part of their 59 | technology stack, is a testament to the Gleam ecosystem and a real milestone for 60 | us. We hope that Lambda inspires other groups, encouraging them to take 61 | advantage of what Gleam has to offer, and to follow their lead in supporting the 62 | ecosystems they are part of. 63 | 64 | Thank you Lambda, we're excited to see where this partnership goes and what 65 | innovative Gleam-powered technologies emerge. Especially cool stuff like games 66 | and anime! 67 | 68 | If you wish to learn more about Lambda and what they do, or if you are 69 | interested in collaborating with them, please visit [their website](https://lambdaclass.com/). 70 | If you'd like to get more involved in Gleam then why not join [the Gleam Discord](https://discord.gg/Fm8Pwmy) 71 | and meet your fellow Gleamlins. 72 | 73 | Thanks for reading 💖 74 | -------------------------------------------------------------------------------- /priv/fonts/CascadiaMono.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/fonts/CascadiaMono.woff2 -------------------------------------------------------------------------------- /priv/fonts/Lexend-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/fonts/Lexend-700.woff2 -------------------------------------------------------------------------------- /priv/fonts/Lexend.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/fonts/Lexend.woff2 -------------------------------------------------------------------------------- /priv/fonts/Outfit.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/fonts/Outfit.woff -------------------------------------------------------------------------------- /priv/funding.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v1.0.0", 3 | "entity": { 4 | "type": "individual", 5 | "role": "maintainer", 6 | "name": "Louis Pilfold", 7 | "email": "louis@gleam.run", 8 | "description": "The creator and maintainer of the Gleam programming language", 9 | "webpageUrl": { 10 | "url": "https://gleam.run" 11 | } 12 | }, 13 | 14 | "projects": [ 15 | { 16 | "guid": "gleam", 17 | "name": "The Gleam Programming Language", 18 | "description": "Gleam is a friendly language for building type-safe systems that scale!", 19 | "webpageUrl": { 20 | "url": "https://gleam.run/" 21 | }, 22 | "repositoryUrl": { 23 | "url": "https://github.com/gleam-lang/gleam/", 24 | "wellKnown": "https://github.com/gleam-lang/gleam/blob/main/.well-known/funding-manifest-urls" 25 | }, 26 | "licenses": ["spdx:Apache-2.0"], 27 | "tags": [ 28 | "developer-tools", 29 | "development", 30 | "programming", 31 | "software-engineering" 32 | ] 33 | } 34 | ], 35 | 36 | "funding": { 37 | "channels": [ 38 | { 39 | "guid": "github-sponsors", 40 | "type": "payment-provider", 41 | "address": "https://github.com/sponsors/lpil" 42 | }, 43 | { 44 | "guid": "liberapay", 45 | "type": "payment-provider", 46 | "address": "https://liberapay.com/gleam" 47 | } 48 | ], 49 | 50 | "plans": [ 51 | { 52 | "guid": "plan", 53 | "status": "active", 54 | "name": "Sponsorship", 55 | "amount": 0, 56 | "currency": "USD", 57 | "frequency": "monthly", 58 | "channels": ["github-sponsors", "liberapay"] 59 | } 60 | ] 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /priv/images/community/discord.svg: -------------------------------------------------------------------------------- 1 | Gleam's web chat on Discord 2 | -------------------------------------------------------------------------------- /priv/images/community/github.svg: -------------------------------------------------------------------------------- 1 | Gleam discussions on Github 2 | -------------------------------------------------------------------------------- /priv/images/date-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /priv/images/lucy-charcoal-2.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /priv/images/lucy-pink-2.svg: -------------------------------------------------------------------------------- 1 | 4 | 9 | 10 | 12 | 13 | 15 | 16 | 18 | 19 | 21 | 22 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /priv/images/lucy/lucy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /priv/images/lucy/lucybi.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /priv/images/lucy/lucydebugfail.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /priv/images/lucy/lucydunno.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /priv/images/lucy/lucyelixir.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /priv/images/lucy/lucyflake.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /priv/images/lucy/lucygay.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /priv/images/lucy/lucyglow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /priv/images/lucy/lucygodot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /priv/images/lucy/lucyhappy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /priv/images/lucy/lucyjs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /priv/images/lucy/lucylesbian.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /priv/images/lucy/lucymail.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /priv/images/lucy/lucynix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /priv/images/lucy/lucypan.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /priv/images/lucy/lucypinkbg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /priv/images/lucy/lucypride.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /priv/images/lucy/lucypridebg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /priv/images/lucy/lucysapphic.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /priv/images/lucy/lucysleep.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /priv/images/lucy/lucytiny-plain.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /priv/images/lucy/lucytiny.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /priv/images/lucy/lucytrans.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /priv/images/lucy/lucywasm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /priv/images/lucy/superlucy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.14-released/night-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.14-released/night-mode.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.20-released/github-syntax-highlighting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.20-released/github-syntax-highlighting.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.21-released/compile.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.21-released/compile.mp4 -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.21-released/compile.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.21-released/compile.webm -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.21-released/formatting.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.21-released/formatting.mp4 -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.21-released/formatting.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.21-released/formatting.webm -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.21-released/goto-definition.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.21-released/goto-definition.mp4 -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.21-released/goto-definition.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.21-released/goto-definition.webm -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.21-released/hover.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.21-released/hover.mp4 -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.21-released/hover.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.21-released/hover.webm -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.21-released/lsp-languages-editors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.21-released/lsp-languages-editors.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.23-released/docs-search.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.23-released/docs-search.mp4 -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.23-released/docs-search.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.23-released/docs-search.webm -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.26-released/deno_hr_circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.15, written by Peter Selinger 2001-2017 9 | 10 | 12 | 87 | 89 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.28-released/docs-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.28-released/docs-hover.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.29-released/autocompletion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.29-released/autocompletion.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.34-released/goto-docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.34-released/goto-docs.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.34-released/multi-target.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.34-released/multi-target.gif -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.7-released/import-cycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.7-released/import-cycle.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.7-released/incorrect-number-of-patterns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.7-released/incorrect-number-of-patterns.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v0.7-released/type-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v0.7-released/type-error.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v1.1-released/imports.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v1.1-released/imports.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v1.1-released/spread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v1.1-released/spread.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v1.1-released/tuple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v1.1-released/tuple.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v1.1-released/unused.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v1.1-released/unused.png -------------------------------------------------------------------------------- /priv/images/news/gleam-v1.4-released/help.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleam-v1.4-released/help.mp4 -------------------------------------------------------------------------------- /priv/images/news/gleams-new-interactive-language-tour/browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/gleams-new-interactive-language-tour/browser.png -------------------------------------------------------------------------------- /priv/images/news/welcome-lambda/champions.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/news/welcome-lambda/champions.mp4 -------------------------------------------------------------------------------- /priv/images/pride-wall.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /priv/images/return-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /priv/images/share-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /priv/images/social-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/social-image.png -------------------------------------------------------------------------------- /priv/images/sponsor-heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /priv/images/sponsors/alembic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /priv/images/sponsors/lambda-class-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/sponsors/lambda-class-black.png -------------------------------------------------------------------------------- /priv/images/sponsors/lambda-class-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/sponsors/lambda-class-white.png -------------------------------------------------------------------------------- /priv/images/sponsors/memo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/sponsors/memo.png -------------------------------------------------------------------------------- /priv/images/sponsors/nine-fx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/images/sponsors/nine-fx.png -------------------------------------------------------------------------------- /priv/images/user-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /priv/images/waves.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /priv/img/gleam-0-13-docs-link.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/img/gleam-0-13-docs-link.jpg -------------------------------------------------------------------------------- /priv/img/gleam-0-13-docs-version.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleam-lang/website/36b1e540acd76b9f106bd380b6696cac31632a5d/priv/img/gleam-0-13-docs-version.jpg -------------------------------------------------------------------------------- /priv/img/lucy-pink-3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 17 | 18 | 32 | 43 | 53 | 61 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /priv/img/lucy-pink.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 17 | 18 | 32 | 43 | 53 | 61 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /priv/javascript/main.js: -------------------------------------------------------------------------------- 1 | function showPride() { 2 | document.body.classList.toggle("pride"); 3 | } 4 | 5 | function expandSponsorsSection() { 6 | document.documentElement.style.setProperty("--sponsors-max-height", "5000px"); 7 | } 8 | 9 | if (new Date().getMonth() === 5) showPride(); 10 | 11 | for (let element of document.querySelectorAll("[data-show-pride]")) { 12 | element.addEventListener("click", showPride); 13 | } 14 | 15 | for (let element of document.querySelectorAll("[data-randomise-order]")) { 16 | let children = Array.from(element.children); 17 | children.sort(() => Math.random() - 0.5); 18 | element.replaceChildren(...children); 19 | } 20 | 21 | for (let element of document.querySelectorAll("[data-expand-sponsors]")) { 22 | element.addEventListener("click", expandSponsorsSection); 23 | } 24 | -------------------------------------------------------------------------------- /priv/javascript/plausible.js: -------------------------------------------------------------------------------- 1 | let script = document.createElement("script"); 2 | script.async = true; 3 | script.defer = true; 4 | script.src = "https://plausible.io/js/plausible.js"; 5 | script.dataset.dataDomain = "gleam.run"; 6 | document.body.appendChild(script); 7 | -------------------------------------------------------------------------------- /src/website.gleam: -------------------------------------------------------------------------------- 1 | import argv 2 | import gleam/io 3 | import gleam/list 4 | import gleam/result 5 | import gleam/time/timestamp 6 | import gleave 7 | import snag 8 | import website/atom_feed 9 | import website/cheatsheet 10 | import website/command_line_reference 11 | import website/fs 12 | import website/language_server 13 | import website/news 14 | import website/page 15 | import website/roadmap 16 | import website/site 17 | import website/sponsor 18 | 19 | pub fn main() -> Nil { 20 | let result = case argv.load().arguments { 21 | ["update-sponsors"] -> sponsor.update_list() 22 | _ -> build_site() 23 | } 24 | 25 | case result { 26 | Ok(_) -> Nil 27 | Error(error) -> { 28 | io.println_error(snag.pretty_print(error)) 29 | gleave.exit(127) 30 | } 31 | } 32 | } 33 | 34 | fn build_site() -> snag.Result(Nil) { 35 | use styles_hash <- result.try(fs.asset_hash("styles/main.css")) 36 | use news_posts <- result.try(news.all()) 37 | 38 | let ctx = 39 | site.Context( 40 | hostname: "https://gleam.run", 41 | time: timestamp.system_time(), 42 | styles_hash:, 43 | ) 44 | 45 | let page_files = [ 46 | page.home(ctx), 47 | page.branding(ctx), 48 | page.community(ctx), 49 | page.installing(ctx), 50 | page.gleam_toml(ctx), 51 | page.writing_gleam(ctx), 52 | page.documentation(ctx), 53 | page.deployment_linux(ctx), 54 | page.deployment_flyio(ctx), 55 | page.frequently_asked_questions(ctx), 56 | page.news_index(news_posts, ctx), 57 | roadmap.page(ctx), 58 | command_line_reference.page(ctx), 59 | language_server.page(ctx), 60 | cheatsheet.erlang(ctx), 61 | cheatsheet.elixir(ctx), 62 | cheatsheet.python(ctx), 63 | cheatsheet.php(ctx), 64 | cheatsheet.elm(ctx), 65 | cheatsheet.rust(ctx), 66 | ] 67 | 68 | let files = [ 69 | static_files(), 70 | page_files, 71 | news_files(news_posts, ctx), 72 | redirect_files(), 73 | ] 74 | 75 | io.print("Writing to disc: ") 76 | use _ <- result.try(fs.delete_dist()) 77 | let result = 78 | files 79 | |> list.flatten 80 | |> list.try_each(fs.create) 81 | io.print("\n") 82 | result 83 | } 84 | 85 | fn news_files( 86 | news_posts: List(news.NewsPost), 87 | ctx: site.Context, 88 | ) -> List(fs.File) { 89 | [atom_feed.file(news_posts), ..list.map(news_posts, page.news_post(_, ctx))] 90 | } 91 | 92 | fn static_files() -> List(fs.File) { 93 | [ 94 | fs.Copy("fonts"), 95 | fs.Copy("images"), 96 | fs.Copy("img"), 97 | fs.Copy("javascript"), 98 | fs.Copy("styles"), 99 | fs.Copy("funding.json"), 100 | ] 101 | } 102 | 103 | fn redirect_files() -> List(fs.File) { 104 | [ 105 | page.redirect( 106 | "writing-gleam/command-line-reference/index.html", 107 | "/command-line-reference", 108 | ), 109 | page.redirect_to_tour("book/index.html", ""), 110 | page.redirect_to_tour("book/print.html", ""), 111 | page.redirect_to_tour("book/tour/index.html", ""), 112 | page.redirect_to_tour("book/tour/use.html", "advanced-features/use/"), 113 | page.redirect_to_tour("book/tour/functions.html", "functions/functions"), 114 | page.redirect_to_tour("book/tour/expression-blocks.html", "basics/blocks/"), 115 | page.redirect_to_tour("book/tour/constants.html", "basics/constants/"), 116 | page.redirect_to_tour("book/tour/custom-types.html", "basics/custom-types/"), 117 | page.redirect_to_tour("book/tour/bools.html", "basics/bools/"), 118 | page.redirect_to_tour("book/tour/bit-strings.html", "data-types/bit-arrays"), 119 | page.redirect_to_tour("book/tour/bit-arrays.html", "data-types/bit-arrays"), 120 | page.redirect_to_tour("book/tour/strings.html", "data-types/strings/"), 121 | page.redirect_to_tour("book/tour/result.html", "data-types/results/"), 122 | page.redirect_to_tour("book/tour/modules.html", "basics/hello-world/"), 123 | page.redirect_to_tour("book/tour/lists.html", "basics/lists/"), 124 | page.redirect_to_tour("book/tour/let-bindings.html", "basics/assignments/"), 125 | page.redirect_to_tour("book/tour/ints-and-floats.html", "basics/ints/"), 126 | page.redirect_to_tour("book/tour/type-aliases.html", "basics/type-aliases/"), 127 | page.redirect_to_tour("book/tour/tuples.html", "data-types/tuples/"), 128 | page.redirect_to_tour("book/tour/todo.html", "advanced-features/todo/"), 129 | page.redirect_to_tour( 130 | "book/tour/assert.html", 131 | "advanced-features/let-assert/", 132 | ), 133 | page.redirect_to_tour( 134 | "book/tour/type-annotations.html", 135 | "basics/assignments/", 136 | ), 137 | page.redirect_to_tour( 138 | "book/tour/todo-and-panic.html", 139 | "advanced-features/todo/", 140 | ), 141 | page.redirect_to_tour( 142 | "book/tour/external-functions.html", 143 | "advanced-features/externals/", 144 | ), 145 | page.redirect_to_tour( 146 | "book/tour/external-types.html", 147 | "advanced-features/externals/", 148 | ), 149 | page.redirect_to_tour( 150 | "book/tour/comments.html", 151 | "functions/documentation-comments/", 152 | ), 153 | page.redirect_to_tour( 154 | "book/tour/case-expressions.html", 155 | "flow-control/case-expressions/", 156 | ), 157 | ] 158 | } 159 | -------------------------------------------------------------------------------- /src/website/atom_feed.gleam: -------------------------------------------------------------------------------- 1 | import atomb 2 | import gleam/list 3 | import gleam/option 4 | import gleam/string_tree 5 | import gleam/time/calendar 6 | import gleam/time/timestamp 7 | import website/fs 8 | import website/news 9 | 10 | pub fn file(news_posts: List(news.NewsPost)) -> fs.File { 11 | let updated = case news_posts { 12 | [first, ..] -> to_calendar(first.published) 13 | _ -> timestamp.system_time() 14 | } 15 | let feed = 16 | atomb.Feed( 17 | title: "Gleam", 18 | id: "https://gleam.run/feed.xml", 19 | updated:, 20 | subtitle: option.Some(atomb.TextContent( 21 | atomb.Text, 22 | "Gleam is a fast, friendly, and functional language for building type-safe, scalable systems!", 23 | )), 24 | rights: option.None, 25 | icon: option.None, 26 | logo: option.None, 27 | authors: [], 28 | links: [ 29 | atomb.Link(href: "https://gleam.run/feed.xml", rel: option.Some("self")), 30 | atomb.Link(href: "https://gleam.run", rel: option.Some("alternate")), 31 | ], 32 | categories: [], 33 | contributors: [], 34 | generator: option.Some(atomb.Generator( 35 | "gleam-lang/website", 36 | uri: option.Some("https://github.com/gleam-lang/website"), 37 | version: option.None, 38 | )), 39 | entries: news_posts |> list.take(10) |> list.map(to_entry), 40 | ) 41 | fs.File("feed.xml", content: atomb.render(feed) |> string_tree.to_string) 42 | } 43 | 44 | fn to_calendar(date: calendar.Date) -> timestamp.Timestamp { 45 | timestamp.from_calendar( 46 | date, 47 | calendar.TimeOfDay(0, 0, 0, 0), 48 | calendar.utc_offset, 49 | ) 50 | } 51 | 52 | fn to_entry(post: news.NewsPost) -> atomb.Entry { 53 | let url = "https://gleam.run/news/" <> post.path 54 | let published = to_calendar(post.published) 55 | atomb.Entry( 56 | id: url, 57 | title: post.title, 58 | updated: published, 59 | published: option.Some(published), 60 | content: option.Some(atomb.InlineText(atomb.Html, post.content)), 61 | summary: option.None, 62 | categories: [], 63 | links: [atomb.Link(url, rel: option.Some("alternate"))], 64 | contributors: [], 65 | rights: option.None, 66 | authors: [ 67 | atomb.Person( 68 | name: post.author.name, 69 | uri: option.Some(post.author.url), 70 | email: option.None, 71 | ), 72 | ], 73 | ) 74 | } 75 | -------------------------------------------------------------------------------- /src/website/fs.gleam: -------------------------------------------------------------------------------- 1 | import filepath 2 | import gleam/bit_array 3 | import gleam/crypto 4 | import gleam/io 5 | import gleam/list 6 | import gleam/result 7 | import gleam/string 8 | import simplifile 9 | import snag 10 | 11 | pub type File { 12 | /// A file with some content 13 | HtmlPage(path: String, content: String) 14 | /// A file with some content 15 | File(path: String, content: String) 16 | /// A directory copied into the output 17 | Copy(path: String) 18 | } 19 | 20 | const dist = "dist/" 21 | 22 | const priv = "priv/" 23 | 24 | pub fn delete_dist() -> snag.Result(Nil) { 25 | use _ <- result.try(ensure_directory_exists(dist)) 26 | use files <- result.try( 27 | simplifile.read_directory(dist) 28 | |> handle_error("List"), 29 | ) 30 | files 31 | |> list.map(filepath.join(dist, _)) 32 | |> simplifile.delete_all 33 | |> handle_error("Reset") 34 | } 35 | 36 | pub fn create(file: File) -> snag.Result(Nil) { 37 | case file { 38 | HtmlPage(path:, content:) -> { 39 | path 40 | |> filepath.join("index.html") 41 | |> write_file(<>) 42 | } 43 | File(path:, content:) -> { 44 | path 45 | |> write_file(<>) 46 | } 47 | Copy(path:) -> { 48 | copy(path) 49 | } 50 | } 51 | |> ok_dot 52 | } 53 | 54 | fn ok_dot(result: Result(a, e)) -> Result(a, e) { 55 | case result { 56 | Error(_) -> result 57 | Ok(_) -> { 58 | io.print(".") 59 | result 60 | } 61 | } 62 | } 63 | 64 | pub fn asset_hash(path: String) -> snag.Result(String) { 65 | let path = filepath.join(priv, path) 66 | simplifile.read_bits(path) 67 | |> snag.map_error(simplifile.describe_error) 68 | |> snag.context("Failed to read " <> path) 69 | |> result.map(crypto.hash(crypto.Sha256, _)) 70 | |> result.map(bit_array.base64_url_encode(_, False)) 71 | |> snag.context("Failed determine asset hash for " <> path) 72 | } 73 | 74 | pub fn read(path: String) -> snag.Result(String) { 75 | simplifile.read(path) 76 | |> snag.map_error(simplifile.describe_error) 77 | |> snag.context("Failed to read " <> path) 78 | } 79 | 80 | fn copy(path: String) -> snag.Result(Nil) { 81 | let from = filepath.join(priv, path) 82 | let to = filepath.join(dist, path) 83 | simplifile.copy(from, to) 84 | |> handle_error("Copy " <> path) 85 | } 86 | 87 | fn write_file(path: String, content: BitArray) -> snag.Result(Nil) { 88 | let qualified_path = dist |> filepath.join(path) 89 | let parent = filepath.directory_name(qualified_path) 90 | use _ <- result.try(ensure_directory_exists(parent)) 91 | use _ <- result.try( 92 | simplifile.write_bits(qualified_path, content) 93 | |> handle_error("Create " <> path), 94 | ) 95 | Ok(Nil) 96 | } 97 | 98 | fn ensure_directory_exists(path: String) -> Result(Nil, snag.Snag) { 99 | case simplifile.is_directory(path) { 100 | Ok(True) -> Ok(Nil) 101 | Ok(False) -> simplifile.create_directory_all(path) |> ok_dot 102 | Error(error) -> Error(error) 103 | } 104 | |> snag.map_error(simplifile.describe_error) 105 | |> snag.context("Failed to create" <> path <> "/") 106 | } 107 | 108 | fn handle_error( 109 | result: Result(a, simplifile.FileError), 110 | detail: String, 111 | ) -> snag.Result(a) { 112 | result 113 | |> snag.map_error(simplifile.describe_error) 114 | |> snag.context("Failed to " <> string.lowercase(detail)) 115 | } 116 | -------------------------------------------------------------------------------- /src/website/site.gleam: -------------------------------------------------------------------------------- 1 | import gleam/time/timestamp.{type Timestamp} 2 | 3 | pub type Context { 4 | Context(hostname: String, time: Timestamp, styles_hash: String) 5 | } 6 | 7 | pub type Create { 8 | Directory(path: String) 9 | Page(path: String, content: BitArray) 10 | } 11 | -------------------------------------------------------------------------------- /test/website_test.gleam: -------------------------------------------------------------------------------- 1 | import gleeunit 2 | import gleeunit/should 3 | 4 | pub fn main() -> Nil { 5 | gleeunit.main() 6 | } 7 | 8 | // gleeunit test functions end in `_test` 9 | pub fn hello_world_test() { 10 | 1 11 | |> should.equal(1) 12 | } 13 | --------------------------------------------------------------------------------