Cypert skillfully demonstrates how Gleam’s type system and the BEAM’s concurrency model turn the archaic complexity of SMTP into a clean, fault-tolerant architecture. This is a refreshing example of using modern functional tools to master foundational network protocols with elegance and precision.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
Building an SMTP Server in GleamAdded:
Cool, cool, cool.
Uh, this should be working. Let's go over here.
New private window. Paste the chat link.
And then move it to another monitor.
Yeah. Put that right up here.
Make this nice and big so I can read it.
Change it from top chat to live chat.
Need to do the same over here. There's like a freaking bajillion steps to get set up with streaming properly.
Uh, okay. Live chat. Cool, cool, cool.
Uh, I think we post a message in the Discord.
See if anyone wants to come hang out.
Uh, okay.
Pull OBS back up. Pull Firefox back up.
Such an involved process. Okay, that should be good, though. Uh, I'm gonna put some music on and then we can get started.
Actually, real quick, sound check. Just make sure.
Last. You're first. You're not last.
How does this sound?
Yeah. Okay. Audio sounds good. Cool.
All right.
Since you're first, do you have any suggestions on what music we listen to?
or I guess what I listen to. I'm afraid that if I play music through the uh stream, I'll get copyright violations.
So, I don't really want to do that. But, we can run um Cider. Could not think of the name of it.
And pick some music out.
Yeah, probably will get uh copyright violations. So, yeah, we're we're going to avoid that. We'll just start with some 90s.
Just shuffle this.
And then as always, need to actually connect the headphones.
You know you've made it big when you have a number after your AirPods Pro.
All right, cool. I think we should be good to get started. Um, all right.
Need to make this bigger.
Oh, why why why is it doing this?
Okay. And it snaps back. Um, Lord have mercy.
Alt right click anywhere in the window.
Okay.
Yeah. Why does it just keep snapping back? Oh my goodness.
Is it because it's No, it just doesn't want to get any bigger.
How visible is this? It's still pretty visible, right? I just uh need to make it bigger.
Okay.
Uh yeah, so let's maybe talk really quick about what we're just wanting to build. Um so I like protocols. I like things on TCP. Um I feel like it helps demystify how things work. SMTP is built on top of TCP. So I thought it might be fun to build a small SMTP server. Um was considering languages to use and I feel like Gleam could be really fun for this.
It gleams also running on the Erling virtual machine, the beam if you will, uh, which is excellent for reliability, fault tolerance, all sorts of other really great things, things that a mail server might possibly want. So, willing to make a trade-off for like a lower level language like Zigg or something that would be more performant because really, how performant do you need a mail server to be um, for something a little more reliable? So, figured this would be a fun little project and I figured we'll start there.
This diagram is actually really interesting. So there's an SMTP client, SMTP server. So our computer will be the client and then the server will be uh what we're building. Um and there's commands, replies, and mail. So I think what we can end up doing is essentially modeling the session that is this line, the commands that get sent across this line, and then the responses that get sent back from the server. Uh probably parse. Yeah, we'll definitely have to parse because it's built on top of TCP.
So, we'll write a a parsing layer.
Um, probably refer back to this document and some RFC's as we get going. I did start this uh actually let me just share where we're at. So, I gleam new and then a project called sheish um because she uh SMTP server in Gleam sounds nice. Uh and then this is it.
This is all we have. Um, so I installed glisten which is a uh package for TCP listeners and gleam. And then we're just pull and gleam Earl. Um, and then we're using Gleam Earl and Glisten to uh create a session here.
Um, and then listening and replying back with the message that's sent at this point. So, we'll actually have to do the SMTP stuff at some point in the near future. But for now, we can gleam run. We get our hello from sheish. We can hop back over here. We can netcat localhost and then send a message like bonjour. And it says bonjour back. And if we say yo, it'll say yo back. So, that's where we're at currently.
Um, let's also probably pull up the Gleam Docs and I'm going to close like some personal things like Gmails and eBay stuff and other things and then pull up the Gleam Docs.
Uh, probably go here and go ahead and pull up the contents.
Okay. Yeah, we'll jump through this as I start to forget things and have questions. I've written a little bit of Gleam at this point. Um, did their tour a while back. I did a first impressions video, wrote a little bit more after that and haven't touched it since really. So, um, there's a good chance that I've will have to refer to this a lot, but it'll be a fun learning experience hopefully for everybody.
Okay, I have uh what the heck is Gleam? It's a great question. I will tell you in just a second, but first, let me just turn on Dark Reader so I stop flashbanging people and people don't complain. Um, okay. Yeah. So, what is Gleam? That's a really great question. Uh, Gleam is this uh, really really fun, robust new language that is um, it's essentially this a friendly language for building type safe systems that scale. Uh, I almost exclusively write type- safe languages at this point. Um, if you are an AI enthusiast who thinks that programming will disappear, um, at least the way we know it, I still think type safe languages will be the future. It provides the compilers provide a lot of information that's both useful to humans and AI systems, but we're doing it all human today. So, uh yeah, type safe systems that scale. The most important thing here is that it runs on the Erling runtime or the beam. Um you can also compile it to JavaScript if you really want to um compile it to JavaScript. So one of the nice things about that is uh there is a um framework called luster that lets you write single page applications and serverside rendered pages in gleam uh that are you know compiled to native JavaScript. So that's an option too. Oh message retracted. No, you can leave it.
There's no wrong questions.
Um okay. So yeah, that's what Gleam is.
Uh, so we're going to build a a mail server running on the beam in Gleam. And that's our plan at least. So, let's hop back over here and I'm going to just make some files because I like the approach I mentioned here of having a session essentially and then having commands and responses.
Um, and if anything's not readable, let me know. Kakai, hello.
More Gleam content. Yeah, I um had just mentioned this. I'm not sure when you got in, but I really like the language, but I've just done so little with it after finding the language and doing my first impressions and then building a couple small things with it. So, I thought this would be fun. And I've never built an SMTP server before. So, um yeah, double win, I guess.
Uh okay. So, let's touch some files.
We're going to touch source session gleam source uh command gleam and source response.
Uh so we can hop into the session. Maybe you just got here. You're happy for me.
That's nice. I am also happy for me I think.
Uh, okay. So, God, it has been so long since I've written Gleam. Uh, types SMTP session SMTP session. And then we need fields state session state. Maybe we should have a from.
Yeah, because this would be the client that it's coming from or rather not the client that it's it's not necessarily the client that it's coming from. This would be the huh okay so essentially every SMTP request is a standalone TCP request. So that session state will model the data that comes across the TCP connection.
So I think from is optional.
I'm actually very curious now.
uh RFC 5321. Let's go take a look at an RFC, specifically S&P TP terminology, transport history, host, domain names, mail agents, buffer, commands, and replies. We'll probably need to refer to this Uh, honestly, 94 pages for an RFC is not too bad.
Basic structure. Yeah, let's start here.
Maybe let's take a look at what the Yep.
So, this is what was in the Wikipedia page. This model, when an SMTP client has to transmit, it establishes a two-way transmission channel to an SMTP server. The responsibility of an SMTP client is to transfer mail messages to one or more SMTP servers and or report its failure to do so. Okay, cool. Simple enough. This means by which a mail message is presented to an SMTP client and how that client determines the identifier's names uh to be transferred local matter not addressed in this document. Interesting.
Okay. Extensions. What else we got? SMTP terminology, mail objects, commands, replies, host, mail agents, senders, and receivers.
Okay.
Sess session initiation. SMTP session is initiated when a client opens a connection to a server. The server responds in the opening message. This is the hello command. H E L O if I remember correctly.
Um formally reject a mail session. Okay, I don't think we'll have to worry about that today.
Yeah, the client normally sends the eh hello command to the server indicating the client's identity. In addition to opening the session, use of e-hello indicates that the client is able to process service extensions and requests that the server provide a list of the extensions or e yeah I guess e-hello I guess is fine. Um older SMTP systems that are unable to support the extension and contemporary clients that do not require service extensions. The mail session being initiated may use hello instead of e hello. Servers must not return the extended hello response for a normal hello command. Okay.
All right. So we'll have to do some parsing of the commands that are coming in.
Yeah. Okay. So this is the from right.
This is what I was curious to see.
Uh, okay. I do think this is necessary, but I think for our state it can be an option because it could be omitted from the state and if we're building up this like uh SMTP session as we go, we might not have all the fields available up front. So I I think we'll just stick with that for now. Um uh that's not correct at all. It's a list string and then um our data lines will be a list of string as well.
Uh okay.
Whoops. Wrong thing. Uh code actions. No code actions available. Love to see it.
Do we have Gleam installed? Enabled languages. Gleam. Yes. Okay.
Interesting. Um, import gleam.
Uh, option.
Imported type is never used. It's used right here.
Huh.
Okay. Uh we'll we'll just have some errors for the moment. Uh session state.
Um so we have that initial greeting.
We have um I guess once the greeting comes through and we're ready to accept, we could just say ex let's just go with ready.
Um first step in the procedure is the mail from command. So we'll just say mail from. Uh the next one is recipient RC pt2 and our data. Let's comment these. So this will be when we are collecting body lines.
This will be after receipt two. And the receipt 2 can be sent multiple times, right?
A list of source routes in the forward path. Okay. So this can be looped. Mail from will be after mail from and this will be after e-hello or hello Gle puts the comments above the line, but you can add dot comments to the variance with slash. Uh, thank you.
Yeah, I'll just I'll put them above. Um, that's doesn't bother me one bit. I just wasn't expecting that. So, uh, had to reformat, I'm guessing. Okay, the imported type is never used. Is that because the thing that's using it is never used? I I would assume that's the case, but um Okay.
How do we write tests?
I don't see anything for tests.
Uh, three warning emojis.
You can add a test directory.
The assert at the bottom.
Let assert.
H. I was really looking for like a full test runner. I'm pretty sure I've done this in the past, right? Gleam test. Oh, we have a test uh from generating the project. Okay, cool.
Uh assert greeting. Okay, so they're just functions that end in test.
Okay. And then multi-line strings. Multi-line strings.
Uh tests are just functions ending in tests. They need to be public. Okay, simple enough.
Schle support multi-line strings form feed. Dude.
Oh, wow. Uh, people are popping in. Uh, Gleam. Yeah. Yeah, we're doing a little bit of gleam today. I'm trying to trying to um remember how to write this language. Uh, okay. Just regular quotes for double or multi-line strings. Okay.
Simple. Oh, literally there's an example right here. I should read uh W lock. Hey, great to see you, man. Um glad that you are uh popping in. We'll have some fun with some random stuff today. Um so, specifically, I'm going to create a function that is going to uh send e hello test. So, something like that.
And then we'll do a um something like this will be the string that we'll send in.
You shared the live in the Discord.
Maybe the core team will come visit. Oh, the pressure's on.
Uh hopefully I I don't embarrass myself too much in front of the live team. I like this language a lot from what I've done with it, but I have not done enough with it. Um, okay. So, we'll take this string and essentially I just want to pipe it into a function and I want to pipe this into a like parse.
Parse doesn't exist yet. We'll have to figure out where we're going to import that from, but this will be in command. Maybe I didn't use your Discord link. I just saw in my subscription a Gleam live. No problem. They love to help and so do we.
Yeah, that's great. I The community around Gleam seems very very friendly, which is always nice.
Okay, so interesting. So, Glisten's going to give us raw TCP packets. So, we're going to need to buffer those as they come in.
Um, to be sure you have updated the latest version of Gleam since 116, the LSP got really better. To be honest, I don't know. Let's find out.
Yeah, 116. We're there.
Uh, let's see.
Let's start with hop into she.
Uh, okay. So, we essentially need like a bite buffer. So bytes, bites, bites.
This might actually just be in the standard library.
byte size byte array bytes tree.
We probably want a bytes tree, right?
Efficiently building binary content to be written to a file or socket internally is represented as a tree. So to append a prepend to the bytes tree is a constant time operation that allocates a new node in the tree without copying any of the content. Okay.
Having a funny/cute name is actually pretty common with gleam libraries.
Yeah, I'm I'm here for it. I've noticed a lot of them start with GL, which is uh good as well because they share the um doc site with hex docs, right?
or hex the hex package manager.
Uh, okay.
I got to turn cake down. Hard to focus.
Uh, okay. So, incoming data is going to arrive What are we looking at? So, we have a packet and that packet is a Oh, it's a bit array. Okay, cool. So, we can work with a bit array.
Um, Gleam is theoretically such a cute language, but I always end up wanting to write Rust when I use it. I noticed enough similarities that um to be honest, Gleam, when I did my first impressions video with Gleam, I enjoyed writing it and I had a couple people be like, "Wow, this is actually really similar to Rust." And it caused me to reive Rust another shot. Uh and I've been writing some Rust, some Zigg, some Odin lately. Um really to be honest, I'm a little exhausted from lower level languages. So looking to kind of get back to something a little higher, but I definitely see the similarities between Rust and Gleam.
Uh not everything's named with GL, but I must admit I named my index DB opinionated library Glendex. That's a good name.
Um okay, cool. So let's uh let's think through this. Uh so we have our packets.
So we have our bit array. We have our byte tree. I think we need to import. So import um What would be the import? I'm going to assume it's Gleam something bit. Yeah, Gleam bit array. Nice and easy.
So, we essentially have a message.
I think I'm going to create a function down here. Handle message uh handle maybe. What is this? So function what do you get user message option selector user message. Okay. So, we'll change this to something like um handle maybe message and it's an option uh selector.
Where did that come from?
selector process gleam selector payload. Okay, simple enough. So, process gleam uh process selector. Does that just exist as is?
And man, I I've already forgotten.
User message. What is user message?
So we have an onet. It takes a user message option selector user message. So user message is just a variable name in this pla case, right? Okay.
So this just be option selector then process selector. Uh, similarity comes from basically all new languages and a bunch bunch of old languages like C adopted pattern matching and record destructuring maybe but pattern matching was already in elixir and Erling process comes from the gleam earring library.
Okay, cool. That makes sense.
Um, case not match trying to remember the lingo. So if we have uh packet uh glisten no we're pulling in packet.
Okay. So if we have a packet with a message um you said let assert looks like assertions. Okay.
Uh let's look at the bit array.
Uh, that's Oh, I wish this was a link.
That would be so nice if this was clickable.
Okay, bit array uh gloom standard library is really about things agnostic from the platform. So, it's not here where you found the pre-made stuff working for JS Promises and Erling OTP stuff. is not here where you found pre-made stuff for working like with JS Promise or Erlang OTP stuff. Yeah, I noticed that um the platform specific pieces are this is what I want. Uh the platform specific pieces are in their own sections of of Gleam essentially not necessarily part of the standard library. The standard library itself is platform agnostic.
Uh Rust inherited pattern matching from okamel which was the first Rust backend.
Was it really? Oh, camel first.
Wild. I did not know that.
38,000 lines of Okamel code.
Um, okay. Which is probably which then popularized it. Uh, pattern matching.
Yeah. I mean, uh, am I crazy? Scola has pattern matching, right? It's had it for a long time, actually. I don't know if it's had pattern matching for a long time. It's had matching, but I don't know if it's been pattern matching. Uh, Scola 2.
Yeah. Okay. Yeah, it's had it since 20 I was writing Scola 2 in 2013. Not saying this was in that version of Scola 2, but Scola 3 came out in what, 2016, 2017?
When did this come out?
Which I guess uh pattern matching has been a thing for a long time. Yeah, I guess I I don't know why I got hung up on the comment that um the first Okamel compiler or the first Rust compiler was written in O camel in 2006, which is probably why pattern matching became so prevalent in Rust.
Um hopefully, sorry if you hear the sirens. We've got windows open and we live very close to a fire station.
Um yeah. All right. Anyways, sorry getting sidetracked as I have been known to do many many times now.
Prologue that is a language I have not ever taken a look at.
So we should be able to dstructure the text from the bit array to string.
Uh and we need to pass in the bits. The bits. The bits. Let's change this. This is I don't know if shadowing is okay. Okay, I would assume it's okay, but I'd rather not shadow if we can avoid it. So, we have our bits and then uh what are the other cases for this message option option process selector.
I actually I'm sorry I what are the other options?
Oh user interesting. Uh okay so these are any messages received from the selector returned from on a net. So we need to handle both of these cases. So we handle the packet and the user. So we have user user message. I don't have a clue what we want to do in that case. So for now let's just do we have to do we do have to do. Let's to do it. Nice.
Uh user is not in scope. So we can come up here and add user.
We're missing an arity here.
Um selector.
There's a payload. Whoops.
I'm actually really confused.
Uh, the Gleam LSP is your friend. I think I have the Gleam LSP installed.
Maybe I don't. Um, control F. Show me Gleam. No Gleam.
Gleam. No Gleam.
Bummer.
Um, Gleam is in the Gleam LSP or the Gleam LSP is in Gleam maybe.
I guess I'm confused. I think uh my Vim setup has some support for it. Like I've got LSP info. like the Gleam LSP appears to be enabled uh right here.
I don't know to be completely honest.
I've been using Zed a lot lately and um I have no idea why I don't want to use Zed on stream. Maybe maybe I feel like it's proof that I can't handle Vim the way that I should be handling Vim. Um okay, uh user message. So uh this would be what a bit array then maybe expected type option option process selector bit array.
Okay, we have a type mismatch. I would love to see more.
Found glisten message A.
Oh, it takes in a payload and you think it's generic. Sorry. Is that dot dot dot?
Can you just No. Okay. I was like, there's no way that just works. Uh, okay. That's that's fine. I need to actually look at the docs and make sure that I'm understanding this.
If you were using code action the on message the LSP would generate a branch option sum and option none.
Oh, it's an underline. I see.
Uh okay.
Should I just use glisten message here then if we are just taking in the message? Let's try this.
Uh, okay.
Um, what do we want to do with the text? So, text is a string that I can parse.
So, let's just do uh IO print line text.
And then we need to wire this up.
Um, I don't know if it's just me, but I don't really like all these plugins and extra clutter. I just compile and follow errors, but that's just me. Um, yeah, I have a lot of respect for you for doing that. I I can't I have tried before.
I've tried going as as uh primitive as I can with like even even going as far as to use like Notepad++ or something. And I I really struggle with that. Having the LSP, go to definition, syntax highlighting with tree setter, that type of stuff just it helps me tremendously.
Um, for me, it's just it helps provide context that I don't have to keep in my head and I can just see at a glance or go to very quickly. Uh, so that's been super helpful for me. My hunch, however, is you are better at working with that context than I am. And you are probably better at the languages you write than I am better at the languages I write for that reason. So if you were given like a piece of paper and asked to write a program that did did something, I guarantee you would be better at doing that than I would because you just don't depend on the tools like I do. And I think there's nothing wrong with either.
Personally, I think both are very very fine ways to approach development.
Uh okay. So we have our function here.
So what I want to do is essentially call this, right? So we can just call handle maybe message. We can pass in the message. So, we don't need this, but we do want to get something back, right? So, handle maybe message. For now, we'll just have it return the message.
Oops. Yeah, let's do our let.
And then we'll come over here and change this to message. And we'll just plug this in and make sure things are working.
Uh, so we hit this branch. And then what we need to do is return from bit array. So we can just return the bit array in this case. So just return bits.
Okay.
Uh oh yeah, our tests are invalid. You know what? Let's just remove that for now. Uh so this would be test she test.
Yeah, that's fine. We'll get to parsing later. One step at a time.
Uh, hint. I think its type is bit array.
Uh, okay.
Safely remove it. Safely remove it.
Yeah, that's fine. All right. Just really quick. Netcat. Make sure nothing's busted. Hello. We get our hello back. We see our hello over here in the logs. Excellent. Excellent. Okay, cool.
This feels not as impactful as I feel like we should be at at this point, but that's all right. So, we still have our glisten pieces. I think we'll still continue to let this do its thing. Um, but now that we're in this part, we can actually start to process things.
Can we get Okay. Uh, this is a glisten question I suppose, but can we get multiple when does this function run? Does this run on every TCP packet? Does this run like do I need to manage the state outside of this?
H uh, okay, Gleam doesn't really need any plugin. If you're just into your LSP and you make your editor run the bug the in the bugger the Gleam LSP command, but when I was doing some Zigg, I do agree doing stuff then running build was better. Have a difficulty with visual noise. I just have docs on another workspace and I switch between them as needed. It's a language problem for me.
We used to only need a notepad or a piece of paper. To be honest, I don't care about LSP or not. I just want a nice form editor that auto formats for me. Space and new line when I save.
Yeah, that makes sense. Um, if you're new to my channel and content, I'm I'm I don't like title myself much, but I will title myself and say that I'm I'm a polyglot developer. So, I bounce between languages a lot, and those tools make it very very easy. Well, I shouldn't say very very easy. The tools make it reasonable for me to bounce between languages uh because of the assistance that they provide.
Okay, so TCP is going to deliver bytes in arbitrary chunks. Single SMTP line might arrive as two packets. So we have to carry a buffer. Um is that true? Let's let's just see how this behaves. Uh yeah, run this. We'll hop back over here. And then we need like a really big payload that we can send over. So it might be something like elo client.acample.com.
Did that crash?
um something like data from the client and then subject hello.
So the server would respond that would be okay. We'd say that we have more data.
Um, sure. Subject hello body text. Wait, no. The client would then send another carriage return. Oops, that's invalid. But, and then we'd send something like body text here.
And then the client would finally say something like quit.
All right. How does this come through?
What is what's going on here?
So, this function runs on every single uh Oh, it's rendering the blank. No, what is Why is there an extra space here?
Hm. Uh, Brad, Polyglot, developer, king of the Andals and of the first man, breaker of chains, mother of dragons.
I'm still amazed you can do STP, SMTP with only netcat. Thank you, Wlock. I um appreciate the sentiment. Also glad to see you're still here. I'm still amazed you can do SMTP with only netcat. Yeah, I mean, so it's it's just TCP, right?
Like SMTP is built on top of TCP. And as long as we're not doing anything with like um SSL or TLS, which I don't plan to at least today, uh there's yeah, you can just pipe in things. And for what it's worth, like nothing about our server is SMTP at this point. This message that we sent is like like technically this is kind of valid. Um but nothing about our servers SMTP. It's just TCP at this point. I just randomly stumbled upon the channel 2. Wanted to learn Zigg 2, but the compiler gets in the way too often. the same experience I had with Rust. We'll probably try again later. I don't know if that will ever change. I think the languages are very low level. They're very um focused on giving you as much safety and correctness as they can with uh letting you get super low and do really powerful things. And the way that they get that correctness and the way that they get that um safety is is from working closely with the compiler. So if you find that the compiler gets in the way too often, I don't know if it will get much better um ever, unfortunately.
Uh but I think that's part of the trade-off we make when we choose languages like Zigg and Rust.
I'll try not to spend the entire time talking about Zigg. That's much much easier for me to do, though.
Um, okay. So, we actually have to send status codes back, too, don't we?
Because there's like what? 250.
Okay.
Okay. 550s, 553s. Oh, this is going to be bigger than I expected.
Um, How do we send?
Okay, I need to see responses because we have Okay, SMTP replies. We have reply codes. SMTP consists of a three-digit number followed by some text. Okay, cool. That's what I was wondering. I was like, there's no header or anything, right? Like there's no way that we could like this is an HTTP so we can't do that. So it's just three-digit number followed by some text unless specified otherwise. Okay. I have to go eat. Good luck with the mail server. Personally I gleam a lot but I haven't touched glisten or anything related to the beam process or OTPs.
I'm very curious what you use if not the beam process and OTPs pieces. Um are you are you writing glisten or sorry you writing gleam and compiling to JavaScript and wonder what the sentiment on Mojo is? seems promising if you read the website, but the whole Pixie thing is weird and the standard library seems a little shallow. I actually know very little about Mojo. This sounds nice though.
Write fast code for diverse hardware from CPUs to GPUs without vendor lockin in a language that's both friendly and memory safe. And I should probably make this legible for you all.
Sounds neat. This looks cute. Um, yeah, this is what I want to see. Show me some code.
This looks kind of gross. Um, okay, cool. So, adding vectors together using tile tensors.
Looks like a type here. Another type of to get a type element size.
result type maybe indicating it's mutable with those subtypes.
Yeah, to be completely honest, I don't really know what's going on here.
Uh, Python syntax is rough, too, but it's meant to be compatible with existing Python code. That's ambitious.
Oh, yeah, literally right here. Uh, that's ambitious. Um, but neat being able to essentially promote from the Python runtime to a different runtime that is better. Sounds really appealing, especially in this day and age.
Oh, 1.0.0 B1 beta 1. That seems promising.
Compile time meta programming. Love to see it. Love this keyword.
What's the pixie stuff? What's What's that?
P I X I Okay, package management tool for developers of all backgrounds. Kind of makes me think about MIS.
Looks like it's Python specific maybe.
No, it's not Python specific. Yeah, interesting. Just seems like an alternative to Mi. Pixie is a package manager for Python, I think. So, it all runs in a Pixie shell, not directly on the command line. That's interesting.
Um, no, I'm using Gleam with both Erlang for backend and JS for front end.
Luster. Nice. But I do web stuff, which needs a web server, which I use Mist for, which makes my life as simple as if I were on Node. Mist is built on Glisten for what it's worth. Uh I've done a little bit with mist and luster as well.
But yeah, mist is built on top of glisten. So technically you are using glisten but you might not be using it directly.
Um at least it is not routing but we have pattern matching so who cares.
Yeah, single routes sufficient.
Pixie is a package manager for Python.
Yeah, we just mentioned that one. Python type annotations got me interested but then it just led me into other languages. Yeah, I think um same. Uh so when I was looking at picking up the beam again, I wrote elixir for uh I was I was at a company that was an elixir backend uh elixir and ruby back end and I was mostly doing front-end stuff for them, but I was also doing some Elixir uh work as well and that was uh three years at a company doing that.
So, um, I didn't much care for elixir at the time, but I grew to respect the beam a lot. And when I was looking at Gleam, I had to ask myself if I should just use uh, Elixir instead. And I looked at Elixir's type specs and the Elixir type specs. Pull these up so you have some context in case you don't know what these look like.
But yeah, they have these uh type specs that I just just do not care for. Um, and additionally, it's not fully typed.
These are optional. So, it's a dynamically typed language and such type specs are never used by the compiler to optimize or modify code. They're useful for these reasons. Um, and mainly dializer. So, you can analyze code with type specs and find inconsistencies and possible bugs. But both the syntax of this and also the fact that it's not like tightly embedded into the compiler, I want statically typed languages with strict compilers.
Um, I would rather fight a compiler during 9 to5 than have production blow up at 2 a.m. because someone double indexed into a map of maps without checking that the first index actually returned a non-null value, which is an actual thing that I have been paged for before.
um in Ruby for that matter. But uh yeah.
Yeah. What I wanted to say is I'm not using actor and not spawning manually.
New earling processes. I still got my feet out of the water. Yeah, I get that.
I to I totally understand. Once you start getting into the OTP stuff and set up actors and um Oh my gosh, what is the other agents? Uh OTP agent. Um I'm not losing my mind. I swear.
Maybe they're not called agents. Maybe maybe I am losing my mind. Phoenix elixir, show me what you got. Show me what you got.
Um, my view security.
It's not a Phoenix thing. It is an elixir thing.
Also, my music has stopped. Uh, docs, maybe.
Shoot. I don't know. There's something in here that uh seems really appealing on the yeah agent.
So they're essentially like multiprocess multi elixir process or erling process uh states that are sharable uh which seems really cool. Uh gen server is just so fantastic.
And then processes, tasks, supervisors, actors, all of that fun stuff.
All right. All right. We man uh you guys, you all have sidetracked me something fierce. But that is okay. That that happens. And to be completely honest, I would rather hang out and talk about these type of things with you all than make a ton of progress on this project. So, but I will try to make some progress on it. Okay. So, we're handling maybe a message. We're addressing that completely. I still don't know where my extra new lines are coming from, but I don't necessarily know if that's an issue at this point. Um, so let's see.
So, we do need to carry a buffer in my session state and then accumulate it until I have a complete terminated line.
This is interesting. An interesting problem is if multiple people are sending these connections over, we can't just have a single shared buffer, right?
I guess for now we could and then just address that problem when we get to it.
Uh you call IO print line after each message.
H. Okay. Yeah, I bet you're right. I bet if we just change this to an IO print, that would actually fix the issue.
Fire this back up.
Elo should terminate, right? Okay. Good, good, good.
Oh.
Oh, I didn't run the Oh my goodness.
elocclient.ample.com.
Okay, that came through. Um, it doesn't really matter. We'll just say hello. Do we have spaces?
No. Okay. Yeah. Yeah. Cool. Good call.
Good call. The line breaks being sent over here being replicated over here as well.
Uh yeah, so a message would include the carriage return line feed usually to which you just add another line. Yep, that totally makes sense. Thank you for that.
Uh okay, one catch to think about. So a single packet is going to be individual lines, but it could be multiple lines.
Uh Um, coffee really quick. I'll mute so you don't have to hear me drink.
Uh, okay.
Yeah, mic's still on. Good, good, good.
Um, let's let's hop back over to our session. Sorry, I'm trying to like wrap my head around how we want to approach this. uh since we need to take in essentially a buffer and we want to keep adding to that buffer until we have a full message and then process like parse that message. Um which makes sense and then it starts to get a little complicated when we think about multiple people could be connecting to this TCP server. So we need some indication like some way to separate those messages I I feel. Uh, but I guess for now we don't really have to worry about that since it's literally just going to be me trying to send a message from the SMTP server. But I do think one thing that would be useful is if we hop back over to the session and then create just a new function to return an SMTP session since we know we're going to need that.
It's going to return an SMTP session with state. Uh this it'll be greeting since we're just starting out that buffer um needs to be added to our session here.
And we can just make this a string.
Yeah, I think so.
That buffer will just be an empty string. From will be option.none by default. two will be an empty list by default and then our data lines will also be an empty list by default. I think that's the correct syntax.
Private type used in public interface.
Yeah, these need to be public, don't they? Public type.
Oh, uh I probably just need to save for it to pick that up. Yeah. Okay, cool.
That makes sense. Um great, great, great.
So we have this. We haven't really let's let's let's uh let's look at commands.
So we don't have anything here for commands. You could use a buffer pool given per connection.
We will come back to that. Thank you for the suggestion.
Um, we have to import strings, don't we?
Gleam string. Yep. Okay.
Uh, I'm going to just focus on the commands. So, the commands are the things that can be sent over the wire.
And then we can kind of get into the parsing piece. Trying to break this down to one small piece at a time so it doesn't feel as overwhelming as it currently does. That's SMTP, not SMPT.
What else? What else we got? Uh, ELO.
These have a domain associated with them which is going to be a string capital s capital s um a hello which is also a string a mail from which is an it's a string right? Uh it's address I guess is a good term for it. um recipient to RCPT2.
This is also going to be a string. I know that we can take in a list, but each individual message will either represent it as a string and you will send multiple messages and we'll accumulate those into a list. Um yeah, I think that makes sense. data.
I think I've looked through this RFC uh a couple days ago and there's like isn't there data line? Am I crazy?
Data. Data. Okay, we'll just go with this data.
Treats the lines.
Is this the Krusty Krab? No, this is Patrick.
Um, I think we can treat data and data line as the same. Uh, although it's not an address anymore. It would be um actually Oh, okay. I see. I see. Yeah.
Sorry, this is weird. Uh we had an example that I sent over here earlier, but essentially like there's data and then there's like a a data line if that makes sense. Um so we'll want to handle both. So there's data which has no uh fields to it. And we have not data type. We have data line which will have a line which is a string.
We also have a quick command. And then there's a case that you type in nonsense. So we'll just call this unknown.
And you can have text with it. So we'll capture that text to just for debugging purposes, I guess.
Okay. This is actually the part that I'm more excited about. This is really what I wanted to get done today. So, now that we have this, I just want to parse this.
So, I'm just going to take in a string and we're going to return an SMTP command. And my understanding is that our tests Oh. Oh, write and quit. And it added the to-do for me. That's nice. So, I can create a test and then I can create a command. Gleam.
Is that right?
We'll follow that pattern. Command test gleam.
Uh commands test gleam.
Have you used earring before? No. No, I have not. Um I earlier in my career, I had the opportunity to write some Erling and I found it to be very repulsive to be completely honest. Um, and honestly, I was writing Java and Groovy at the time, and I, um, it was just so different. It was so different than anything I was doing. And it was very early in my career, and I was not as willing to, uh, try new things and, and just make a fool out of myself trying um, a new programming language in front of I have no idea how many live people we have right now, but my analytics say that we've had 166 viewers at some point.
Uh so yeah, used to used to be way more reserved about that. Is Gleam supposed to be a functional language? Yeah, it is. It is a functional language.
Um I have already forgotten what our tests look like. So we're just going to pull this up over here. We'll hop back over here. So we're going to import glee unit. I'm going to type this out instead of copying it in hopes that it will make me actually remember how this stuff works.
Yep, you have data and functions. That's all you need.
Hey, uh Kai, question for you. I'm looking at this um test that was autogenerated and it says pub fun main and it returns nil and then calls glee unit main. I don't need that in every test file, right? Like if I create a new test file, that executor should pick up all of my tests for me. Is that correct?
I would assume so.
Nope. To I no, I don't need it or no, I'm wrong.
Only in the module test.glame, it calls the testr runner. You just need a function ending in test. Okay, cool. I can do that. I can I can totally do that. So let's write some tests. So we have a parse elo e-hello test which will be something like this.
And then I would assume I need to import command uh source command. Does that work? How do I import my Maybe the LSP will help me. We'll try that.
So I've got a string. The string will be something like um elo uh bradciper.com.
We'll go there. We'll pipe this into parse and then um we should be able to pipe that into assert.
Is that true? Is assert a keyword? Uh does Gleam have a for or while loop or it's recursion for all? just recursion, but you should probably check the standard lib before doing that. No loop, just recursion. Usually map, fold, scan, etc. Calls the test runner, searches for all the tests in the directory. Okay, cool. Makes sense.
Um, asserts asserts pattern can be partial.
So keywords I can't pipe into assert like I can a function, right? I would assume no.
which I guess if that's the case, uh let assert as a keyword. Okay, yeah, that makes sense. Um so in our case then we can just let bind this to out maybe and then assert out is got to go study for my oncology exam.
All right. I hope you have a great day.
I hope you do as well. You can pipe into echo but I think that's unique magic.
That makes sense. Uh, so I want to assert that out is and I want to do a essentially a type assertion.
Show me.
Yeah, also for the person who asked about loops, there's information right here on recursion. Um, actually, sorry, this probably isn't very big. Let me make this a little bigger. There's information on recursion, tail calls, list recursion, um, stuff of that information that might be useful.
custom types.
Can I just assert that the type is equal to um what did we call these? SMTP command ELO code action. And yeah, not able to import. So, how do I import my code?
You can assert on the enum fields. Yes.
Okay, that's what I want to do.
Tuples, record pattern matching, record accessories, record updates, nil, results, spit arrays. Probably want this.
Oh, would it be?
If I type correctly, that might come out better.
Uh, whoops.
Oh my goodness. Please.
Random idea that might be relevant.
Let's make sure these are public.
Uh yeah, they both are public. Okay.
Sheesh.
If my enum is public and on the she module just got home and open the streamed again. Um do I need to God did I make like a silly mistake and forget to put it in the appropriate module?
A module is a bunch of definitions that belong together. IO print line. All Gleam code is in some module or the other whose name comes from the name of the file it's in. So, Gleam io. Okay.
So, in theory, can I just come up here and import she slash uh command? Yeah. Okay, cool. Uh, no.
Unknown module.
>> Okay. Um, SMTP command unknown.
Import she should work.
Sheesh. Elo is a constructor for an SMTP command variant. Okay, that that makes sense to me. I guess the part I'm confused on. So this file here is in command.glleam.
This is under source command. So would this not need to be like sheish. Command or like would I need to import she command or just import command? Okay.
Oh, okay. Yeah, yeah, yeah. Perfect. Uh, and then this would be bradciper.com, right?
We are importing command unknown variable the name.
Uh, show me line diagnostics. Parse is not in scope here. So, do we just need to pull in parse as well?
And I don't need the gleunit module.
All right, let's run this and see if it actually executes.
Sheesh, I did not expect that to be so difficult.
Let's run gleam test.
To-do expression evaluated. This code has not yet been completed. Uh, that makes sense because our parse is entirely a um a to-do. Yep. Okay, cool.
So, we are calling parse.
Sheesh.
All right, cool. Man, uh I am so sorry that we struggled through that. Thank you so much for your help, though. I really do appreciate it. I honestly though, like to be completely honest, I feel like there could be more information on running tests in You know what? I'm just in the language tour.
Maybe that information is elsewhere.
Yes, I I would imagine it's in this writing gleam. Do we have any information on tests workflow test var test v tests tests testr runner latest versions testing your code. Yep. Okay. I just should read the docs. My apologies. Thank you for being the docs that I should read. I appreciate you all tremendously.
Um, format pair function call the format pair function for the test directory where test. Okay. Yeah, I'm I'm so sorry. I should not have I just uh for a minute was just assuming that the language tour was like every piece of documentation and that is not true at all. Um I should read the docs for writing gleam and uh if other people are interested I would encourage them to as well.
Thank you again for your help. All right, so let's parse. This is the part that I'm excited about. I like writing parsers. It's been fun to do in Zigg.
It's been fun to do in Rust. Um I guess I've technically written one in Dart as well. So yeah, that's uppercase.
So we want to essentially when you're writing a parser, if you can, if it makes sense to you, you want to normalize your inputs. So in our case like whether it's lowercase ELO or higher ELO or hello or mail from or whatever I I'm pretty sure we can just normalize it. So in our case I want to uh uppercase all of this. And I think that in in our case simply because 1 2 3 4 5 6 7 8 9 all nine of these all 11 of these 10 of these however many it is are four characters. So we can also normalize this even further.
Um, oh that was weird. I'm not sure what that was.
Uh, by the uh check out the writing gleam docs later. Pretty helpful.
Helps with conventions. Yeah, I will I will read through that probably right after this. I don't really I don't know.
Maybe it's a good use of everyone's time to to read through it with me, but I don't want to force that on people. Uh, by the way, you should probably move your code to a she namespace, just a folder. Um, yeah. Okay, I'm open to doing that.
Uh so string um we want to essentially take the substring no substring slice. Yeah, that works. Uh take a substring from a given graphim index and a length. Negative indexes are taken starting from the end of the string.
Yep. Okay, cool. Makes sense. I do need that to get out of the way though.
Um, so we'll take in our line and then we just want zero to four. We want the first four characters. This is our upper.
This needs to return an SMTP command. So I'm just going to go ahead and put unknown here at the bottom so it will quit complaining.
No. SMTP command. Unknown.
Can I not just do unknown? Oh, I need to provide data. Um, sure. Yeah, that makes sense. My apologies. Okay, so we normalize this. Now, uh, we can use our beautiful matching syntax here. And we can handle ELOS like this.
Um, hellos will be the same. We'll go ahead and write these two together since they're almost identical. Actually, most of these will really be identical, but we'll write these and we'll actually adjust our test or run our test. Really, our test shouldn't need adjusting if we do this, right? So, string and then I essentially want to slice the Oh, hang on. What's that? Drop start.
That actually might be exactly what I want. Drops in graphimmes from the start of a string. That is perfect. Yep. So we'll take in the line and we'll drop the first four and then same thing here.
So this will be string trim and then string drop start line four. Actually we will probably drop start like crazy.
Uh and then we need one more pattern. So um empty empty pattern will return unknown and then uh just the input I guess the line that is inputed in and we don't need this anymore. So now we should be able to run gleam test.
It didn't fail, which is good. But I'd like to make it more prevalent that it did pass. Okay, cool. So, two pass, no failures. Huzzah. Let's go to our tests, command tests. Let's add another. So, what we want to yank six, yank seven lines. We'll come down here. We'll paste. We'll add another one. And this will be a parse hello test. This will be a hello.
And we'll do lowercase on this since we're normalizing it. It should be good there. And we can change this from an ELO to a hello.
Uh oh my gosh. Uh sorry, I'm missing a lot of stuff. Unknown takes a parameter.
Yes, you are right. I'm glad we got that. Unknown with a string. Yep. Can anyone tell me what is Gleam? I am new.
Yeah. So, Gleam is a functional programming language written um it runs on it's a multi-host functional programming language. So, in a way it's similar to Closure in that sense, but honestly I shouldn't assume people know what Closure is. Um it can compile to code that runs on the Erlang virtual machine, the Beam if you will, or it can compile to JavaScript. But it's a highle functional programming language with an intentionally simple uh syntax. Like the amount of keywords in Gleam I think is 18 18 keywords. Is that right? It's very very small. Um but small doesn't mean not powerful. It's uh I wish I remembered where I saw that it was 18 keywords.
Um but yeah, small small does not mean not powerful. Uh so it's it's really really nice. Um, the beam itself is a fantastic deployment platform, uh, created by Lewis or Louie Pillfold.
I'm not sure how to pronounce whether it's Louis or Lewis, but um, my apologies if I get it. One of those is wrong. So, my apologies for getting it wrong on one of them. But, yeah, somewhere they had like an FAQ, I think maybe, where they were talking about what the language won't be.
Um, and that's when they started talking about how small the language was, but I don't I don't know where it's at. I'm not going to troll through their docs, uh, to try to find it right now. Uh, what other questions? You might be able to pattern match. Uh, oh, SMTP server.
SMTP server is a server for sending emails. Uh, simple mail transfer protocol. So, Wikipedia page here if you're interested. And then there there's an RFC that's linked right here, 5321. If you really want to get in the weeds, there's like 98 pages of how this is supposed to work. Um, but yeah, this is used to send emails. So, and when we have this finished, which I don't know if we'll have it finished today. We've already been live for an hour and a half, but uh the idea would be um that I can just connect to this using netcat and send an email uh that will get proxied to relay servers and then um in theory I could send an email to myself, which would be pretty sweet.
Um okay, what other questions? What other questions?
You might be able to pattern match even without the slice and draw it because you can do the concat syntax to lay out all the chunks in a match. That is smart. I was going to say clever, but clever is sometimes bad and that doesn't sound bad at all. So, what you're saying would be essentially instead of slicing here, we could do like this and then like uh domain like this. And then over here we could just do probably still want to string.trim it, but then we could do domain.
I think that is a great idea. So what what did I do wrong?
Um uh command I also think that should work, but I don't know why it's not.
Uh, okay. chats coming through. Does Envim have anything over normal idees? I see everyone using Invim, at least YouTubers, but it seems learning curve in quite steep and not sure if it's worth learning. Um the the if you have not used Neov and you want to start using Neovm, your first week will be pretty miserable.
uh after that you will you will basically probably I shouldn't assume that you you will follow the exact same path that I did but after the first week I was at the point where I was about as proficient with Neovim as I was a normal IDE and then the part that has been nice for me afterwards has been um modifying things to fit my needs in a way that I could not do with an IDE.
So yeah, if that's appealing I would check it out. I use lazy vim which is a dro that is really really nice. Uh so it's this this looks really really similar to what you see on my screen because to be completely honest uh the customization that I've done after the fact has been quite minimal.
Um so yeah lazy vim it looks nice out of the box. It works really nice out of the box. You have a bunch of really nice plugins. So like I can hit space twice which I think is the default key binding for this but it lets you search files.
If I hit space sg, I can search with GP.
So if I want to find like um glisten, I can see everywhere that glisten is used.
Um and one of the nice things about this is like this actually it says search GP, but mine's using RIP GP behind the scenes, which is just a faster implementation of GP. Uh so you can get performance with that that you wouldn't be able to get out of an IDE most likely. Um there's other searches. So, like if I hit basically for me it's like hot keys to get you there. So, if I hit space S, this is the search and then I've got this little like preview panel here. I need to stop moving my mouse. It tends to go away whenever I do. But you can see that I can search for like LSP symbols and now I have all of my LSP stuff up here. So, if I wanted to jump to address, I can now jump to address.
That's maybe not the best example because that's in the same file. But if we were to um actually workspace symbols doesn't seem to be working.
Um but yeah, you can search for different things. Uh so like searching through diagnostics which um we have a to-do and it tells me that the code's incomplete and it'll crash. Uh and it also says I think this is a bit array which I search diagnostics. Hop back over here.
This one's in sheish 29. So like sheesh 29.
Um oh it's telling me that this type that it is inferring is probably a bit array which to be honest I think we'll throw this away. So I don't necessarily care.
But yeah you can do some really great stuff like that. You can add jumps swap to your jumps. So you can um like in our case these are the the areas that I've jumped to recently. So it keeps a log of that. So if I want to quickly go back to something search for man pages marks location lists all sorts of stuff.
Actually I don't have man installed. So, uh, yeah, for example, I have a key map to add a new pipe operator to my code.
Yeah, that's nice, too. I could see that being useful.
Uh, okay, where were we? Yeah, why is this not working? So, in theory, we should be able to match on this. So, we'd have ELO and the domain, and then we should just be able to return the domain. But that does not seem to be working. So, let me see if I can find an example.
Um, matches matches functions flow control case expressions.
It was right there.
variable patterns.
Yeah, that looks like what we're doing.
So, we have our domain elo domain elo. We trim our domain.
Uh let's just hop over here really quick and then just gleam test and see what we get.
So it's empty. So we're asserting out is equal to command ELO with my website.
But instead it is empty. Why? Why is it empty?
Let's drop the string trim just in case and see what this runs.
Okay, same thing.
So, oh my goodness.
That's because we're only we're we're matching on the upper. So instead, we just change this to line. We remove this. Oh man, two passed, one failure. The one that failed this time is the hello, which makes sense. So I like this pattern.
Let's change to use this pattern all across the board. Um because this feels way nicer and probably is more idiomatic which is always nice to see.
Uh please.
There we go. And then same thing here.
So this will be domain and this becomes domain.
Beautiful parsing. Love to see it.
Uh yeah, the slice was still in there.
That was that was for sure the issue.
Thank you for for mentioning it too. Uh pipe off. I guess that's useful. My keys positioned awkwardly on the keyboard just right above the enter. Also have to press shift in Turkish keyboard. Could change that on my key map as well. Maybe ELO without a space. I might be wrong.
Yeah, I think the issue was that we were we were matching on the trimmed version where we just took the first four. Um so should be good there. Let's continue our Let's not continue yet. We just made changes. We had failing tests. They should all pass. They don't pass.
Let's save and try again.
They still don't pass.
There we go. That should take care of it.
Oh, except we are uh can we so instead of uppercasing the entire line?
Yeah, I think the the issue here is I have a test that is intentionally lowercase hello. So, um, because I don't necessarily know if the spec requires it to be uppercase.
Yeah, forget it. We'll we'll make it we'll require it to be uppercase um because it does look like that is expected. It would be nice if we didn't have to, but we can always fix that later.
There we go. Test should work now.
Yep. Nice. Okay. Makes sense. Yeah, that was just a small thing. Okay. Um mail.
Mail.
Same thing here. Uh but we actually need to do a little bit more. So male is going to be male.
No. Oh yeah, this is actually we can because we chose this new pattern with the um concatenation operator. This actually gets even easier here. I thought this one was going to be tricky, but we can just do mail from uh and then domain and then return a mail from.
What does the mail from look like? Mail.
Mail. Mail.
uh initiate a mail transaction in which the mail data is delivered to an SMTP server that may in turn deliver it to one or more mailboxes or pass it on to another system possibly using SMTP.
Okay.
Mail from reverse path.
Excuse me.
I wish I had another example.
Yeah. Okay. This is what I was looking for.
So, we have our path. We have a reverse path.
I think we can just replace these in our reverse path while we're parsing because I don't think we need those for anything. Like there's no reason we would need them. So let's let's take that approach. So we're here. We have our mail from um we want to replace both. I don't know if this is a good idea or not. We'll try it and one one of the many Gleam experts here can uh help me figure out if this is a good idea. But we can essentially replace the uh domain. It wouldn't be domain actually. This is an incorrect name. Um reipient recipient. We can replace this with this.
And then we can just pipe that into another replace.
There might be like a multistring replace type thing, but I don't know.
This feels kind of cool, right? Feels gleamy at least.
No idea. All right. You failed me.
You've been so useful this entire time, and you finally failed me on something that doesn't matter.
H I just I really do appreciate your help.
Uh okay. So we have our mail from let's do another we'll do uh recipient. So this is recipient to same thing here. And then we'll add tests for both of these. Um recipient actually again this is actually the sender.
Sender.
Yeah. Okay. This one's a recipient though. So we have recipient two. And what we want to do is return a recipient two. And actually just the same thing.
String replace uh recipient replacing uh yep with yep.
And I already think that instead of doing this, we're just going to change this and create a function because we're using a functional language. So let's do that. uh clean recipient I n and this will take in a string this recipient I'll take in a string and all we want to do is return so we'll take in recipient pipe into string replace this with this pipe into string replace this with this Nice. So now we have this fun clean recipient. So now we can just say um recipient clean recipient.
Oops, wrong set of combinations there.
Ti. There we go. And this will be uh same thing. Sender pipe into my goodness.
Clean recipient. Yeah, something like that. That feels much cleaner. Uh, wait, is it the recipient if it's from them?
So, no, the Yeah, the if it's from they would be the sender. You can always refactor later when the answer inevitably comes to you in a dream.
Happens all the time with me. Way better. Yeah, I do feel like this is way better. Showers are my um those those are my answers. I don't unfortunately my dreams are strange usually and they don't help me with these type of things but showers help me with uh refactoring all the time. All right, we're almost done with our parsing here. So we have data data is just going to return a data and then uh quit is just going to return a quit.
And the only thing we're not capturing right now is data line. I'm not entirely sure what data line's going to look like. So, uh, maybe we come back to that at some point in the future.
This feels good, though. This is nice and clean. Um, feels weird that we spent so long writing 30 lines of Gleam, but it is what it is. Let's hop back over to our commands test, and we'll come up here. We'll yank seven lines. Hop back down here.
Undo that. Press the P to paste. And we'll add a new test for what we want.
Uh, parse What did we add next? Mail test.
So, this one will be uh mail and then we have an address. So, we'll do um like hello at bradciper.com.
Fun fact, mail from is also a function that you can pipe stuff into. Wait, hang on. Are you telling me I don't have to build an SMTP server? Gleam ships with one.
Oh, no. You're saying the the Okay, for a second I thought you were saying that there is a like SMTP server with mail from, but you're saying that I can pipe into the constructor function for this record, if that is the correct terminology. That's interesting.
I let's let's write this test then we'll refactor to that even if just to see what it looks like.
So this will be a mail from and then this becomes hello at bradcipert.com.
Write this hop over to this tab. Run our tests for test pass. Nope.
Oh. Uh, oops. Uh, it's because it's mail from like that.
And we're not trimming.
So, let's hop back over to command. Come down here.
After we replace, we will string.trim.
Hop back over to this and we should pass this time. Good.
Excellent. Excellent.
I think you can pipe thing into clean into mail from. Yeah, I I Let's try it.
Uh, seems neat. So, we do sender uh into pipe. Oh, wait. Sorry.
My goodness.
So, we go up here. Let's just throw this away. And sender clean recipient mail from.
Do I need Yeah, I probably need to call it.
Give it a shot. Holy cow. Do I like that better?
to be honest. I think I do.
Same thing here then. Sender clean.
Recipient 2. Yeah, I like this a lot better. It um type mismatch.
Oh, I see.
And this isn't sender. This is recipient. Nice. Okay. Uh you can format it to have a pipe in every line. Oh, okay.
Yeah. So like where it would be uh something like this.
Oops.
This.
Let's do that. I think that seems nice.
Cool. Yeah, I like this a lot. I mean, the big fan of pipelines just in general. Um, wrote closure for a couple years and being able to say this is my input and I'm transforming it through these series of steps and just kind of reading it linearly linearly is super nice.
Okay, cool. Let's write a couple more tests.
So, we'll go up here, yank seven lines, hop back down, paste down here. So, we did mail. We also need to do parse uh recipient RCPT test.
And this one will be RCPT2.
And it'll basically be the same thing here except it'll be an RCPT2 add another test. Uh we can just paste again, can't we?
Whoops.
Come back here and do parse data was the next one. Data test data.
And then this should just be data.
Command data.
One more. Whoops.
Come back down here. New line. Paste.
Parse. Quit. test.
Nice. Bunch of tests should pass now.
Um, is SMTP like now you get your own email server and address? Uh, pip pipe linearly. Um, is SMTP server like getting your own email and address?
Uh, no. Yes, no, kind of. Um, so SMTP is the it is the transfer protocol to send emails. So there's technically like when you send something from like your Gmail, for example, um, there's technically nothing stopping you at like the protocol level from sending it as a different email address. So you could send it as like my email address, for example. Um, so there's there's nothing really at that protocol level that prevents you from doing that. I mean, Gmail probably prevents you from doing that. Also, IMAP and POP might prevent you from doing that if we support IMAP and POP, which we might at some point, but for now, we're not going to worry about it. Um, I don't know. And I say that with no real exposure to IMAP and POP except knowing that they open up a brand new can of worms for this. So maybe they don't solve that problem as well. But the point there being that when you send an email, you can send an email as whoever ju similarly to putting a letter in a mailbox or like taking a letter to the post office like there's they don't check your ID before you send a letter. Like there's no one to validate that you are who you say you are. Um, so yeah, so essentially a server, SMTP server would be if you want to send an email, you can think of it like an outgoing post office where you can write whatever you want from your letter in your letter to whoever you want and you can pretend you are whoever you want and then you can send it via SMTP. And SMTP is that post office that you drop that letter off on. And then SMTP helps make sure that that goes to another post office that gets it closer to the person that it's supposed to go to. And then at some way some far some point down the road someone ends up getting that letter because some post office says I can't send this to a closer post office. I need to deliver this to the person.
Uh pipe operators and the use keyword are the features of the language that most beginners like to use the most when they are learning. Um yeah use is use is super interesting. Uh I think it might actually be worth talking about that. Uh even when not necessary just because it's fun. I used the Gleam Reax package, but it killed me that the parameters were not ordered in any way to facilitate piping that I could see how that would be very frustrating. Can SMTP servers also receive emails? Um, so SMTP servers can, but they use POP and IMAP as protocols for receiving those emails.
Um, I I'm pretty sure if we can support it out of the box with SMTP alone, I I'm not sure how that would be done. I guess in theory like if you were if we had like an SMTP server like you and I worked at the same company or something and we had an internal SMTP server that didn't go out to the public internet in theory I could send an SMTP message to that to send you an email and you could read it from the same server like that that seems like something that would be pretty easy to facilitate. But yeah, for more information on retrieving messages um IMAP and POP are widely used. uh the post office protocol and the internet message access protocol.
And then also I think Microsoft is the one behind active sync. So that's another option.
Yeah. Uh okay, my throat is already starting to get dry. Um can SMTP server also receive emails? Just answered that.
Okay, cool, cool, cool.
Uh where are we now?
So, let's hop back into our source.
So, we're creating we don't um yeah, we'll keep that.
Let assert. Okay, we're creating our listener here.
Cool.
Yeah, it is cool. Technology is cool.
Also, the really cool thing about SMTP is that it's built on top of TCP. Um, so if you know TCP, which to be honest, even if you don't know TCP, there are so many TCP libraries and languages um that you might like to write. uh which I have I have no idea what languages you like to write, but um there are TCP libraries in in basically every excuse me, every language and some of them even ship with standard libraries that have TCP support. So you could always build a uh SMTP layer on top of TCP, which is what I'm doing exactly right now. Uh Glisten is a TCP layer, so we're building SMTP on top of glisten.
So you don't have to do that part by yourself. And um yeah, kind of nice.
Subscribed. I love to hear it. I'm glad you're here.
Okay, this is the part I'm confused on.
So on and is a function that takes in a comm connection to a user message and returns a state option selector user message state. How does this work?
I have assumptions, but but assumptions make a fool out of me. And um maybe I can find gay, lesbian, and straight education network. That sounds fun, but that is not what I'm looking for. Uh Gleam Glisten.
Uh you're right. It is state lowercase.
Uh it's not like a type. But the part I'm confused on is like how long does this persist? Where does this persist? What's the what is the tell me more about its persistence? How can I use that? Can I use that to set up a session that is available for the entirety of that uh TCP connection? So essentially what I'm trying to figure out is if I return state here, yeah, it's got to be right. Like this is an assumption but if I return state here instead of nil this state is going to be scoped to the same connection.
So this will be state for that connection and then that state will be avail but I don't think it will be. So instead what I can do here in this tupil is instead of returning nil I can set up a session new uh lowercase new my apologies and then we don't need to return anything else here I am going to create another function uh loop And essentially all this is going to do is this part.
So let's come up here, go visual, grab all of this, delete it, come down here, paste it in loop, and then loop needs what? the state which is now going to be an SMTP session. Yeah, feeling it. And then it needs the message and the connection just like so. And we can come up here and clean this up further and just pass a reference to loop.
Cute. Love that. Uh I think the type of state is a session.
It is. Okay. Hi. I'm also learning Zigg.
Um, we're not doing Zigg today, but I'm happy to hear that. I think Zig's a really pleasant language that, uh, I think a lot of people will enjoy. You can hover it. I can hover it. That's smart.
Um, state is usually an analog of the HTTP context. That makes sense. Okay. Uh, I guess the flip side here though, yeah. No. Okay. No, that does make sense. There's no flip side. Zigg and Gleam are your favorite. That's great.
you and I would probably get along very very well in person. Well, I don't know.
It's my personality is more than my like two languages that I enjoy, but um yeah, I don't know. I have have a hunch that if you like those languages, we probably would get along in other things.
Uh okay, so we have any reasons why Gleam? Uh yeah, I talked about this a little at the beginning of the video, so I won't go too ind depth on it, but uh Gleam can run on the the beam the Boggden Erling something machine. um advanced machine maybe. I don't I don't remember specifically what it stands for, but uh the beam is just like a really fault tolerant system and I think that an SMTP server doesn't need core performance from like Zigg or Rust or C and instead like just having a really reliable system seems nice. Um so, uh you also get some really fun stuff with the beam where like if you were to Yeah, so many people don't know what SMTP is.
Uh yeah, one second. Um, but if you were to uh with the beam, if you want to deploy a new version of something while something is running, you can go ahead and like hot deploy the new version while the old version is running and like drain um drain users from the old version to the new version just in real time as they are like interacting with the system.
something that is like crazy that it felt like felt like took forever for us to get with Kubernetes and it's just something that the beam just just supports.
Uh SMTP is the standard mail transfer protocol. There's a Wikipedia page here uh if you want to learn more about it.
And then if you really want to learn more about it, there's a link to an RFC here that is 98 pages of how SMTP works.
Um the short and sweet is that sorry for the flashbang. Um it is this right here.
So there's uh a client and a server. We are building the server and uh the client is my computer using netcat to talk to the server on my computer. Um so really straightforward but it's built on top of TCP. So we're building we're using a a library called glisten which is a gleam TCP library and we are building um the SMTP layer on top of it.
Uh okay I'm behind and I have to look at my second chat window.
Just this morning I was looking at basic CRUD web server and Rust and they use the same state convention to store their context. Yeah, I noticed that like um that's seems to be pretty common uh with Rust and then also Go has quite a few that are sort of like that and the uh HTTP Zigg by Carl Seuin um and Zigg I think also does that if I'm remembering correctly. Um yeah, quite a bit. You can build systems that handle thousands of processes running concurrently. Yeah, that's true. and processes in in um the beam are really lightweight. They're not they're not like operating system processes. They are really really lightweight. Uh the beam uses message passing to communicate. So there's benefits on that as well. Um it has a very uh what's the theuler is very something and I cannot remember the specific word they use for the scheduleuler but it's very fair. it it very much focuses on giving a fair amount of processing to every individual process.
Um, Gleam can also compile to JavaScript. Yep. So, it runs the browser. Kind of shocked at how clumsy Rust web servers HTTP support and OM are. Yeah, Diesel's interesting on the Rust side. Um, it's not bad, but it's it's different.
Um, uh, sorry, random message from a friend that confused me. Um, what else is this for practice or do you hope to use this in prod? Ah, it's for now it's for practice. I don't know.
Maybe I mean I would like for it to be complete and it'll be open source and if people want to use it in prod like I don't see any reason they couldn't. Um whether I will use it in prod or not I have no idea. Uh it I can't imagine like I'm not going to build a business around SMTP servers or anything. So if anything it would run on my home lab and uh we'll see where that goes.
Uh, didn't say that. You'll don't say that. You'll summon the web people. Oh, runs in the browser. Just joined the stream. I'm here for Lucy. You and me are both here for Lucy. Are processes like cooperative schedule? Yes, that might be the term. Um, are processes like green threads? They're closer to green threads than like anything else that I would I would compare them to. Yeah. Um I think the key thing there is the beam uses message passing to communicate across processes and across like functions. Um so there's there's this it's not necessarily like an event loop in JavaScript where like things get scheduuler scheduled in that sense but because it uses message passing there's like a lot of really a lot of what's the right way to phrase this? My understanding is that there is a lot of um points in the program where the thread the thread the process can be free from action allowing the CPU to pro work on another process. So um what what that means in practice is that the beam is not great for CPU intensive workloads, but if you have a bunch of tasks with a little bit of CPU usage each, it's actually really great for that. And do you know what that is? Web servers. Um and OTP if you really want to like it was built for telecom so like running phone systems. Um so phone systems great for that. Uh great for web servers. Great for um things of that nature. Uh Rabbit MQ is built on Erlang.
So it's a queuing system that's built on Erlang. Um and that runs on the beam. So there's there's proof that it works in all sorts of different things.
uh build single page applications without having to deal with the JavaScript ecosystem. Yeah, I'd show off a Luster project, but it's a bit of a mess at the moment. So, um maybe not.
Mandatory Luster call out. Yep, that's my Luster call out, too. Beam garbage collection is concurrent, by the way.
Also good to know.
Um also important to know garbage collection is per process instead of being a sweeping garbage collector that looks at everything that the uh the runtime is managing. So and in in Gleam processes are tiny processes are lightweight. So the garbage collector is running per process and so if you have a process that isn't creating a bunch of garbage, you're not going to ever see the GC run in it or or rarely see the GC run in it. Um, but if you're doing something that is creating a bunch of garbage, that process is going to see the garbage collector more than anything else.
I I don't know if I can send a link without being banned. You are more than welcome to try to send a link. You will not be banned, but it might not go through. I unfortunately because it's a live um I have no control over that.
Uh, another option I'll shamelessly self-plug. If you're not in the discord, you could always join the code with cyper discord which is in the description and share your blog there.
Okay, so we have our loop. This feels good. We have our session.
Uh I don't know, man. You say it worked, but I do not see a blog anywhere. I just see the word Shazam.
Uh okay, so we have our loop. We have our bytes. What else?
So, our loop is handling the message.
This doesn't need to be public, does it?
This can just be a fun. So, we're handling maybe a message. I don't really know if we need that. We could clean that up.
Yeah, I think we're going to need to change this some. So, I'm actually just going to go ahead and toss all of this that we've written so far.
And same thing here. So, what we're going to do in our loop is we will case off the message and we will again check if packets uh if it is a packet then we'll get the bits.
We'll do like a let assert. Okay, text.
This is similar to what we were doing previously. Actually, probably should just copy and paste it because this is very similar. So, we'll do the bit array two string with the bits.
And then what I really want to do here is call process lines, which does not exist yet, but we're going to send in the state buffer with the text. So, we'll concatenate those two together.
We'll send in the state, and then we'll send in the glisten connection.
And then down here we will call glisten.
With our state. So we can remove this.
We can remove this. And then we need to actually add our arrow here. And we need to create process lines. Okay. I like this better.
Process lines. We have a buffer which is going to be a string. We have our state which is SMTP session. And we have our connection.
uh split. So we want to split because we could have a single packet with multiple carriage return line feeds. So we'll do string split once on the buffer or rather we could have a buffer. Even if it's not a single packet, our buffer could have multiple carriage return line feeds. So we'll split on those.
Um what is this thing? That's the pipe operator. Kai, stop being so fast. Uh, it takes what is used before and uses as the first parameter of the next function. Yep.
Hey, you're killing it, Kakai. You You might uh you're my unofficial moderator at this point. Answering the questions before I can get to them.
All right, so we're splitting once in the buffer. We can check if it's an error. This does return an error, right?
No definitions found.
Yeah, returns a result. Okay, perfect.
So error and if we do have an error then we just want to um just keep buffering buffer that state and we should be able to just spread the existing state and our buffer into buffer.
Okay, then we have our inexhaustive patterns.
So, oh, okay. We need the Okay, we have a tupil that we need to dstructure.
Uh, interesting.
Which language is this? This is gleam.
Um, it is uh check the code action for a Yeah, I will try it. Let's code action. Add missing patterns. Boom.
Beautiful.
Is there also a code action? No. How about here? Code action. No. Okay. It doesn't know what this is, I guess, maybe. Or it just can't help me with that. But it should be a tupil and it should be a line and then everything else.
Uh, okay. We'll wrap this in a block because we're going to do a little bit.
So, we'll get the next state.
We'll get the next state. So, we'll have to we'll have to create a function for this. It's going to be Yeah. Yeah, this will be a bit uh but actually this will be a bit of work, but actually we'll be in a really good spot once we get it finished, I think, because I think this is really the last missing piece. So, we'll just go ahead and create an abstraction here called handle line. It's going to take in a line. It's going to take in a session, SMTP session, and then it will do the exact same thing where we spread the state, and then we pass in the buffer, but the buffer is going to be the rest this time.
Um, this needs a connection too.
Yeah, we'll have to pass in the connection.
And then recurse in more complete. Oh my god, English recurse in case more complete lines are in rest.
So in our case then we'll just call process lines rest next state and then our connection.
We'll pass that back through. Okay, cool. Yeah, feeling good about this um unknown variable because handle lines does not exist. Totally fine. Let's write it. Handle line on a roll on a roll. Let's go. So we have a string.
We have our state which is an SMTP session. And we have our connection.
It's going to return an SMTP session.
So, we have a case state.
All right, I'm going to I'm going to try it. Come up here. Code action. Add missing patterns. Oh, beautiful.
Although Although I think the only one we actually care about is data at least at this point because everything else will just need to be parsed and modified.
Yeah. Okay. Well, it was cool, but data is really the only exception here.
So, we'll just do this. And then if we get to data, we need to handle a data line.
So another function. Sure, it'll take in the line. Do we need the state? Do Yeah, why not? We can pass it in and remove it if we don't need it. And I do think we will need the connection one way or the other.
Uh you can handle the data case and to do at the end. Um, I think we can. Yeah, we should be good here. So, yeah. So, session data and then underscore and then now we want to check case command parse. We're going to call our parse function that we wrote earlier. We're going to pass in the line.
And now, boom. Yes. Okay, this one we need all of them. So, that works out great. Um, question about this. What's with the colon?
Why is there a colon here? Should there be a colon here?
Very confused on that.
Oops.
Okay. I don't know what the deal with this like where the colon came came from. Um I I don't I'm not sure what I means, but that's okay. Uh not a huge deal.
So confused by that. Um I'm going to create one more function. Oh, it translated it. Gotcha.
Uh, and I'm just going to create a function here called um cons send string because we're going to use this quite a bit and it should just help us. So, all we need is the connection.
Does that make sense?
Pipe in.
Yeah, that's fine. Connection and then string.
And all this is going to do is do a glisten send connection and then bytes tree.
Nope, not append. Bytes tree from string from string and then string something like that.
Yeah, good enough. Um, I guess it's like a keyword arg if it's command elo whose domain is something.
Yeah, I mean there's got to be docs, right? There's got to be docs for this record pattern matching.
No, I don't see them. Um, it seems fine to remove it, too. I'm Yeah. Okay. I don't know. We'll figure it out. Let's just continue and see what happens.
Hi, I've never heard of this language before. Um, yeah, it seems like a lot of people haven't heard of it before. It's really, really pleasant. Uh, it's a functional language that is kind of similar to Rust syntactically, but don't let that scare you off. There's no borrow checker. There's no um reference counting or unsafe or anything like that. It's a higher level language, but there's definitely some inspiration from Rust. Uh, but it's a higher level functional language that can compile to JavaScript or uh can run on the b the beam, the Erling virtual machine. Um, the Erling virtual machine alone has a lot of really good selling points for it. And yeah, Kakai is right. The language is small like Lua. It's I think it's around 18 keywords uh that make up the language and then a fairly robust standard library and um productive like Golang. Yeah, I would I would say so.
Yeah, it's a it's a really nice language. Um, a lot of things I like like uh result types, um, options, uh, pattern matching, all sorts of fun stuff that I I enjoy. But yeah, uh, if I'm biased in saying this, but I would love it if you hang out for a little bit and see more of the language and see if it's something that you would enjoy.
All right, let's do a let assert because we need to make sure. We don't necessarily care about the value, I guess, but I want to make sure that glisten send we just did this. So we'll do con send string and we want to send our connection and we want to send a string which is going to be so if we get an e hello we want to send 250 okay carriage return line feed and then we can also session SMTP session spread our state once more and then set our state to session.re although it uses options in a different way than other languages more for inputs than outputs.
Yeah. Yeah, I would say that's that's probably true.
Hadn't thought about that.
Um, do we need to support hellos?
probably probably need to support them all I guess but uh okay mail from oops so this will be the same right we just always need to send acknowledgement back over TCP so in our case we'll have con send string we want to send our con and then in the case of mail from same thing I think it's 250 okay carriage return line feed. We can double check that in the either the RFC or oh man would have been nice to have found this earlier. Mail from 250. Okay. Yeah, perfect. Okay. So, we have our cons send string. That looks good.
And then we need to do another session.
SMTP session. One thing that might not be obvious for someone who mentioned that they're newer to Gleam, the um last item in the return path in in the the control flow, I guess, is returned. So in our case, like when we hit this branch right here, uh this stuff um this is returned from the function. So there's no return keyword. I don't I don't think there's a return keyword or if there is, it's not very common to use it. It's more common to just let it flow.
In fact, I'm I'm actually pretty sure there's not a return keyword.
Uh, okay. What else is going on? So, we have our SMTP session spread the state.
I think we'll just keep this pattern going over and over again. Uh and then the session state will be session mail from and then from will be option sum address.
So we need to get our address. Oh yeah uh it's ESS.
I still don't know what the deal with this colon is but it seems fine. I'm it's not complaining about that at least. So it's very interesting. Um there's our mail from same situation here. In fact, so much of the same situation that I think I just copy this, come back down here, paste this, format it, and then change this from mail from to uh recipient to.
And then instead of from our two becomes a list with our address and then the existing two. So, we'll spread the existing two in there.
Seems good to me.
Uh, okay. Chat's popping off. No return.
Yeah, I think I know what you mean because in Russ the question works for both result and option, but here we want it narrowed to just one. That's yeah, lots of modern lings. Forbid return to dodge early return problems. Yeah. Um I I write a bunch of TypeScript for work and also just for other things too.
Well, I used to write a bunch of TypeScript for work. Now it's mostly Go, but some TypeScript as well. And there's definitely a case for early returns that I do like. Same with Go as well. Um but uh yeah, you can also trigger a code action a function not yet defined just to generate it. Good to know. What is SMTP?
SMTP is a standard mail transfer protocol. It's a layer built on top of TCP. It's used to send emails. Um, so if you send an email, you're probably going through an SMTP server. Or if you're not going through one directly, you're probably calling an API that goes through an SMTP server like resend or something like that. Um, but yeah, in theory, once we get this finished, we should be able to send emails from my machine using all code that I've written.
Always wanted to host my own SMTP server for privacy, but didn't want to deal with the hassle of security and reputation. also was never too comfy with the idea of DMZing part of my internal network. Yeah, I don't I don't necessarily know if like this is something I will this is a learning opportunity more than anything else and an opportunity to hang out with some people in the chat which is super cool.
This actually has been a very very chatty stream. Um but yeah, I'm I'm right there with you. Also worth mentioning if you do want to host an SMTP server yourself, there's like there's this concept of reputation and your server has to be reputable or else stuff will go to spam. Um, and essentially like if you send enough emails without being flagged as spam, like you your reputation goes up, then after a while your stuff will start landing in the inboxes instead of spam.
But if someone marks your stuff as spam, it ends up hurting the reputation of the SMTP server that sent it. And like it's it's it's very interesting.
Uh, hey Cyper, just tuning in. How's it going? Uh, pretty good actually. Um, it's a holiday here in the US, so I'm just kind of enjoying the day and riding some Gleam and just hanging out. Um, trying to learn some stuff and, uh, work with it.
How are you?
Uh, you can emulate an early return with bullgard, letting the use keyword do the rest of the content of what if we didn't. You can emulate an early return with a bull guard, letting the use keyword Oh, yes. Yeah, I've seen examples of that. I also I'm really glad you mentioned that. So yeah, you do have that option of essentially an early return using the use keyword, which I guarantee has to be in here.
Maybe advanced features use. Yep.
Yeah. So this being able Wait, sorry, not this.
This.
So essentially, this is neat and arguably one of the cooler things that Gleam does. So just by the nature of it being a functional language and by the nature of like message passing and all sorts of other fun stuff, you're going to have very callbacky focused code.
This is hard to follow. Like this is this is kind of hard to read. Um I I feel it's hard to read. Uh but the exact same thing can be done with the use keyword and it looks like this. So essentially use keyword helps you flatten visually flatten the callbacks so it's more apparent as to um the flow of things and just easier to read and follow. So this is the same thing as this in gleam.
I'll leave that up on the page for a second for people who want to read it and it gives me a time to drink some coffee.
This month, Rescript people are having a flame war. Uh, if they should include early returns. Most people don't want it. I I mean, they break control flow.
So, I see the value of not having them, but I also see the value of having them.
And I think for me, I would rather have the option.
Yeah, I would rather have the option and then set standards with the people I'm working with to either use it or not use it.
Kind of weird. Early returns a pattern or may not something that should be gatekeeped by the compiler. Yeah, actually that's that's a really good way to say exactly what how I feel about it.
I think Elixir has something similar.
Elixir has something similar to use maybe or early returns.
If you don't care about sending emails, only receiving self-hosting is great.
Yeah, I think the flip side to that is we're only building SMTP at this point.
We're not building pop or IMAP. Um, if I really enjoy this project, we might add that in at some point, but for now, just SMTP.
Oh, okay. Similar to use elixir. Gotcha.
Okay, let's make some more progress.
Let assert. Okay, same thing as before.
I think my neighbor is doing yard work, which means I too might have to do yard work at some point.
Uh, glisten.
Sorry, we don't actually want to glisten. We want to send con string. Con send string. We want our connection. And then what we want to send for this one is different. This is the 354.
So this is 354 something. Let's find out.
Yeah, this I just copy this.
How are you going to handle encryption?
I'm not I'm not going to handle encryption. We're not going to worry about it right now.
I will cross that bridge when we come to it. But we can get something working and learn a lot before we even have to worry about encryption.
I like your response to that. Um, it's good to see that modern languages have easier to read, write, try catch statements almost built in. Yeah, I I would agree. I mean, like languages have changed a lot and as much as I love some of the older languages that I have written, especially because they feel nostalgic, I do think that um modern languages should try to probably try to innovate in some interesting ways.
All right. So, constant string we're sending in connection. Um, carriage return line feed. Carriage return line feed and then an actual carriage return line feed. Feels good. And then we need to return session SMTP session spread the state and then set the state to session data since that was the most recent one that we processed.
Don't think we need to handle this one.
I don't think we need to handle unknowns. So, we can come down here. And now we need a catch all. So, we'll just do let assert.
Uh, no, no need for that. Well, no, we do need that actually. Let assert because we need to prove that this did get written to the socket.
Con send string we will send the con and then the other one is a 500 which is unknown command carriage return line feed. Okay.
Uh and then we can just return our existing state. We don't have quit yet.
So let's wire up quit. And we should be pretty close. So let assert um and just to clarify pretty close with our parsing. We actually have like no sending done yet.
Uh, oh my god, I keep doing this. We wrote a function for this. Con send string con and then uh 221 by and then return state H. That's troubling. I just got a notification that my computer appears to be connected to a network but does not appear to have connection to the internet.
Also, my music has been stopped for a very long time.
What do we want to listen to? What do we want to listen to? Let's do this.
Yeah, I uh I definitely am online. I mean, the stream's going through. Um, it's worth mentioning that I have I have two computers. I've got a computer that is hardwired to the internet. That is my recording device. And then I've got my laptop that was what we're coding on so far. So, um, the the coding device is the one that said it no longer has a connection to the internet, but the desktop that is doing the video processing and streaming seems to be fine.
And if we're lucky, we might not need a connection to the internet anyways. So all right let's try this. This is unused. So let's just ignore it for now.
Okay. Code action generate function.
Boom. Go to definition. Beautiful.
All right. What is handle data line going to look like? Should actually be pretty straightforward. I think we like let's just take a look at what these look like and the dot and the dot. That's important.
Okay. case line.
If it's a dot, it is the end of the message.
We're just going to do it again. Con send string. Send our con. And then the string that we want to send is 250.
Okay. Cued, right?
We'll come back to that.
Okay. And then here we can just return session SMTP session state state session ready data lines. We can just clear out that state. Actually, it's not clearing out the state. We're still using the old state, but we're clearing out some of the fields. So, we're setting it back to ready and then saying that we have no data lines.
Um, we're not actually queuing this. Uh, and then if it's not a dot, then how do we want to handle this? Then we just want to accumulate what we have. SMTP session state data lines Add our new line. Spread the existing lines.
Cool.
Probably should do the let assert. Okay.
To make sure this actually gets written.
Uh, wow. Okay. Uh, streams. All right.
LSP code actions are lifesaver. Does it even handle? They even handle generating JSON decoders and coders sometimes.
That's cool. Still doing old school Erlang. That's hardcore.
Feel like I'm not smart enough for Gleam. That's funny. I feel like I'm not smart enough for Erlang. Uh but you have written Gleam. Good. Uh they get better with every release. Oh, I wrote a bit of Erlang. It has a funny syntax. Sure does. Probably the only one at about 400,000 lines in production. Sheesh.
have a comma at the end of lines. Uh, sounds like a lot, but it's not. And a dot at the end of functions.
Hashing the earl out. Um, I think this is pretty good. Are we missing anything?
I'm going to go with no.
and say I don't think we're missing anything. I'm also going to go with no lawn work today. I will just It's raining like crazy every single day here and I think there's a window to mow today, but I'm just not going to do it.
No. Okay. Is not correct. Thank you.
Thank you. Thank you.
Uh yeah, let's run it.
Oh, rip.
Sheesh. 45.
It's running.
All right. Netcat me up, my boy.
So the server.
Okay. So actually I think oddly enough this example will crash.
No, it did not crash.
Uh, okay.
All right. What am I doing wrong?
Let's start with bit array two string bits. We process it. Um let's just io.print print uh received message text.
Do a little bit of debugging.
All right.
Big netcat elo com.bradciper.com receive message. Yeah, we're good there.
Uh continue actually to send another message.
Can't send another message because we haven't responded.
Why have we not responded?
Um catching up on the chat. Sounds like a lot, but it's not. Um syntax looks like a mix of Erling, Rust, and JS. Is okay.
Correct. No, it's not. Do you also program for a living? Are you asking me?
Because if so, yes. Yes, I do.
At least for the time being. I am also I feel like I'm midlife crisising super hard. Um, and I think that's been exacerbated by the changes in the software development landscape over the past year. And uh, I don't know, we'll see how much longer I stick with it. I would love to. I love building stuff and crafting, but to be completely honest, I've just been yeah, super super exhausted. Um, some some of the stuff at work has been actually really fun and it's been like revitalizing to work on some of that stuff. But I think just even like if we put work aside, just my general like drive has been lower than ever, which makes me sad. I'm not sure if part of it age and part of it's the landscape. Like it could be a bunch of different things.
Um, but building things like this really helps. I feel like I'm learning. I feel like I'm trying something new. I I get to talk to like-minded people who actually like this language and like what I'm building and have questions and like to learn and that gets me super jazzed. So, um yeah, I don't know. It's just kind of where I'm at as a person.
Not that anyone asked all of that, but I'm an open book, so I'll spill that stuff out. You're a digital artist and a software developer, so not a good time right now. Still have a lot of fun with both. So, yeah, I get that. I um man really sorry that you're going through that. That seems like two of the worst professions to try to have right now. I don't know. I feel like like software engineering is like I worry for juniors more than anyone else. And I it's I don't think that junior dev rolls are going to go away um long term, but I think there are a lot of people who look at the output they get from a junior and see it as an investment and then they look at coding agents and say I could get more quicker without worrying about the future and them potentially leaving or anything like that. But I I think that's a very narrow-minded perspective and I don't think it's a very good perspective to have. Not just for the economy and for people and things of that nature. But even like even if you look at it selfishly, I think it is a bad perspective to have. And I think a better perspective that would even be more selfish would be that I should hire more juniors and grow these people um for a multitude of reasons.
And it's less selfish at the same time.
Uh, with less juniors, we'll have less seniors. Yeah. I mean, I'm going to retire at some point. So, like, and and I guarantee every other person who's been doing this for I've been doing it for what, it's 2026, so 16 years now.
Um, I guarantee everyone else is going to be the exact same way at some point.
So, uh, run out of software, folks.
Someone has to maintain the cobalt.
Okay, let's let's actually think through this. Why am I failing? What is going on? So, we receive a message. We call process lines. We take that text, we add it to the buffer. What's going on in process lines? We split once, no complete line. Is it because I'm not actually sending carriage return line feed?
Let's try this.
Netcat localhost elo www.bradciper.com bradciper.com carriage return line feed it. We should respond, right? We received a message. We have a character return line feed. need to respond and say that we've acknowledged that they've sent this to us. Right? So handle process lines.
So we should be sending a 250. Okay.
Back. All right. Let's let's litter this with some io.print print line. Print non data line. Let's just start there. Do we get to that point?
Oops.
No, we don't even get to that point.
Uh, your mom used to have a typewriter where you would literally turn the wheel and slide the thing to do line feed and carriage return.
It's interesting that those have carried over and we we still have carriage return and line feed. Like so much is built on them, I guess, at this point that it would be like it would be so hard to just remove them. Kind of interesting.
Okay, so we're not getting to non-data lines. So I don't think we're getting to handle line then at all. So go to references.
Let's start here. So IO.print buff and then we'll print the buffer.
Glamr run elo bradciper.com.
Let's just send that as is.
Okay, so we have our buff.
More is coming through. That's good.
That's good.
Test, test, test.
Great. Now you feel old. You learned to type on one of those. I um have never touched a typewriter. I think that is a truth state a truthful statement.
All right. What's the move? Why? So, we're holding up the buffer.
This is accumulating.
Yeah, that's not I'm not sure why I'm even trying that.
Okay.
Process lines error.
It's one thing I don't know how to do is debug anything that runs on the beam.
like with an actual debugger, not just littering print lines over places. So, okay, run this netcat. Hit it again. ELO www.bradiper.com.
Miguchi process lines error. Okay, we are running into an error. Okay, why why are we running into an error?
How does this function behave? split once string split once to the standard library.
That's Mojo. I don't really care about that right now. Don't care about pixie.
Don't care about lazy vim. Close glisten. Split once.
Splits a string a single time on the given substring.
Returns an error if the substring is not present. So we do not have a carriage return line feed.
What if I send one process lines error again? Interesting.
Uh, personally been working in cyber for the past 5 years. I'm already hitting the midlife crisis at 31. The deeper I go, the less I'm expected to do stuff I like. Codehack. It's boring. Yeah, I am similar. Very similar. I'm 30. It's 20 26. I'm 33. Um, and feel the the same way. Um, yeah, very very similar. Uh, I feel I don't know, man. It's It's hard. I like typing. That sounds weird. I like getting into the weeds of the syntax like this. Um, and I I really like every part about software development, but I feel like the current trend in the industry is to get take those people who have those skills and like the architectural skills and and I' I've worked as an architect before as well, so I have some of those skills and just kind of pull them farther away and have them micromanage agents. And I just don't know like I don't know, it's different. Not saying it's bad, but it's not as enjoyable for me as, you know, what I've been doing in the past. And it's hard, too, because like if the agents do a good job, they're way more efficient. So, I get it. But at the same time, like I don't know. Maybe if money wasn't always the main driving factor for most things, then things would be different and people could do the things they loved.
Yeah, I'm They are. Yeah, I'm I'm with you.
Uh, you're thinking about diving into graphics programming in your free time now. Watch too much Jonathan Blow. Um, try it out. See if you like it. I have done very little graphics programming.
I've done some game stuff.
NC localhost 3000.
Dang, we crashed the supervisor this time.
Okay, non data line.
Cool. So, that actually uh worked, but we did crash.
Um, yeah, it exploded. But, but cool.
You notice the process is still running.
It didn't explode. The supervisor crashed, but the beam restarted it. And now we can crash it again by sending the same message again. But but really cool, right? Really cool. Like all things all things considered is um I mean we can see why it crashed on the to-do uh for what it's worth. So something somewhere I have a to-do that we need to finish flushing out. But the point being here that is really cool. the process despite us running into essentially the equivalent of a panic, the process is still running. It can still handle requests. So if we have an error case, like an edge case that just blows up, that supervisor will restart and can continue handling requests again.
Uh you're right, child process, not supervisor. Supervisor gave a report, so it was the child process.
And then you can see the supervisor is the GLlisten connection supervisor there. Also, what time is it? 1:30. Need to eat soon.
Uh, yeah.
Neat. All things considered. Okay. Todo evaluation exception error handle line 64.
She handle line shees 83. Okay, let's head to shees 83.
Uh, by the way, supervisors can also crash and be restarted by other supervisors. Yes, they can. I'm trying to write an SMTP server just for learning in Golang. I think it's a great idea. What should be my goal? Your goal should be Here's how I would break it up. And here's how I'm probably going to break mine up because uh I've been streaming longer than I expected and I need to eat soon. Um so parse parse your messages first. Focus on parsing the incoming messages and building like an actual model from them. Make that be goal one.
And then after that I would say your next goal would be actually sending those messages from the SMTP server. Um actually parsing would probably be goal one. the messages back and forth between the client and server would be goal two.
And then that's kind of where we're at today. And then goal three would be um actually sending the messages from the SMTP server. Goal four would be probably supporting POP or IMAP. Goal five would be supporting whichever one you chose not to support previously. Um yeah, and then at that point you have a pretty functioning full-fledged uh SMTP server.
Uh, okay. Hey, will you be doing more of these lives? I would like to. Um, they'll probably be on the weekends because I work Monday through Friday.
Um, yeah, we definitely have a to-do. We can search search g to-do. There's several.
Actually, there's just one. It's this one right here.
Interesting. Why? Oh, because we helloed. Yeah. Yeah. Hello's not supported for some reason. Let's let's support Hello, too. Um, yeah, I plan to do more of these lives.
I don't know when exactly. Um, I I am employed full-time for the time being.
Um, so I it is a mix of like I'm also uh employed, married, have dogs and friends and uh unfortunately other hobbies that have been super addicting lately. Like I've been playing a card game called Riftbound, which I have immersed myself into way too much. Um, but yeah, I uh where am I going with this? I would love to finish this out and I would love to finish it all out on live stream. Um, so I plan to do more. We'll see how it goes. Uh, it is basically timing, scheduling, and then how tired I am by the time I have free time to do it. Um, most likely it would be like Saturday probably starting at like 11:00 a.m.
Eastern Standard Time in the United States. um or Sunday probably starting at like 10 or 11 Eastern Standard Time in the United States. That being said, you can always catch the VODs. Um I know it's more fun to participate in the chat, but if if you can't make it and you do actually want to follow through with this and learn and all that fun stuff, um yeah, you can always catch the VOD.
Uh yeah, same thing. So spread the state state becomes session. Why did we not do this?
Try again. Maybe don't break the uh child process this time. 250. Okay.
Excellent. Why? So I can't use interactive netcat then or like how process lines error. So we run into an error.
Uh, same thing here.
Okay, so we process lines. Okay, we get a non-data line and then we hit process lines error.
Uh just a beginner. Yeah. Um sorry I missed your just a beginner uh comment.
Um I I think honestly building an SMTP server is probably a pretty friendly beginner.
I would say it's a challenging but be friendly beginners um task. Don't build the TCP layer yourself. Leverage the TCP layer. You said you want to use Go. Go has a built-in TCP Golang TCP standard library.
Uh yeah, in the net package you can do TCP networking. So I'm using a library called glisten which provides TCP networking in Gleam. Um you just have that built into the standard library for Go. So I would just build on top of that if you wanted to do this project in Go.
Uh thank you for streaming. Cheer could be hitting two new line types, Windows, Unix. Yeah, of course. Happy to stream.
Um, could be hitting two new line types. I guess I'm just kind of confused.
Oh, we stopped printing the buffer.
Let's That would be helpful to print the buffer.
We can just line feed.
Give it a run.
So there's our buffer process lines. Okay. Non data line. How?
Okay. Okay. And then we get into an error. So non-data line and then process lines error.
We send this back. So we're sending back an SMTP.
So we're sending the string on the connection. And then we're returning back a ready to handle line. Handle line is here.
Our issues is here, right? Recursing because we're recursing and then we get into an error because there's not.
Okay.
Looks like NC doesn't send control carriage return line feed properly.
Great, great, great, great. Thank Thanks, Netcat. Um, okay. What are my options then? Test an SMTP server via TCP locally.
Telnet.
Sure, let's try it. I'm here for it.
Tnet uh localhost 3000.
No TNET. Yay. Tnet.
I'm sorry. There's no MTNet.
Oh, here comes the rain.
Uh, certunet inet utils. Okay.
Yeah, that is much more of what I was expecting.
Oh, Riot made a new TCG. I'm not not giving my money to that company again.
Thank you for the discussion. Best of luck with the rest. Always inspiring to see people program for fun. Yeah. Um yeah, Riot did make a new a new TCG or I guess technically their first TCG since Legends of RA is just a CCG maybe. Um but yeah, going to take a break right now to work, but looking forward to watching the next stream. Take care and come to the Gleam Discord channels whenever you need help. I will do that.
I'm in the Discord. I just uh I'm the amount of Discord servers I'm in is so so many that I have so many muted and I kind of forget about a lot of them. But I will do that. Thank you.
All right, let's try this TN net again.
Local host 3000.
Are we running? We're not running. Maybe that's part of the problem.
Team run big TNET connected escape character is okay.
Interesting.
Elo 250. Okay, we're good. Process lines error. So, we are hitting that afterwards. So, yeah, that is definitely an issue that we'll have to sort out. I am curious if I can continue to send messages here. So, if I can say we got an ELO. Next will be mail from mail from uh hello at bradcipy.com 250. Okay, that seems good. RCPT recipient to no reply atbradciper.com rcpt to no reply atbradciper.com.
Wait, that would be twice which let's just not. Hello bradciper.com.
Uh data.
Okay, cool. The parsing part seems like it's going well.
This is uh just going to say hello cued. Excellent. Excellent.
Quit. Cool. Okay, that stuff is fairly working. Um at least the parsing and responding is is fairly working. we do have this process lines error that we keep hitting and that is probably because we're not defensively coding against uh this case here where we're automatically recursing and then we're getting into a spot where this is erroring because there is no um are there options besides split once?
No, just split once.
I think we just case this.
So if string dot because we're replacing the buffer.
Yeah, let's try this. String ends with rest.
If it ends with carriage return line feed, then actually I feel like we could just simplify this by saying case rest our carriage return line feed and then we can call process lines with rest next state and then con.
Oh, we can't do an ends with code line. It must be a string literal.
We can't tell what size the prefix is.
We don't know how to handle this pattern.
Interesting.
Gleam ends with case match.
Uh I was shocked when I knew having a mail from does not mean the mail is actually from the mail unless signed.
Yeah. Yep. That is that is completely true.
Oh, okay. Yeah, that's what they recommend doing.
True, false.
What's the void type?
No.
Code diagnostics.
Oh, because this will essentially always recurse until we get to a glisten continue.
Yeah, that's that's fine.
Yeah. Yeah, this should be fine.
All right, let's give that a roll. I think we might wrap up here, too. So if we gleam run this hello from sheish hit up that tnet.
Hello bradciper.com.
Okay, everything looks good. Errors.
Errors.
No errors. Good.
Uh what else? Um uh what is the message? Hello mail from [email protected] rcpt to no reply atbradciper.com good two two okays is interesting I wouldn't expect to see it twice. Why do we see it twice?
Oh, because we're recursing there, aren't we?
H.
Okay, we can clean this up.
What else? What else?
We'll keep that one. Keep the receive.
That seems good. We don't Yeah, we can keep the buffer.
Sure.
Um.
Oh, interesting. And we get an okay.
Instead of split once and recurse, would it be easier to just split and maybe treat the last one differently? Split and treat the last one differently.
Instead of split once and recurse, would it be easier to just split and maybe treat the last one differently? Can you elaborate more? I'm sorry, I'm kind of struggling to follow.
This is the issue, right?
This should be rest instead of the instead of the buffer or that would still have a similar issue. can't match on the last item of a list the same as you can't match. Yeah, that was my my thoughts as well. All right. Hello.
250.
So far so clean.
Mail to uh no reply at bradciper.com.
500 unknown command.
Why? Why is that an unknown command?
How do we want to do this? Okay, so we're going to print the session. So we'll just do IO print and it'll be the state and it'll be session.state can't concat those, right? So, is there like a two string?
Oops.
Uh, gleam enum to string.
Oh, really? Use a use case to manually map all of them. O fun uh session state how do we okay fun session state to string and we'll take in a session state and that'll be of type session state and it will return a string.
Actually, we can just infer that invalid argument name.
Sure.
Um, session state session state.
So case state code action missing patterns greeting ready mail from RCPT2 data. Uh we'll call that down here session state to string.
We'll do another print line.
We have our buffer. Our buffer should be a string. So, we should just be able to do our buffer like so.
Uh yeah, sorry. Session.buffer do another IO print line.
This will be the from and it'll be session from same issue there. So we'll need to map that.
So I don't know we could just do um let from can we do inline cases case session from uh option Nice. That's actually pretty clean. Uh, okay. session from man I'm getting tired which makes me think that I am due for lunch our two so we have a list so we can do session two um do like a list map How does this work? So we have list.mmap. Go to definition. We take in the list. We take in a function with function.
Oh, we don't actually want to map, do we? I really want to like um join. I guarantee there's a string join, right?
So, if we do string join. Perfect.
Nice. That's pretty clean. Uh, got to go. But but good luck. Hope to see the next one of these. Yeah, I hope so, too.
Print line data lines. Same thing.
Okay, that's our session printing. So let's hop back over here and then basically every time we get access to our we get a message we get a valid message. Let's print our session going into it. So we can just call session print state.
Let's give this a run. Wrong thing.
All right. Big Tet.
Hello.
So, okay. Greeting none. So if we send another message uh mail to Okay, so we default to greeting. Yep.
Yep. Looks good. Buffer from.
So if we send another one um rcpt22 test bradcipyer.com 250ac from didn't get added there.
Okay. Um yeah it seems we have some bugs. We'll have to debug this. I need a break. So I'm going to call it here. But thank you for coming out for everyone who did come out. Had a great time.
Hopefully you did as well and um I will see you in the next one.
Peace.
Related Videos
Agentforce NOW AMA: Build with React and Salesforce Multi-Framework
SalesforceDevs
490 views•2026-05-28
How agent o11y differs from traditional o11y — Phil Hetzel, Braintrust
aiDotEngineer
450 views•2026-05-28
WEB TECHNOLOGIES UNIT-2 | Degree 4th sem BCOM Computers web technologies unit-2 full explanation💯✅
LearnwithSahera
1K views•2026-05-29
More tests are always better? How to use AI to identify tests that bring little value
Alliance4Qualification
335 views•2026-05-29
Search Algorithms Explained in 60 Seconds! 🤖💨
samarthtuliofficial
218 views•2026-06-01
People of Game of Thrones using JavaScript DOM
AltCampus
296 views•2026-05-30
Introduction to Problem Solving Part - 1 | Lecture 1 | Intermediate DSA
ascensionix
107 views•2026-05-29
So What's Odin Lang Even Good For
TechOverTea
131 views•2026-06-01











