[00:00.000 --> 00:13.600] So, now we have Harry Berstow with an instruction to Gleam, which is another language running [00:13.600 --> 00:17.400] on Erlang VM, so give it up for him. [00:17.400 --> 00:27.120] Hi, everyone, my name is Harry, and I'm, as was said, an instruction to Gleam. [00:27.120 --> 00:28.120] You might ask, why is Gleam? [00:28.120 --> 00:32.040] Gleam is a programming language for building type-safe systems that scale, it's powered [00:32.040 --> 00:35.720] primarily by the beam, but can also be run on JavaScript targets too. [00:35.720 --> 00:40.240] I thought I'd go first into the three key points which make Gleam what it is. [00:40.240 --> 00:41.240] First it's safety. [00:41.240 --> 00:45.080] Gleam has powerful compile-time type checking built into its core. [00:45.080 --> 00:49.120] This helps you write fast code that's integrated with Erlang and Elixir while giving you the [00:49.120 --> 00:52.560] safety of a statically typed language. [00:52.560 --> 00:56.400] Secondly it's performance, as was just discussed before, building on the success of Discord, [00:56.400 --> 00:58.960] WhatsApp, Ericsson, and more with the beam. [00:58.960 --> 01:02.760] Gleam adds no overhead, so you get the same great type-safety and performance with an [01:02.760 --> 01:04.800] enjoyable syntax. [01:04.800 --> 01:08.640] And finally it's friendliness, both the community and the syntax of Gleam are friendly. [01:08.640 --> 01:12.160] The community is more than happy to help with any problem or just friendly chit-chat, they [01:12.160 --> 01:14.520] even help write some of this talk. [01:14.520 --> 01:18.640] And when you get something wrong, the compiler provides insightful help so that you can hunt [01:18.640 --> 01:20.560] down the issues and stop them. [01:20.560 --> 01:23.440] The syntax of Gleam is similar to that of Rust, but if you're not from one of those [01:23.440 --> 01:27.280] backgrounds, don't worry, there are several guides to get started if you're used to a [01:27.280 --> 01:30.920] syntax from Python, Elm, Erlang, or even Elixir. [01:30.920 --> 01:35.640] Here's an example of the start of the Gleam project. [01:35.640 --> 01:40.520] All Gleam projects have an exported main function in the project name.gleam file, which is within [01:40.520 --> 01:41.520] your source folder. [01:41.520 --> 01:46.920] If you need I.O., you can import the standard libraries I.O. module as shown there. [01:46.920 --> 01:49.560] And the standard library contains several modules to help you with everything you can [01:49.560 --> 01:52.800] think of, from regex to options, iterators, and more. [01:52.800 --> 01:57.680] If you need target-specific standard library features, look at the Gleam, Erlang, and Gleam [01:57.680 --> 02:03.760] JavaScript packages, which are both available on Hex and GitHub. [02:03.760 --> 02:07.080] Let's explore some Gleam examples to get a better understanding of the language. [02:07.080 --> 02:10.840] And once we've done that, you can go away and look at the docs yourself for more examples, [02:10.840 --> 02:15.560] and we'll go on to building some stuff with Shimmer. [02:15.560 --> 02:18.080] Variables in Gleam are created using the let keyword. [02:18.080 --> 02:20.240] They are assigned to a name and a value. [02:20.240 --> 02:23.800] The name can be reused later by other let bindings, but the values contained within [02:23.800 --> 02:27.960] are immutable, meaning the values themselves cannot be changed. [02:27.960 --> 02:31.240] Here's an example of blocks. [02:31.240 --> 02:34.080] Every block in Gleam is an expression. [02:34.080 --> 02:37.200] All expressions in the block are executed, and then the result of the last expression [02:37.200 --> 02:38.200] is returned. [02:38.200 --> 02:42.520] So as you can see here, the response will be false, even though hello and 42 plus 12 [02:42.520 --> 02:44.040] are evaluated. [02:44.040 --> 02:49.280] This can be used to build more advanced expressions where the order of operations is important. [02:49.280 --> 02:52.360] Here's an example of using the blocks to convert from Fahrenheit to Celsius, meaning [02:52.360 --> 02:58.200] sure to remove the 32 before multiplying and dividing. [02:58.200 --> 03:00.320] In Gleam, lists are all homogenous. [03:00.320 --> 03:03.760] This means that elements in a list must all be of the same type. [03:03.760 --> 03:08.320] If you try and construct a list of multiple types, this will result in a compiler presenting [03:08.320 --> 03:13.200] you with a type error and showing you where you try to use the multiple different types, [03:13.200 --> 03:17.480] so you can find it and correct it. [03:17.480 --> 03:20.920] Planning to a list in Gleam is very fast, and this is the way that Gleam's documentation [03:20.920 --> 03:23.680] recommends that you should add new values to a list. [03:23.680 --> 03:27.840] In the standard library, there is a list module, which allows you to do more advanced operations [03:27.840 --> 03:30.200] and also add to lists that way. [03:30.200 --> 03:34.600] The above example uses two constant lists, well, a constant and a constant list, but [03:34.600 --> 03:38.520] the same principles apply whether you have one dynamic and the other constant or vice [03:38.520 --> 03:41.920] versa. [03:41.920 --> 03:47.440] If you need multiple types in one place, you can use two pools using the hash and bracket [03:47.440 --> 03:48.440] syntax there. [03:48.440 --> 03:51.920] They can have multiple types and can be pattern matched against. [03:51.920 --> 03:55.520] We'll look at pattern matching in a few slides, but if you want to access the values on a [03:55.520 --> 03:59.800] two-pool, there's always the dot syntax, which I'll show you on the next slide, which is [03:59.800 --> 04:03.840] similar to that that you'd be used to in object-oriented for custom types and objects. [04:03.840 --> 04:08.320] Here's an example of a two-pool, which has two elements, and they're selected using [04:08.320 --> 04:11.520] the dot syntax and assigned to their own variables. [04:11.520 --> 04:14.760] It's not particularly useful here because they're constants, but with runtime variables, [04:14.760 --> 04:19.320] it's easy to access. [04:19.320 --> 04:22.720] Gleam supports custom types, and custom types in Gleam are a collection of keys and their [04:22.720 --> 04:26.360] values, and you can see them as objects. [04:26.360 --> 04:32.200] There's just one caveat though, types in Gleam don't have methods. [04:32.200 --> 04:38.200] Similar to two-pools, you can use the dot syntax to access properties within them, but [04:38.200 --> 04:46.360] instead of dot and position, you use dot and the name. [04:46.360 --> 04:50.400] In Gleam, custom types can have multiple constructors, similar to in the Rust ecosystem [04:50.400 --> 04:52.000] for enums. [04:52.000 --> 04:56.640] This does bring another caveat though, which is that the dot syntax now only works for [04:56.640 --> 04:59.120] keys that are shared across all elements. [04:59.120 --> 05:03.280] In this case, the only key you would be able to use the dot syntax with is name, otherwise [05:03.280 --> 05:11.480] you would have to pattern match against them to make sure that type safety stays. [05:11.480 --> 05:13.160] Case statements can match anything. [05:13.160 --> 05:17.320] In this first example, we use basic integers, but there's more advanced pattern matching [05:17.320 --> 05:19.560] over the next couple slides. [05:19.560 --> 05:23.400] You can see we match the first three numbers and produce a value, and otherwise we just [05:23.400 --> 05:31.040] consume as a variable and say we can either use or discard that variable. [05:31.040 --> 05:34.600] Some pattern match against two-pools here and even extract values from within. [05:34.600 --> 05:38.680] In this example, we're checking for two specific paths where one is no and the other is yes. [05:38.680 --> 05:42.960] The unique thing about the yes path is that we're discarding the integer in the middle, [05:42.960 --> 05:52.080] but we could again take that as a variable and do further checks against it. [05:52.080 --> 05:55.320] If you remember the custom type from earlier, this pattern matches against that, so we can [05:55.320 --> 06:01.840] extract the values into certain variables here, like talks and mic, and the rest can [06:01.840 --> 06:04.440] be thrown away with the two dots. [06:04.440 --> 06:08.320] You can also use the two dots and assign that to a variable so that then you can reconstruct [06:08.320 --> 06:13.640] the type afterwards to pass it back on somewhere else. [06:13.640 --> 06:16.800] There's lots more about Gleam syntax that I don't have time to cover today, such as [06:16.800 --> 06:21.040] external functions, generics, the use keyword, and more, and stuff's always being added [06:21.040 --> 06:22.480] to the syntax. [06:22.480 --> 06:26.280] All of it's documented in the language tour, so feel free to have a look over there and [06:26.280 --> 06:30.640] get a better understanding of what else is available within Gleam. [06:30.640 --> 06:39.280] Now let's get on to building some bots to put our Gleam skills into practice. [06:39.280 --> 06:42.680] Shimmer is a library which I've doubled in and out of over the last 13 months. [06:42.680 --> 06:48.240] I started as a project to learn Gleam and get into the Beam ecosystem, but in the process [06:48.240 --> 06:49.600] I've done much more. [06:49.600 --> 06:54.760] I'm doing this talk now, I've started contributing to the Gleam compiler and the wider ecosystem, [06:54.760 --> 06:58.360] and I use Elixir and Erlang more day-to-day now. [06:58.360 --> 07:04.560] At this point in Shimmer's development, we've moved away from using Erlang foreign functions [07:04.560 --> 07:08.800] and now a majority of it is in Gleam. [07:08.800 --> 07:11.920] Some key features of Shimmer, first, is compatibility. [07:11.920 --> 07:16.040] While Shimmer is built in Gleam, it can be used in Elixir, Erlang, and any other Beam [07:16.040 --> 07:20.640] language, it's published on Hex and the source code is available online. [07:20.640 --> 07:26.520] I've been working on some examples for Erlang and Elixir, which I'll publish into the GitHub [07:26.520 --> 07:29.120] repository once I've got them to a stable point. [07:29.120 --> 07:31.600] Secondly, it's actor-based. [07:31.600 --> 07:35.760] As we discussed before with its resilience, Shimmer is built on top of actors, and when [07:35.760 --> 07:40.160] we're running in single shard mode, you only have one actor, multiple shards, that's not [07:40.160 --> 07:41.160] a problem. [07:41.160 --> 07:45.160] We use a supervisor tree so that all the shards stay alive, and it's built on top of Erlang's [07:45.160 --> 07:47.800] OTP using the Gleam OTP package. [07:47.800 --> 07:50.720] And finally, it's type safety. [07:50.720 --> 07:53.960] As well as Beam Core to Gleam is a useful feature for Shimmer. [07:53.960 --> 07:58.160] While building your Discord bot in Gleam, we leverage all of Gleam's type functionality [07:58.160 --> 08:01.400] to ensure that the code you write for the Beam is type safe. [08:01.400 --> 08:04.920] You only get the full type safety when you write all of your code in Gleam, but you can [08:04.920 --> 08:08.400] always trust that the core of the library will be type safe. [08:08.400 --> 08:14.360] It's a little fun fact, moving more and more of Shimmer to Gleam. [08:14.360 --> 08:17.960] We're currently at 97% Gleam, and the rest is just Erlang foreign functions for small [08:17.960 --> 08:25.680] parts of networking, which are yet to have libraries implemented in Gleam. [08:25.680 --> 08:29.560] For some of you, this might now be the most interesting part of the talk, and for some [08:29.560 --> 08:30.560] of you, it might not. [08:30.560 --> 08:34.160] But I'm just going to quickly touch on how Discord's gateway works so that you have a [08:34.160 --> 08:40.400] better understanding of why we use actors and how that's useful to us in Gleam and [08:40.400 --> 08:44.120] with the OTP package. [08:44.120 --> 08:47.960] Discord Bot is powered by Discord's real-time gateway, which uses WebSockets to send and [08:47.960 --> 08:49.460] receive messages. [08:49.460 --> 08:55.960] For Shimmer, we use Erlang's gun library from 9.9 to receive them, and we use a typed wrapper [08:55.960 --> 09:03.160] on top of that, which is based upon Lewis, the creator of Gleam's Nerf library. [09:03.160 --> 09:06.760] The diagram here shows what happens when Shimmer opens a connection to the gateway. [09:06.760 --> 09:13.720] We use ETF encoding and hand the frames off to actors to pass, manage them, and send them [09:13.720 --> 09:21.800] to the event loop, and eventually either trigger handlers or discard them. [09:21.800 --> 09:26.240] Inside of that, Shimmer has a powerful event loop built on top of actors and messages, [09:26.240 --> 09:30.120] which manages multiple messages as well as its own state, both internally and externally [09:30.120 --> 09:35.320] accepts messages so that you can send updates to Discord, or internally, we can manage the [09:35.320 --> 09:37.000] updates. [09:37.000 --> 09:40.880] The next slide shows a state diagram, which roughly shows how it works. [09:40.880 --> 09:46.040] The state diagram shows what happens at different stages, depending on the initial message. [09:46.040 --> 09:49.680] For example, here, if you have a WebSocket frame, it's then passed. [09:49.680 --> 09:52.640] We then check whether it's what it's asking us to do. [09:52.640 --> 09:57.800] We then either respond, discard it, stop the bot, and then terminate. [09:57.800 --> 10:00.840] This diagram isn't complete at all, but it just shows you how complicated it can be [10:00.840 --> 10:07.120] very quickly, and how Gleam and the beam can easily handle it. [10:07.120 --> 10:10.400] Now that we know some Gleam and understand how Shimmer works under the hood, let's actually [10:10.400 --> 10:12.000] get our bot written. [10:12.000 --> 10:15.680] Above the boilerplate we're going to use, and as a side note, the final code for all [10:15.680 --> 10:21.720] of this is in the GitHub repository, which there's a link to at the end. [10:21.720 --> 10:25.240] Shimmer uses a handler-based system, which allows for one function to be registered for [10:25.240 --> 10:26.240] each event. [10:26.240 --> 10:30.320] For the purpose of this bot, we're only registering two events, but you can always register more [10:30.320 --> 10:32.640] as and when they're implemented in Shimmer. [10:32.640 --> 10:37.920] But before we have a look at that, let's understand how this code uses what we learned earlier [10:37.920 --> 10:41.960] and what it actually does. [10:41.960 --> 10:43.840] Here we create a new Shimmer client. [10:43.840 --> 10:48.320] Here we use a function that wraps around a custom type. [10:48.320 --> 10:53.120] The custom type holds both internal data as well as token, intents, and other data you [10:53.120 --> 10:54.120] pass in. [10:54.120 --> 10:56.040] So we create a function to wrap it. [10:56.040 --> 10:59.520] That way you don't have to manage all of that state yourself. [10:59.520 --> 11:04.880] And then we pipe that into the connect function, where it takes the client, passes that as [11:04.880 --> 11:09.760] the first parameter, and then passes your handlers in as the second. [11:09.760 --> 11:12.600] Normally the token should be an environment variable, but for the purpose of this, we're [11:12.600 --> 11:14.160] just using a string. [11:14.160 --> 11:18.520] Finally, we'll tell Erlang to sleep forever so that our actor and supervisor can run in [11:18.520 --> 11:25.960] the background, accepting messages from the gateway, and passing them to the event loop. [11:25.960 --> 11:31.160] Now that we know what it all vaguely does, let's revisit the handlers. [11:31.160 --> 11:33.440] First we're going to add a handler for the on-ready event. [11:33.440 --> 11:36.880] All handlers are passed in their event, as well as the client. [11:36.880 --> 11:41.360] That way you can use the client to call other methods, such as updating the bot's presence [11:41.360 --> 11:44.080] or sending messages yourself across the gateway. [11:44.080 --> 11:49.800] On the client, there's no private accesses, so you can access all the internal stuff as [11:49.800 --> 11:52.800] well if you want to add your own custom functionality. [11:52.800 --> 11:57.000] The client has its gun connection and all that other stuff in there as well, so you [11:57.000 --> 12:01.400] can adapt that as you please. [12:01.400 --> 12:04.960] Let's quickly zoom into the handler and explore that. [12:04.960 --> 12:09.800] Here, you can see the event in this case is an on-ready event, which provides us crucial [12:09.800 --> 12:10.800] information. [12:10.800 --> 12:17.040] As I said before, there's the client that we have just spoken about. [12:17.040 --> 12:21.800] The Gleams accesses syntax we learned about earlier makes it easy to access fields within [12:21.800 --> 12:24.000] the types, even when they're two levels deep. [12:24.000 --> 12:30.320] As you can see here, we're accessing the user's ID, which is in the user field of the event, [12:30.320 --> 12:35.440] and then we're printing it to the console using the standard libraries IO. [12:35.440 --> 12:40.440] We can then make this into a function, and then we can pass that into our on-ready handler. [12:40.440 --> 12:43.840] That way we could have the functions in multiple different files and import them from across [12:43.840 --> 12:48.000] the project to keep everything tidy. [12:48.000 --> 12:53.360] Let's move on to actually receiving some messages and sending some responses. [12:53.360 --> 12:59.160] When we receive a message, we get the on-message payload as our event. [12:59.160 --> 13:03.440] This contains information about the message itself, as well as the Guild ID mentions the [13:03.440 --> 13:06.640] message content and other variables. [13:06.640 --> 13:09.920] For now, we're going to assign the content to a variable for ease, but we can always [13:09.920 --> 13:15.520] collapse that into the case statement we use later on if that isn't something you need. [13:15.520 --> 13:21.080] Let's have a look how we're going to use our pattern matching to match against the content. [13:21.080 --> 13:24.560] Using Gleams' powerful pattern matching, we can check it as a desired prefix, and then [13:24.560 --> 13:29.320] we can extract the part to the right of the prefix into a separate variable. [13:29.320 --> 13:33.440] If not, we can take the message out itself, and we can just print that for easier debugging [13:33.440 --> 13:34.440] for now. [13:34.440 --> 13:36.720] Let's say we want a specific command, though. [13:36.720 --> 13:41.640] We could either add another case statement onto that, or we could just edit it so it's [13:41.640 --> 13:45.520] exclamation mark on what we want as the string of pattern matching against. [13:45.520 --> 13:47.600] Let's say, for example, you wanted some arguments, though. [13:47.600 --> 13:50.800] You could put the two together, and you could have your prefix with the command and take [13:50.800 --> 13:57.160] all of the arguments out separately to then pass and manage them. [13:57.160 --> 14:01.760] Now we'll match against a specific command, and in the response, we'll use the message [14:01.760 --> 14:08.440] send function to reply to the user by sending another message. [14:08.440 --> 14:14.520] As before, we can use the Handler's Builder to add this in as a function, and the bot [14:14.520 --> 14:15.520] should be done. [14:15.520 --> 14:19.680] Now you have a basic ping pong where you can send and receive messages using basically [14:19.680 --> 14:24.080] everything you learned from the introduction earlier. [14:24.080 --> 14:26.840] This before code, as I said earlier, was available on the GitHub as well, if you want [14:26.840 --> 14:30.600] to have a look and take a deeper dive there. [14:30.600 --> 14:34.320] Just to recap, at the start of the talk, we went over some Gleam syntax before we get [14:34.320 --> 14:35.760] ready on our exploration of Shimmer. [14:35.760 --> 14:40.400] We found out how the Discord's gateway worked on a high level and how to leverage Gleam [14:40.400 --> 14:50.040] OTP, and how Gleam OTP is leveraged within Shimmer for Actors. [14:50.040 --> 14:53.920] Thank you very much for listening, and if there's some QR codes to the Gleam website [14:53.920 --> 14:56.880] as well as the Gleam Discord, if you want to talk there, and if there's any questions, [14:56.880 --> 15:06.160] I'm happy to take them if I have time. [15:06.160 --> 15:12.400] So there's time for questions. [15:12.400 --> 15:19.360] You showed the tuple access syntax, which was tuple dot zero, tuple dot one. [15:19.360 --> 15:26.280] Does that mean that if you use a record, or if it's called, can you still use zero as [15:26.280 --> 15:27.280] a key? [15:27.280 --> 15:28.280] Or is that not? [15:28.280 --> 15:31.240] If you use a custom type, no. [15:31.240 --> 15:34.040] When you use custom types, you have to use the keys you define in the custom type to [15:34.040 --> 15:41.920] access them, the index syntax is only available for tuples. [15:41.920 --> 15:43.080] Question there. [15:43.080 --> 15:52.360] I have a question about the handlers on the library, and about Gleam, I guess. [15:52.360 --> 15:59.040] When I'm writing the handler, do I know what type of the event is, and the client, by the [15:59.040 --> 16:00.040] time of writing? [16:00.040 --> 16:06.440] Yes, so when a Gleam project is put onto Hex, we produce Hex docs, and they're all documented [16:06.440 --> 16:07.440] there as well. [16:07.440 --> 16:13.400] So the types on Hex docs you can look at, and also Gleam has an LSP built into it, which [16:13.400 --> 16:20.000] gives you the information, which is going to give you the information in your editor. [16:20.000 --> 16:21.480] Okay. [16:21.480 --> 16:23.720] Hello. [16:23.720 --> 16:29.440] If you're used to LX0, what are the things that you would miss in Gleam, or is there a [16:29.440 --> 16:31.680] big overlap? [16:31.680 --> 16:36.640] There's a fairly, it has most of the features you're used to, along with your type safety. [16:36.640 --> 16:40.240] The only, I guess, difference would be in Elixir, you can define multiple modules in [16:40.240 --> 16:44.320] one file, whereas in Gleam, that's not really something. [16:44.320 --> 16:45.800] Modules are files themselves. [16:45.800 --> 16:48.280] I guess that's the only thing I could think of off the top of my head. [16:48.280 --> 16:49.280] Right. [16:49.280 --> 16:50.280] Thank you. [16:50.280 --> 16:51.280] No worries. [16:51.280 --> 16:53.160] Is there a microse as well? [16:53.160 --> 16:57.720] No, we don't have macros right now, but there has been several discussions about how we [16:57.720 --> 17:01.440] want to do them and what they're going to be like, so there's potential for that in the [17:01.440 --> 17:05.360] future. [17:05.360 --> 17:07.360] Any more questions? [17:07.360 --> 17:08.360] Okay. [17:08.360 --> 17:09.360] Yeah. [17:09.360 --> 17:10.360] I'm sorry. [17:10.360 --> 17:11.360] Thank you for your talk. [17:11.360 --> 17:12.360] It was very nice. [17:12.360 --> 17:13.360] I have one question. [17:13.360 --> 17:14.360] I think currently it's version 0.25 of Gleam, or 0.26. [17:14.360 --> 17:15.360] 0.26, yeah. [17:15.360 --> 17:16.360] I'm sorry. [17:16.360 --> 17:17.360] This week. [17:17.360 --> 17:31.000] Are there any big hurdles before plans for 1.0, for example? [17:31.000 --> 17:37.320] I believe Lewis wants to get LSP features more properly implemented, but you can always [17:37.320 --> 17:39.520] join the Discord and talk there. [17:39.520 --> 17:44.400] I think Lewis is probably better, but I think we also have a GitHub milestone on the GitHub [17:44.400 --> 17:51.160] repository, which says what we want before V1. [17:51.160 --> 17:52.880] Any more questions? [17:52.880 --> 17:54.360] Okay. [17:54.360 --> 18:15.880] Thank you, Aria, again.