We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Surprisingly Unsurprising

00:00

Formal Metadata

Title
Surprisingly Unsurprising
Subtitle
The joy of unexpected simplicity
Title of Series
Number of Parts
637
Author
Contributors
License
CC Attribution 2.0 Belgium:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
Raku is a programming language that spent years in development under a philosophy of torturing the language implementor developer for the sake of the language user. This results in the language and its core classes behaving in a way that it is surprising — particularly to those coming from other languages — but also intuitive. The philosophy also carries over to those who develop modules as well, particularly as certain features in Raku allow for modules to integrate themselves deeply (and often transparently) into the language.
179
Thumbnail
20:09
245
253
Thumbnail
30:06
294
350
Thumbnail
59:28
370
419
491
588
Thumbnail
30:18
Computer programmingSoftware developerCodeInteractive televisionQuicksort2 (number)Level (video gaming)Arithmetic meanComputer animation
MathematicsJava appletScripting languageFatou-MengeMoving averageDefault (computer science)Statement (computer science)Electronic mailing listFunctional programmingDataflowProgrammer (hardware)Electronic mailing listMoment (mathematics)MultiplicationWater vaporOnline chatRegulärer Ausdruck <Textverarbeitung>Element (mathematics)Matching (graph theory)BlogBlock (periodic table)String (computer science)QuicksortMultiplication signRepresentation (politics)Programming languagePoint (geometry)Statement (computer science)Type theoryResultantBitDefault (computer science)Different (Kate Ryan album)Data structureCodeIP addressPlastikkarteNumberModule (mathematics)IntegerMathematicsSoftware developerSocial classFlow separationDemo (music)Scalar fieldComplex (psychology)Library (computing)ChecklistModule (mathematics)Execution unitUnicodeCalculationFatou-MengeSineStreaming mediaFood energyEndliche ModelltheorieAlpha (investment)40 (number)Level (video gaming)Process (computing)FamilyParameter (computer programming)Limit (category theory)Membrane keyboardMereologyTerm (mathematics)Scaling (geometry)Taylor seriesNetwork topologyTheoryCellular automatonProfil (magazine)Formal languageComputer animation
Parameter (computer programming)System callString (computer science)Control flowStatement (computer science)Condition numberCodeObject-oriented programmingBlock (periodic table)Loop (music)Multiplication signMaxima and minimaResultantSpacetimeSystem callLevel (video gaming)String (computer science)Parameter (computer programming)Scalar fieldIterationQuicksortNumberElement (mathematics)Electronic mailing listDifferent (Kate Ryan album)CodeProcedural programmingCASE <Informatik>Statement (computer science)1 (number)Block (periodic table)Reading (process)Loop (music)Functional programmingMessage passingSingle-precision floating-point formatGame controllerRule of inferenceClosed setSound effectObject-oriented programmingLine (geometry)2 (number)CoroutineSet (mathematics)Module (mathematics)Open setLetterpress printingMathematicsGame theoryBit rateRight anglePrice indexExpressionGoodness of fitInfinityComputer configurationCondition numberCue sportsFlow separationOrder (biology)Disk read-and-write headAxiom of choiceExtreme programmingControl flowBitComputer-assisted translationRing (mathematics)Computer animation
CodePrimality testComputer-generated imageryLocal ringBlock (periodic table)Computer virusProgramming languageInternationalization and localizationFormal languageFormal languageDivision (mathematics)DivisorFunctional programmingCodeCondition numberDefault (computer science)String (computer science)Boilerplate (text)Figurate numberLocal ringPhysical systemBlock (periodic table)Object-oriented programmingRevision controlVariable (mathematics)System callLevel (video gaming)Prime idealSet (mathematics)Statement (computer science)QuicksortChainControl flowTranslation (relic)Process (computing)BitLine (geometry)Boom (sailing)Dynamical systemElectronic mailing listInheritance (object-oriented programming)Exception handlingResultantRight angleSingle-precision floating-point formatCASE <Informatik>Group actionArithmetic meanEndliche ModelltheorieCuboid2 (number)Physical lawSineBit rateParameter (computer programming)MultiplicationContent (media)Goodness of fitTwitterWater vaporMathematicsComputer animation
Execution unitModule (mathematics)Local ringFormal languageBlock (periodic table)Programming languageCompilerData structureComplex (psychology)MultiplicationFormal grammarToken ringAlpha (investment)Regulärer Ausdruck <Textverarbeitung>FlagSeries (mathematics)CodeParameter (computer programming)Multiplication signFunctional programming1 (number)CodeAttribute grammarGroup actionSurfaceWritingBitFunction (mathematics)Standard deviationLengthInformation privacyCheat <Computerspiel>MultiplicationLetterpress printingMaxima and minimaOperator (mathematics)TimestampStatement (computer science)Motion captureModule (mathematics)Formal grammarFlagRegulärer Ausdruck <Textverarbeitung>Internet service providerCASE <Informatik>Block (periodic table)QuicksortLink (knot theory)Type theoryDifferent (Kate Ryan album)String (computer science)Matching (graph theory)NP-hardPoint (geometry)Token ringFormal languageInformationIntegrated development environmentSystem callCase moddingNumberResultantAlpha (investment)Moment (mathematics)IdentifiabilityWell-formed formulaSocial classCategory of beingLine (geometry)Product (business)Module (mathematics)Object-oriented programmingElectronic signatureCellular automatonHost Identity ProtocolEndliche ModelltheorieArithmetic meanSummierbarkeitPhysical lawNetwork topologyPerspective (visual)Bit rateDirection (geometry)Arrow of timeRegular graphCartesian coordinate systemTablet computerSoftware testingShared memoryProcess (computing)
Modul <Datentyp>Function (mathematics)Message passingSerial portUniqueness quantificationData modelEquals signMetropolitan area networkAreaPrincipal idealStatisticsTotal S.A.Square numberBendingCharacteristic polynomialAverageError messageString (computer science)Social classReverse engineeringProgramming languageModule (mathematics)ChecklistBoilerplate (text)Computer configurationLogische ProgrammiersprachePersonal digital assistantFunctional programmingData miningDifferent (Kate Ryan album)Module (mathematics)Function (mathematics)Standard errorNumberPower (physics)Computer fontCodeAdditionPresentation of a groupDatabaseRegulärer Ausdruck <Textverarbeitung>Electronic signatureParameter (computer programming)Declarative programmingText miningObject-oriented programmingSampling (statistics)Module (mathematics)Numerical analysisChecklistSound effectComputer configurationWeb 2.0Boilerplate (text)Logische ProgrammierspracheTheory of relativitySocial classWordString (computer science)Token ringSocket-SchnittstelleProgramming languageQuicksortType theoryFormal languageMoment (mathematics)BitSubsetResultantGame controllerMatching (graph theory)Block (periodic table)PlastikkarteInformationPotenz <Mathematik>Software developerFrequencyDefault (computer science)Internet service providerSemantics (computer science)Set (mathematics)MereologyGoodness of fitLine (geometry)Reverse engineeringCore dumpExtension (kinesiology)Flash memoryPhysical systemError messageCASE <Informatik>Network socketObject-relational mappingMultiplicationEndliche ModelltheorieWebsite4 (number)Metropolitan area networkSingle-precision floating-point formatLetterpress printingOrder (biology)Selectivity (electronic)Peer-to-peerDistributed computingControl flowBit rateSource code
Presentation of a groupElement (mathematics)EmailMultiplication signGroup actionRight angleFormal languagePlanningBinary codeSequelType theoryDeclarative programmingRegulärer Ausdruck <Textverarbeitung>Computer animationMeeting/Interview
Declarative programmingHydraulic jumpMultiplication signBitMacro (computer science)Object-oriented programmingParameter (computer programming)SequenceTable (information)System callLimit (category theory)Abstract syntax treeStatement (computer science)Social classMatching (graph theory)QuicksortCodeForm (programming)GravitationCausalityCohen's kappaMeeting/Interview
Element (mathematics)Meeting/InterviewComputer animation
Transcript: English(auto-generated)
Hello, I'm Matthew Stuckwish and welcome to my talk. Now the title of my talk is surprisingly unsurprising, the joy of unexpected simplicity. But there's also a second subtitle here, which is going to be and how to bring it to end users. Because Raku's ecosystem is still in a relatively early state,
I really believe that thinking and talking about how users interact with code is important. I mean, that is sort of the Raku way since it was under development for so long, because everyone wanted things to be just right for us today. Those of you who already know me know that I'm a literature guy.
And so to illustrate what I mean in this title, I wanted to share with you a short poem by Marta Mori that I think really describes a lot of people's experiences, certainly mine, with Raku. It goes, I like surprises, I like those things I can't anticipate, the sound of water in the depths of the forest,
open endings in those late-night chats that, like the flow of a river, can lead us to places known and unknown. I like surprises, I like the chance and fortune, for this I yearn for the calm that might end up bringing me
chancefully to the next moment. Raku is designed to just let our code be simple and unremarkable. And yet, even for people like me who have worked with Raku for years, I still get these moments of, oh wow, that's really cool, I had no idea Raku could do that. And not for me trying to push Raku to its limits,
but from everyday bits of code. The goals of this talk. First, I'm going to describe some of the surprisingly mundane features of Raku. Then, I'm going to consider how some of them might be used in module design.
And then, I'm going to give several demos of ways that you can create or even recreate some potentially useful things. Then, I'm going to kind of finalize everything by showing some ways that existing modules have approached things to stay Raku-ish, and take advantage of these things that I'm going to talk about. And then, lastly,
I'm going to provide a rough checklist for module development to sort of keep us within this sort of idea of being Raku and taking advantage of the language. So, let's start with a quick first example. Point one plus point two equals what?
What's the value of these two numbers added together? Now, there's two main answers that different languages come up with. For answer A, 30 quintillion, 46 tillion, we have languages like C, Java, JavaScript, Julia, Python, 2 and 3, Perl, Ruby, Rust, and Swift.
For answer B, 3 tenths, we have languages like SageMath, R, Mathematica, MATLAB, and I don't know about you, but those languages seem to have something in common. Sounds like they know something about math. But, oh! Here's Raku 2.
Now, there are libraries or special syntaxes that you can use to get our point 3 in some of the other languages, but the reality is that adds extra complexity to get the natural result. In Raku, the two most basic class types, numbers and strings, are chosen smartly.
Numbers prefer rational and big integer types, and strings default to a grapheme-based Unicode. At the end of the day, smart defaults save people time that they might not even know they're otherwise losing, and I say this as
somebody who works a lot with strings, and I think a lot of people don't know how big of a deal trying to work around all the intricacies of Unicode can be. But, obviously, also somebody who works tons of time, scientists who need to do numerical calculations, they also understand some of these issues of numbers, and
why waste their time making them have to use big integer, class, or all these other things? Defaults are there. If somebody needs performance because they need to use floating points and take speed advantages from that, or getting, taking advantage of rollovers from, you know, more native type integers, those are available. But what most people want is the default, and it saves people time and a lot of hassle.
Switch statements in Raku are a very interesting thing, and I wrote an article about this blog post a couple years ago about it, about how it's a lot more powerful than what you get in
most traditional languages. And this is really because, in part, Raku doesn't use a traditional switch statement. Instead, it uses given. Now, granted, when you see a typical given statement here, like given foo when 1, when 2, when 3, default, doesn't seem like there's a huge difference. The only thing is that it automatically
breaks, as opposed to cascading down like a traditional C switch statement would do. And so you might think this is a little bit of a distinction without a difference. And, in fact, I think a lot of programmers will go through working with Raku blissfully unaware of really just how powerful the switch statement
can be. But if I change the, this one to say this, given foo when a, when 1, and when regex alpha, I think it's interesting because it's a little different how most languages handle switching,
but the code is also instantly readable. We all understand how this, how this flow is supposed to work. Now, what if I do this, though? I'd venture to say that this is also readable to everyone who's looking at this code.
And yet, it's a little different. In fact, I can even push it further and add in a when, whatever, or string, do whatever, other thing. And this is totally readable. And I came across this structure a few years ago when I wrote that blog post, and I was thinking,
wow, this is so cool that back in the day, Larry and everyone else thought that maybe someone would want to do a switch over multiple items. And then I realized, actually, wait, no, that's not what's happening at all. All we're doing is switching over a single element. It just happens that the element is a list, and
what given does is it smart matches the given with the when statement, and it just so happens that smart matching in a list in turn smart matches each of the constituent elements of the list,
such that we could rewrite this, if we wanted to, as this monstrosity of if FUBAR smart matches with A and B, and this in turn can also be written as if FUBAR smart matches to A and if BAR smart matches to B, else of all these other different things.
And then it's also interesting if we take a look at this star and string, and how they mean, right? The star is a whatever block and means exactly that, and the string can type check, so everything is working differently here based on what just makes sense.
But when you realize everything that's going on in here, it's both simultaneously mind-blowing because it does so much for you, and yet it's pretty pedestrian once you understand how it's actually working. And so we have, you know, and Raku is able to do this where we take this incredibly complex code, and we give it this very sort of declarative,
beautiful, concise representation in the given statement, which I think is pretty much universally, we can all agree, just looks better. Now I'm going to talk about the Slurpee family, and this is, I think, the first of the features that I'm talking about that really apply to
module developers. There are three different ways to consume lists of items that have been passed in as arguments, which I, some time ago, very creatively
determined, or gave the names of Pour and Savor, Chug No Regrets, and Read the Label. Now there are reasons for these beyond just wanting to tinge this with a little bit of a bartender humor, but we have the first one, which is Pour and Savor, and these are items, what this means
is that items inside of lists are iterated themselves and effectively flattened. So you can see if I have this very simple subroutine here of parrot, right, sub parrot, where all we do is just say back every item that we get, and I have this very
complicated embedded list of 1, 2, 3, 4, 5, 6, 7, 8, and a simpler one of A, B, and C. You can see the result is just that, 1, 2, 3, 4, 5, 6, 7, 8, and A, B, and C. And this is something that's really useful if you know, you have a
function that's supposed to iterate over or do some process, a list of items, and they are all ultimately going to be some scalar class, you know, thing, whether it be a list of IP addresses or whatnot. And this is also the same as the default behavior in Perl 5. Then we have Chug No Regrets, and what this effectively does is takes in the list and
treats it exactly as it was given. So if a list is given as a list, or if we pass in a scalar value, it's treated as a scalar, and if it's a list value, it's treated as a list value. And so we can see here on this one that the 2, 3, 4, 5, and 6,
they're all contained within one set of parentheses, and so they are passed collectively as a single list in that list. But the 1 stays on its own, as is the 7 on its own, and then the 8 gets kept in there as a sort of super embedded list. On the other hand, when I pass in the A, B, and C, in the array signaled
variable, it stays as A, B, and C as a single item, because I only passed one item. It just happened that that item was a list. And this is certainly one where if you need to treat things that are iterable and non-interable differently, this is what you as a developer will want to put in to your
module. Or if it's something like say, like the say statement, right, we'll use this because, you know, it's just going to call the gist function or the gist method on each one of these, so it doesn't really care what they are. So it's sort of like if you don't care or you do care, this is when you need to do it.
Use the the chug no regrets or the double starred one. And then lastly we have the read the label, and this just decides smartly by the single argument rule. And what this means is that if we passed in a pass in a list of items
like we did before, then it handled, it treats it just like the chug no regrets. It gives us the one, then it gives us this list of two, three, four, five, and six, and the seven by itself, and the super embedded list of eight. But the difference is when we pass it in a single argument that is also iterable,
like in the case of our array sigil ABC here, then it no longer treats it as a single item, a list of one item. Now it goes ahead and expands it, and we get to, it passes it sort of directly in. And so now we iterate the A, B, and C separately.
And, you know, when you think about it, there are uses for all three of these to work as you would expect, and so if you look at a function, as we think about these poor and saver chug no regrets and read the label, and then let's think about some functions like say. If I say say, you know, A, B, and C here,
versus saying A all by itself, well, I don't want it to treat A any differently here. And so that's why we would use chug no regrets, right? Unless you want to actually say all the way down to the individual scalar items within A, then I guess you'd use the poor and saver. And there might be a situation where you'd want that.
On the other hand, when we use for, if I say for A, B, and C, well, the most obvious one here is to read the label. Here I pass in three items, and I want it three iterations. It's very clear I want three separate individual distinct iterations here on each of those three values. But when I say for A,
it's also clear I want, no, no, no. Now I want us to drive into the elements of A and iterate over those, so the number of iterations will be determined by the number of items in A. And that's what this read the label sigil would do, assuming four were a function. And it's actually a control statement, but we're actually going to get to that in just a second. Parentheses.
In Raku, parentheses don't make a list. Parentheses are honestly more likely to be superfluous. For example, we can simply say my foo equals one comma two comma three done. No parentheses needed to create a list here. And this works even for
sub and method calls. So I could say bar open parentheses foo A and B and close parentheses, or I could say bar foo A and B. Or for methods, I can say foo dot bar parentheses A and B close parentheses. Or I have two options. I can use an object-oriented style, foo dot bar
colon A and B. Or I can use a procedural style, which is bar space foo colon A and B. And a lot of people don't, I find the procedural style doesn't get used anywhere near as much as I think it should in Raku. I think there are absolutely times that this is for clear and concise code. This is the style that needs to be used.
But I don't see it used as often. So maybe you think of when you can use that in your code somewhere. But these sort of avoiding of parentheses, we can chain it as long as each call is the final one of the previous. So if I take a statement like A
of B of C of D of E of F of 1, 2, and 3, we can trim all this down to just A space B space C space D space E space F 1, 2, and 3. And what we're doing is we are still doing the same thing. F is being called with the arguments of 1, 2, and 3,
which then is pumped into E, is pumped into D, is pumped into C, into B, and into A. Now that's kind of an extreme example, but sometimes if you have functions that are smartly named, then you will see that this can kind of make code a little bit clearer. So if I get something like eat, bake, sear, butcher, get, cow,
you know, we can kind of read this backwards and see what's happening, right? We are eating the result of baking, the result of searing, the result of butchering, the result of getting a cow. And that's a lot cleaner than all these parentheses here. And I even use it a lot of times when I'm doing something like say, and then substring, and then string,
you know, whatever the string might be, zero, and then the max of these two other different things. And you can see here there's actually about three levels of calls being used, and they all happen to be done, you know, by leaving kind of the one that most likely would have multiple arguments or it might be called out to a different routine. It always gets put at the end.
So this is actually even just like smart designing of these routines. And they're not even required with control statements. And this is I think one of the things that most throws people off when they get into Raku. I mean you can absolutely use parentheses here, but there's there's no problem with it.
And so we can just say if condition, run it, or for list like we talked about earlier, and run it. Even when it's a fairly complicated one. I can say unless foo and bar or abc and x, y, and z or override. And here the and even has a tighter
you know, precedence than the or. So you can do something like this without a single parentheses. Which is just beautiful. And so why is this important? Well, it's cleaner code, less line noise, no parentheses, hell. And in fact, I even remember here
a couple months before we passed, Dr. Fort kind of had mentioned doing making a special challenge for himself of trying to write his code with as few parentheses as possible. And I think almost anybody who writes Raku code can benefit from trying to take that approach. You can't get rid of every parentheses, but if you try to
you'll be really amazed at what you can do without needing to to use them. And so definitely try that out sometime. Now on the other hand, the lack of parentheses does have one side effect. Methods, subs, and control statements can now all be very visually similar.
But that's not necessarily a bad thing, but I'll get back to that in a second. In Raku all blocks are objects. So if I take a to these two subs for instance, just to show an example of it, we have subs foo and bar, and they've both taken a single
argument. If I just say foo and then pass it in a block, we can actually see that it's quite literally just a block and we see all the raw data about it. And if I say in the second one in bar, you can see that we can just
execute it on command when we need to. And so this might lead you to think that maybe something like loop followed by a block, maybe it's just a sub and basically, you're right.
So let's try this out. Let's make our own loop control statement. And so to use this to avoid terminology confusion here, we're just going to do this in Spanish. So we'll do sub bucle with a single argument called codigo and you'll see all we're going to do here is take the codigo and we're going to execute it with our
postfix parentheses and we'll just do xx infinity. So this code will end up running an infinite number of times. And so then I can just put this into a normal code and I say bucle, say hola, and there it's going to print off an
infinite number of holas. So we can then take this and maybe create some more complex ones. So let's think about something like gather and take or what I'm going to call collect and grab, again so that we don't confuse what's actually built in and what's not. And so all we really have to do for this, if you think we need our
gather block, right, all it does is it returns a set of items that have been collected in a list. And so and then we have another function which is take, right, which simply adds an item to that collection.
So how could we do that in completely vanilla Raku only using sub functions? And we can begin with a sub collect and in that we're going to take in a block, which is our code, and before we execute our code, we're going to take advantage of the dynamic variables and we're going to initialize
one called collection. And then at the end, we're just going to return that. And so that's simple enough. So all through this code block now that we've executed, that code will have access to our new collection object. But of course, nobody wants to call that, that's supposed to be our own internal variable. So we have a second
sub which is going to be called grab in our case and grab will take a single argument, an item, and what will it do? Well, it will push it onto our collection, right, and this is very similar to our take function that we would have normally. And actually that's it. In all of what,
seven lines of code, we have now recreated a version of gather and take. Now, it's not quite as fancy as you know, the built-in one, you know, this is not a lazy collection by any means, but it gets the job done. And so we can test this out now, right?
So here you can see the function on the left, the original that we wrote, and we'll just say my primes equals collect grab if, if is prime for the first hundred. And then we just afterwards we say prime, say primes, and we end up with a listing of all the primes. And because of the way dynamic variables work,
which is that they can be reset at various levels of the call scope, or the call chain, we can actually nest this and it works exactly as we would expect it to. And so each of these two collects has their own
dynamic variable that they, of collections that they're holding on to, and then the grabs within each one will only refer to the one on the nearest outer one. And so this, as you see here now, right, we're taking them if it's divisible by three and then feeding that one back into the next one if it's divisible by two, so we should end up with
basically factors of, or multiples of six. And so if we say six factors, you see that is indeed what we actually get. Super simple code, and we've been able to recreate something that really shows off some of the features of Raku. So we can take this to another level, I think, and show off
really a very practical way that we can use this with a localized block. So when I say this, what do we want? Well, basically this. I want a say statement to produce you know, content exactly like we normally would, just parodying things back, except for when we are inside of a sort of localized
block, right? And so we see our localized say hello, except that I don't want it to then print hello, I want it to translate it in some way. Now I'm not going to go into the different ways of providing localizations, that's a totally different thing. I want to just focus on this localized block, right?
And so I basically, what do we want? We want to say hello, but then inside this localized block we want to change it to sort of chain the function and say translate hello, which is you know, not too, not too terrible, but the fact that say is a built-in for right now is probably going to throw people off. So
let's go ahead and just sort of mock things up with a foo sub. I think this one can work a little better. We can separate things out from our initial say, which is a built-in and devise how we want things to work. So what do we want in this foo sub? Well,
basically, it's a very simple if statement. If some condition we're going to translate, otherwise, we're just going to do a normal say. But now the question is how am I going to get that conditional to be put in there? Well, if we remember from the collect and grab example,
we can always just pass in dynamic variables. And so if we, all we have to do is put in a my localized and set it to be true. And then when we call foo, we can come back in and now it'll say if localized, then we'll translate it. And otherwise, we'll just leave it as is.
So that works just fine with foo. Now we've got it working. But now we actually do want to put it on say because say is what people actually use, not foo. And again, I want us to try to merge as you know, deeply into Raku as we can. And this is actually very simple to do.
We have this original statement, right, sub foo if localized, say translate, otherwise. But I can actually just rewrite this changing only the tiniest little bit of code. And instead of saying sub foo, I'm going to now say the say.wrap and
we're gonna wrap it with this sub, which is exactly what we had above, just without the name foo. And instead of saying say because we're wrapping say, we can just say call with, which means we recall, we call the next candidate for say, which is the original one, but with a new argument, which will be translated version of our string. And otherwise, we just call the exact same one. And
now when people use this, whenever somebody says say, they will actually get this instead of the original one. And this is global, right? So the conditional here is very important because we do not want to be affecting other
people's say statements. We want to make sure that the default, if we are not somehow in a special condition, we want to make sure that we call the original one. Alright, so that works pretty well. But we have this translate function in there.
And the translate needs to know what language we're dealing with. So, well, that's easy enough. We just say, you know, my and then language and we set it to something like English or whatever. Except I don't want my users to have to think about putting in the language. I mean,
presumably the language that their system is running on is the one that they would want and that's a good default to work on. So, thankfully there's a module for that. And it's called international user language. So we import that one in. And then we just use the user language which calls up the system functions and figures out what the
system's default language is. And then we can pass that through. So that's great, but you know, that's a lot of boilerplate. So instead what we can do is is put all this into our initialization just like we had with
the collect block. And so here we have now the sub localized. It takes in a block and there we put in use international user language, my local is true, language equals user language, and then we execute the block. And then we can see the results of this, right? So I say hello. And now let's say my system is set up to Spanish.
Then presumably if our translate function is working as we would expect, we could say say hello and we get hola. Say goodbye, adiós. And then now outside of the block our by would just pair it back by. But what if we wanted to set our language manually, you know, maybe
we're actually getting input in from some other function. We need to send it out. Somebody's requested a particular language. Or for just whatever reason I don't want to use the the default that's been established. Well, we want a nice easy way to override that. And so let's say I wanted to do Korean. Well, I think this syntax right here would work very nicely for us. Just say language and then give us a language code.
So how might we do that? Well, very similar to how our grab would have worked. We take our initial localized function and we see that we have that dynamic variable language. And so we just make a new sub which is language, takes in a string and boom sets it and that's it.
And now again with only a handful lines of functionality, we have managed to create this this very specialized localized block that looks like a native control structure and yet it's not of course.
And so we can see the results here, right? With Korean or then we could do German or even Cherokee. And this is it. This would be the entire module for what we've done here. As you can see this maybe reaches 20 lines of code including comments.
Obviously if you did this in a production environment, you'd want to wrap more than just say you need to catch some of the methods because some objects have their own same methods. Or you might want to do the print functions and some others but
effectively, you know we have this very functional module here that you could install today if you want it. Now, let's move on to traits. Traits allow you to modify most anything at compile time.
So we have example here of some of the built-ins. Class food is export, has thing is read write, has private is built and all of these, you know you might think that there's some very complex structure or at least something that's been special cased because all these things have very specific actions that they do. But they're just subs. They're sub that receives
the class or receives the attribute or the sub or whatever you might be passing into it. And then they can do things that compile time to it. They can modify features of it. So let's consider a trait that
wants to log access to a sub. So we have our module secret stuff and it has a sub called get. We could do this as simply saying is logged and all of a sudden this entire everything that happens in this sub we could figure out what's being what's going on.
And if you're in a you know an environment where access to information is particularly sensitive say in one that for us in the United States that in HIPAA controlled environment or one that you have to follow FERPA guidelines, which are our data privacy you know laws. This might be one that could potentially help fulfill some of those
or at least verify that things are being done correctly. So how are we go about doing this? Well, the first thing we do is create this kind of very fancy named sub which is multi sub trait mod is and it's always gonna have two arguments. The first is the thing that we're going to apply the trait to. In our case
it's going to be a sub, but you could make this an attribute. You could make it a class. You could make it anything. The important thing though is to make sure that you here you do want to set up types and do hard strict typing because otherwise it'll try to apply it to a whole bunch of different things other than just what you want.
Definitely maintain strict typing here. And then we actually want the name of the trait which will be a named parameter and you'll want to make it mandatory. So definitely put that exclamation point after it. So what this function will do this sub right here is the signature for this signature here is for a trait that would just say is logged.
Right and then on to that What are we gonna do? We're just gonna wrap this up as we saw we can wrap the same one well here at compile time we can wrap the sub and modify it to give it a lot of newer properties. And so in this case we have the right sub
takes in all of the arguments and we're just gonna call at time which will insert the current time called and then r.name. That's the name of our routine that we're doing here which of course would be the get but of course you apply it to other ones and you have different names with and then we
just pass in the arguments and we'll get the output of it. So if we were to run this we would get the following. So we have this multi sub here's up there and we have our sub get medical data that patient employee is logged and then we'll have our little statement here if employee may access patient
and I'm cheating here may access is actually an operator because you can define your own infixes and sometimes named ones work really well like max and min are two and I think this is a great example of one where it it works nicely and makes your code more readable.
So if employee may access patient then we do some other stuff and so now at the end we call get medical data for John and Dr. Jenkins is requesting it or get medical data for Jane and our output it says at and then we have a timestamp
called get medical data with and there we see the capture. That's what the backslash is for of the arguments that we had at the function. And so traits can be these very powerful things that on the surface look very very simple. This just is logged and just like the built-in ones
they kind of give these same ideas. We just want to add a little bit more and they do a whole bunch of stuff in the background at compile time for us. And so traits are something that are really fun to play around with in modules that can they can do a lot with very little from the end users perspective. The last thing that I want to talk about here are regex and in tokens. We've got here
for example, if I do a grammar, right, you can just put in these different tokens. Everybody who's used Raku at this point I think has written a grammar and kind of understands this. Really though what's happening here is this special syntax inside of the arrows, that's technically just a method call that returns a match.
Right, it has a different syntax because we're in the regex language and not in the main language, but effectively that's all it is. And these can also be declared outside of our regex and our grammars so that we can use them across multiple definitions of grammars or other regexes.
And that actually can be very powerful when we're creating modules to provide things for users that fit within sort of these very established ideas. So for example, I could create
let's say using some emoji here, we could make a token called happy and give a couple of different smiley faces and another token called sad and another token called flag which uses the two regional identifiers which combine to create a flag. And then we can now create a sub that's called describe that then just detects say emotional if happy or sad.
Say patriotic if the text has a happy face followed immediately by a flag and you'll notice that this syntax then it's very predictable, right? It looks like saying alpha and yet we're describing very different
things. So we can then just say something for the user, right? They just put in happy or sad and it just does what they expect it to do. And you can see the results of this, right? Describe I got the job, emotional, I failed the test, sad face, emotional, we won the gold, happy face, American flag, patriotic. But where this gets even more interesting is that tokens can have code in them and can easily
dictate how far to advance the token even if they're not matching based on anything that's actually a regex. So for example, let's just take a very simple token here foo and
we'll just set up an immediate a quick variable advance and inside the code block we can actually get access to the string that we have and you just do that with doing the dollar slash dot orig dot substring and then just start it from the two and that starts us right at the beginning of the
you know, wherever we're at at that moment in the match and and then you can do some function over that so maybe you have something where creating a trying to create a regex is too complicated or it's a very complex formula that you need to use to determine it
you could actually just write standard rockoo code to detect it and then return the length of it and set that to advance and then outside of that you just say match whatever because at this point we already know exactly how many characters we need to go so we don't need to do any other additional checks and say whatever and take the advanced number of them
And if you set that to a link that you know of then you've set exactly how far it needs to advance You know if you potentially could fail this match. Well, you just put it in a fail block And of course our advance here at the end will You know verify that indeed we have
You know advanced and a zero would fail So there's a lot of different ways that you can play around with this in certain cases. You might actually have a performance benefit here If there's something that you might be able to do a little bit faster in code than in regex or might be more easily expressed through code than in regex
but if you can do that Then you can export it out to somebody else and they can use it and they have They might have no idea that you're actually using code instead of regex All they know is that it's a token that or a character class or whatever it might be best described as that they can reuse So the last thing I want to do is a showcase of modules that just work and how
And I'm going to show off a couple different modules by different people And I think really show things off There may be a couple of mine in here just because I I've really tried to do and I can also explain what's going on Behind them a lot better First off we have silently by Elizabeth Mathiessen. It functions just like quietly
Except that it also suppresses output from standard error. And as you can see in practice, it really does work just like quietly And the way it works is very simple. It takes in a block like we did earlier But when it creates the captured object that you can see it sets out an error to
Objects that mimic an IO handler but save what they're passed so that we can capture it and return it back if the user needs to Inspect it later and as you can see It's a very simple way to get a nice new feature that really looks like it could have been built into Raku
Crow is a module made by edgement designed for distributed systems and is particularly popular for its HTTP handling Here's a sample of code using it for web sockets If we look at it We can see the same style of using subs to mirror control words and they blend in seamlessly
But one addition is the signature for the blocks For the first one get the parameter is a literal and isn't actually used in the block Instead crow intro specs it to process the type accordingly which makes it blend in a little bit more with the rest of Raku's code in
The second one Instead of running the block with simple parentheses as we saw in our previous examples an argument is passed in So the web socket block feels a lot more like a given block And the end result is some wonderfully declarative code masquerading as vanilla Raku
Next we have read by Fernando Correa de Oliveira It's an ORM module for interacting with databases If we look at some sample code for it We can see how simple clean and declarative we can make things by extensive use of traits as well as a custom declarator Whereas traits allow additional work to be done after an object is impaled
The declarator allows us to modify the higher-order workings or how in effect allowing us to do even more setup in the background Now how that works is a little bit out of the scope of this talk But nonetheless you can see how the traits here are setting up some what would be very complicated relations in the background
freeing the user from having to write all this database code manually and yet even with all of the power that Red provides Looking at code like this Really makes you think that Raku might have been designed just as much for database work as it was for reg X and That's pretty incredible that we can do that just in a module
Next we have one of my Modules, which is international token number And this one just because I mentioned the tokens earlier. It kind of shows off how you can do this everybody's had to have those moments where you take a You want to do some text mining and find all the numbers except, you know, if you see in this text right here
We've got all sorts of different types of numbers. I have percentages I've got some with decimals in it. I've got some that have commas in it. And how do you how do you handle all that? No, everybody ends up writing all these complex, you know Regex to do it and then somebody says oh wait, but hey Can you do it for French now and in French the commas and the periods are switched up?
Or then they'll ask you to do it for Arabic where things will be, you know different Especially if you have like exponentials and other stuff like that And so it gets a lot more complicated But here we have the local number Looks just like any other number or any other token, but if you use it
You'll see that it can immediately detect all of these different Different numbers that we have here and then we can run this and we end up with this result and the way that it works is Actually this token ends up right before it gets exported gets wrapped by a method just like we saw those wraps before and
Then the wrapping mixes in a role that has a dot numeric method and this enables us to provide both the string Value as well as the numerical value and you can see where we have like the 90% that's actually translated into 0.9
There's a lot of things there that you can do For International language tag, there's just one small thing. I want to show you here's a rough outline of the class and you can see that we make it from a string and it also has a string method and in this case the string would be the exact same in both cases making it a two-way street and
The string method also means that if somebody uses a coercive parameter It'll course exactly as we want it to But the reverse as you can see in this code to the right Will actually error and this is because string doesn't have a language tag method to use for the coercion
Or at least that's how things used to work Just a few months ago. Ladin Bayman committed a new set of coercion semantics into core By adding this course method with a single typed parameter It's now possible to enable coercion from other classes into ours
So this code now works just as nicely as with any other core class This is going to be a great way to make things just work Now finally and to sum up this talk. I want to provide a sort of module development checklist These are perhaps more aspirational goals than anything
But if you're designing a module, I think the things on it will help guide your development First think about how a user would want to use your module especially with regards to form and defaults to that end We should aim to avoid boilerplate as much as possible while still finding convenient ways for enabling options when those defaults aren't what's needed
Next we should avoid having our module function in a bubble. I mean this in a few ways First we should provide appropriate accept methods for smart matching Coerce methods for coercion as well as logical string and numeric methods And you might even consider a special gist method too that gives good debugging information
We should also aim to where appropriate go beyond a mere sub or class in most languages That's really all about you can do but in Raku We can do so much more via declarators or traits or tokens and even more once we get into slang
Along those lines we need to remember that Raku is a multi-paradigm language And I think by having our modules work through these different parts of Raku Will help people be able to use the style that they feel best working in or that their code demands Third document this could be a discussion unto itself, but documentation removes so much headache for users
This is especially if you use pod6 so that you can integrate with other ecosystem tools Or if you use declarator pod so that comma can show it in line as they work Juan Jose Mirello Garibos has some really good talks and articles on the subject
Finally surprise the user with Raku-ish mundanity Bring the user joy by writing modules that have Flashes of brilliance that they find tucked away in their everyday code So that is my presentation I'm happy to answer any questions that anybody may have
Or if you've got any after the presentation feel free to hit me up on IRC on github or if you prefer just using old-fashioned email
It should be able to hear me now. All right if anybody has any questions. I'm happy to
answer them
So slangs yeah, they can Be much more good the only reason I don't I didn't really want to go into them too much as one time I actually was about to talk about the slang sequel And I had to cut it out, but I think slings right now. They're not quite as well developed as
roles and rappers and other such things are But certainly I think once we get Raku AST and We'll be able to do a lot more and I've actually got some plans there to do like a binary type reg ex It'll have to use the slang. There's no other way. I can I can get it done
As to whether it needs a custom custom declared requires a slang it actually does not And so if you look at all through red, I don't I don't think that on the uses of The slang at all anywhere or anything like that in his it's a little more complicated to set up So I can't just kind of explain it in
If I miss oh, sorry Jewish the question was if custom declarator requires a slang and It just it does not but it is more complicated to set up than just The things that I showed up on this one, so I can't I didn't want to go into it
Okay, so family. I need to go over all the former questions. So that I answered in chat
So, let me go back and read over There was one question about whether the Given when using the smart matching is inherent limitation in the sense that it takes a Speed or performance takes it has to actually do the smart matching on each one
and the answer to that is that it is actually an inherent limitation because of the way that the when statement works it has to be Checked in order or in your in sequence But if somebody were particularly concerned about it and wanted to implement something that was a lot more
Performant for something that really needed that performance. You actually probably could create a macro Once raccoon ast comes out that would be able to like pre-compile a you know A jump table or or do some sort of a binary search over the values as long as they're constant to do more of a C style one Let's see, what else did we have here
There's a question about whether for the When avoiding parentheses if for method calls if you had to use the
colons and or kind of what the purpose was and that that is that it helps to distinguish between Sub calls and method calls. So if you do a sub you say, you know sub foo ABC Or you just say foo ABC then you're calling foo with those three parameters
There's three arguments A, B, and C, but if you were to say like You know foo A and then you do a colon B and C then you're actually calling method foo on the object A and Passing up the arguments B and C. So the colon is is required
Then let's see here that was the main questions there, so I don't know if anybody else has any other questions
So I'm not sure there's a question here about if I accomplish quite a lot with a little to no class Declarations I'm not sure if that was for me or for Liz Matt, but
Yeah, there's not you know as you can see with that I don't think I actually really declared a single class at all You know in the in the code that I had So you can actually do Do quite a bit not having to touch that and I guess I should also do the
Call that always needed to be made is that if anybody has any questions on rock who and you come to? You know to rock who Definitely, you know and you come into the chat room
you know an IRC definitely stick around if you ask your question because if you a lot of times we get people that come in they ask question and Before anybody you can see it or answer They've already left and so people eventually answer it, but it might take More than you know 30 minutes or so especially if it's times when most people are asleep