Functional Programming with Go
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 490 | |
Author | ||
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 | 10.5446/47502 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
00:00
Functional programmingProgramming languageFunctional programmingRoundness (object)Computer animation
00:38
Menu (computing)outputFunctional programmingOpen sourceProgramming languageDescriptive statisticsOrientation (vector space)Library (computing)Self-organizationObject (grammar)Physical lawMultiplicationComputer animation
01:07
Interior (topology)WordLine (geometry)Social classBitMathematicsElectronic mailing listProgramming languageObject (grammar)Integrated development environmentResultantIndependence (probability theory)NumberFunctional programmingCodeSummierbarkeitPattern languageState of matterFunctional programmingCondition numberDeclarative programmingJava appletPhysical lawConcurrency (computer science)Video gameCycle (graph theory)FamilyComputer animation
03:06
Function (mathematics)String (computer science)Beer steinRing (mathematics)Normed vector spaceGame controllerComputer programmingFunctional programmingFunctional programmingOrder (biology)CASE <Informatik>Sound effectMultiplication signHydraulic jumpString (computer science)2 (number)DatabaseRight angleOperator (mathematics)Bit rateCodeDifferent (Kate Ryan album)SpeicherbereinigungPoint (geometry)Arithmetic meanGroup actionMereologyFunction (mathematics)QuicksortResultantGame theoryElement (mathematics)Pointer (computer programming)Object (grammar)Revision controlProcess (computing)Program slicingProgramming languageLine (geometry)Electronic mailing listPlanningPredicate (grammar)NumberMusical ensembleSubject indexingState of matterSystem callChainLogicInstance (computer science)Execution unitJava appletSoftware testingLoop (music)Lambda calculusMathematicsStack (abstract data type)TimestampDeclarative programmingCore dumpSinc functionBoolean algebraRandomizationoutputRow (database)Computer animation
08:33
Algebraic closureString (computer science)Hill differential equationReduction of orderConvex hullLetterpress printingDeclarative programmingComputer configurationMaxima and minimaFunctional programmingData structurePoint (geometry)Server (computing)Survival analysisConnected spaceQuicksortNumberHoaxMultiplication signService (economics)ArmStudent's t-testPredicate (grammar)Type theoryVariable (mathematics)Parameter (computer programming)Process (computing)Default (computer science)Hardy spaceBit rateCASE <Informatik>Electronic mailing listConstructor (object-oriented programming)Line (geometry)ResultantSimilarity (geometry)PrototypeProtein foldingAlgebraic closureStress (mechanics)Physical systemProgramming languageGame theoryTerm (mathematics)Different (Kate Ryan album)BitDemosceneRight angleSoftware developerInheritance (object-oriented programming)Letterpress printingFunction (mathematics)Order (biology)Revision controlBlock (periodic table)BuildingIntegerDeclarative programmingString (computer science)Query languagePointer (computer programming)Drop (liquid)Functional programmingInternet service providerIntegrated development environmentLoop (music)FrequencyLecture/Conference
15:58
Functional programmingSoftware bugLogicType theorySinc functionLevel (video gaming)WritingGroup actionPoint (geometry)Field (computer science)Computer animation
16:41
Menu (computing)MaizeHill differential equationFaculty (division)40 (number)Moment (mathematics)ProgrammschleifeComputer programmingNumberRoundness (object)Functional programmingLibrary (computing)Type theoryArmFunctional programmingRange (statistics)Filter <Stochastik>Metropolitan area networkForcing (mathematics)WeightMereologyStandard deviationOperator (mathematics)Multiplication signGenerating functionCore dumpPoint (geometry)Physical systemBitProgramming languageData structureElectric generatorSinc functionMassProgram slicingJava appletString (computer science)Reflection (mathematics)SummierbarkeitReduction of orderArrow of timeCASE <Informatik>CodeIntegerDefault (computer science)Pointer (computer programming)Lambda calculusAbsolute valueComputer file
21:39
Ring (mathematics)Condition numberRight angleAddress spaceOperator (mathematics)Functional programmingThree-valued logicGoodness of fitLecture/Conference
22:29
Point cloudFacebookOpen sourceDiscrete element methodLecture/Conference
Transcript: English(auto-generated)
00:10
Cool, so next up, we had someone that flew all the way from, I think, West Brussels. Yeah, so we have a Belgian person, which is nice. He's going to be talking about functional programming in Go.
00:21
Round of applause for him, please. Thank you. Can everybody hear me okay? Yep, okay, great. So I'm Dylan. I'm going to be talking about functional programming in Go. And so, when you look at these languages, one does not look like the others.
00:45
Most of them are obviously functional languages, and Go is actually kind of an outlier here. Well, it kind of looks like that when you look at a lot of open source libraries out there. Most of them are programmed in the same kind of pseudo-object oriented way.
01:01
But if you look at Go, the description is, it's a multi-paradigm language. You don't really have to stick to using objects everywhere. So when people think of what is a functional language, quite a few things come to mind. So you can have a declarative language, right? So in a functional language, you tend to say what you want, but not how you want
01:22
to get it. So in Haskell, you would say sum, and then a list of numbers, and it will return the sum of the numbers. While in C, you would iterate over the list and basically keep the sum in a variable and keep updating it, and then return the result at the end. Of course you want first class functions, which Go completely supports.
01:41
Often pattern matching, that kind of goes hand in hand with recursion, because languages tend to be very heavily on recursive side. You tend to have pure functions, so we'll go into that a little bit more. And immutable functions, so you have your state, and your state of your objects doesn't change throughout the lifecycle.
02:01
You create new objects instead of modifying one, and actually many, many more things. A scary word is monads, that's something that people relate to functional programming. But the question then is, why would you want this? Why do you want to do this in Go? And well, you create safer programs, you have easier concurrency, right?
02:22
You have no shared states, so you can run things in parallel without really influencing each other. You're going to have less code, but be more expressive. So normally I would say like having just one line of code instead of ten lines of code could be a bad idea if you're doing something traditional in Java, but yeah, if you read Haskell, that one line, just ten lines of work, but it's very, very expressive
02:44
and you can understand it, so that's an advantage. They tend to be easier to debug and easier to test. Everything can run independently. And yeah, it just makes using such a language kind of a pleasure to use. One of the complaints of this language is they don't have an IDE very often, but
03:03
well, the tools are good enough in the language that you don't really need them that often. So that's what you're going to get out of functional programming. So let's jump into some examples. This talk will mostly focus on how to use the functions and higher order functions. So you have pure functions.
03:23
So the basic idea is your function will not produce a side effect, right? That makes them more testable and you can run them concurrently. So in a traditional example, you have A, you run a function lambda, and you get B as a result. That's how every function works in a functional language.
03:41
In Go, you could actually have a function A, so A goes to a function lambda and it calls change name. And suddenly that object A actually has a different name, but it's not a new object. So you have now introduced a state that is modifiable. So in code, a simple pure function would look like this.
04:01
You have greet, hello, it takes a variable, it returns hello and then the name of the person. It will always do the same thing and it's safe. In a bad example, you want to count how often this function is called. And call counter is modified in the function. I mean, there's more wrong with this code because it's a global in this case.
04:22
And so now if you run this function many, many times, well, what will call counter be? Depends how often you run it. If it runs in parallel, it depends how far in the execution the program is, right? So different function calls might be at different steps of execution and you can't know what your call counter will be when your function has exited.
04:40
So in a more Go sense, so if we remove the global, we can still get this kind of behavior by having a rename method in this case. And so we're using a pointer now to a person, so the method receiver. And we modify it. Now luckily, this function is called rename, so you expect the name to be changed. But any function that receives a method pointer, you don't know how it will change your object.
05:04
If it's 10 lines of code that says do something, well, what parts of your object are changed? You don't really know at this point, right? But if you call a function like this, which takes a value instead of a pointer, you know that whatever happens, your original object is still intact. You still have it.
05:20
So the calling function has two copies. It has the original version and it has a function, well, a return value from the rename. And so this kind of gets the reaction quite often, like, okay, but now I'm using values everywhere and performance is gone. As a side note, that's just not the case in Go. Values are faster than pointers in many, many cases.
05:42
There's places where you want to use pointers, but pointers live on the heap, generally, not always. And that's garbage-collective, and that is slower. So if you use values, you're constantly on the stack, and stack operations are push and pop and they're quite fast.
06:04
So item-pointing functions. So functions should always produce the same results for the same input. So X goes in, Y goes out, it's always the same for every X, the same Y comes out. And you're going to violate this somewhere, because for some programs to be useful, you don't want this.
06:20
If you're programming a game, part of it is that you need some kind of randomized function. Something needs to happen randomly. Well, you don't know what the output will be because, well, it's random. So a simple item-pointing function is uppercase. Every time you get a lowercase input, the uppercase will be the same. So now if you have this function, which basically, it says set the update time.
06:44
So you create a row in a database somewhere, and you want to keep the timestamp of when this happens, and you do it in Go. You call time.now. If you call the function now, or two seconds later, all the result is going to be different. So you can't test this anymore.
07:00
A way around it is by basically re-upping this in another function that takes the time as a variable as well. So now they said, update time could be unit-tested, because you provide the time and you know what the output will be on the send time. Of course, the calling function will still call time.now, but you've basically managed to contain most of the logic in an item-pointing and a pure function, and the calling function
07:23
will need to do something messy. But the core functions can be tested. And so the idea would be that your whole program consists of these small testable functions, and then you chain them together in ways that you need them. So first-class functions, or higher-order functions, like people who come from other
07:43
languages like Java and stuff, they're starting to do this as well. So you have a slice of strings, and you take basically a predicate function that tells you if your string satisfies the predicate, yes or no, it returns a simple boolean. And if it satisfies the predicate, your output list will basically append it, and then you return the result of every element that matches your filter method.
08:05
So you can create a function called long-strings, which returns true if the string is longer than 10. You call this in the main function, and basically you've written out a higher-order function because you passed your long-strings function to the filter function that is using this.
08:20
If you would imagine that the left part does not exist, that would be declarative programming on the right. So the main function is saying filter by x, and it's not telling you how to filter. So the loop is abstracted away, basically. And if we continue with higher-order functions, we can also use closures.
08:42
And that is basically using a function inside a function, and the inner function is referencing the outer function variables. So, this aptly named function, closure, has a function inside of it called drop. But the drop function references a variable that's not from itself, right? So the drop references the string from the closure, s, and then basically drops x amount
09:06
of characters. And then we call the drop function with 5. That's the basic idea of a closure. You're going to basically use variables that are not from your own function, but from a parent function. So yeah, we can use this, and then we drop hello from hello world, so we just have world
09:22
left. And then this comes up, we can take one step further again, and then we can get into function querying. And function querying is kind of the same idea, but instead of using the function directly, you're going to return a function. And in the end, in functional languages, you tend to basically have every function takes
09:43
one parameter, and that function returns a new function requesting the following parameter. And that's how you chain all your functions together. Now, in actual functional languages, this is done basically behind the scenes, so you don't notice, right? You just write it with five parameters, but it will convert it in the background to five different functions taking one parameter.
10:03
In Go, we don't really have this luxury, so we have to write this ourselves, which makes it a bit more verbose. But you have a greet function that takes a prefix and a name, and it basically is going to print, for example, hello, and then your name. And then we create a prefix greet function, and this function only takes the prefix, but
10:23
it's going to return another function that will take the name as a parameter and return a string. And so inside the prefix greet function, we are returning another function, right? Taking the string, returning a string, and it's only when this inner function is going to be evaluated that we're going to call the greet function with the prefix and the name.
10:41
All right? So this allows us to basically start creating building blocks, and we're going to create intermediate functions and store them as variables. So then we can create a hola and a hello function, right? Hola has the prefix hola, and hello has the hello prefix, and they're sorting variables.
11:02
At this point, they're not really evaluated to a result yet. They're only evaluated to a partial result, to another function. And if you would call this function hola and hello, we false them, then we create two different hola and hello print lines. So basically, this allows you to compose functions in a different way, and to store
11:23
them somewhere as a variable in your struct or whatever. And there's actually a practical use case for doing this type of stuff, right? It's not just fun to play around with, but imagine that you have a function taking quite a few parameters. Typically, it's going to be a constructor of some kind, and in typical Go fashion, in
11:44
the top example, I've given them one letter variable names, right? Because in Go, everything tends to be concise. And then your IDE will try to help you, and it says to you, okay, I want nlpam, and it also gives you the type. Well, you don't really know what this is, right?
12:01
If you have a more Java mindset, you give them long names, or extremely long names if you want to. And then you have name, last name, foreign, age, and married. So the bottom example, you kind of know what it does, but you're relying on your IDE. So let's make such a constructor.
12:20
So we have a type server, and instead of listing the options in the server itself, we're creating an older struct with the options in it. So we're going to store the maximum number of connections, the transport type, and a timeout. And so we're also going to create a type alias, so a type definition, of server option. And server option is a function that takes options and returns new options.
12:43
This is just the same as if you would actually create a pointer to options and modify options in place, but as we've seen earlier, we're trying to avoid that kind of modifying stuff. So we're returning new options every time that function is called. And then we have the constructor, and it takes the variadic server option as input,
13:01
so you can pass as many as you want. And so now we would create the closure slash period function. So we have maximum number of connections, which is a function you can call with an integer. And that returns one of those functions that can modify options. So it returns a function of options to options.
13:21
And it's only when this function is evaluated that we're going to set the maximum number of connections equal to the integer that's coming in from the outer function. So this is the same closure type stuff that we saw earlier. Our inner function is referencing the outer function parameter, and we're returning the function instead of actually evaluating it. We can do the same for timeouts, or for transport layers, or for however many things we want.
13:46
And in our constructor, we're simply going to say, okay, I have all these server options, I'm going to loop over all of them, and I'm going to call the options function that we're getting. So maximum number of connections or timeouts function. And those are going to modify our options in place.
14:01
And at the end, we're actually going to create a server with the options that come in from the outside. So when we do this, so we have a new server now, we can just provide as many of the options as we need. We don't need to put, like, if we have a list of 10 items in the constructor, we know we only need to set two things, we just pass two of them,
14:20
and the order doesn't matter anymore, right? Just doesn't matter, they're all server option functions. And then you get the output. And okay, we didn't say the timeout, so the default zero value is zero. And actually, this is a really nice way of one having, like, optional parameters in your Go functions or constructors, but you could also provide default values if somebody doesn't enter something.
14:44
So in this example, if options had not been initialized as the empty struct, we could have put, let's say, timeout 3000 in here. And if a timeout function is not passed to the constructor, timeout will never be overridden. So that's a way to get basically default values in your functions and in your constructors,
15:05
which Go typically does not offer. So that's a practical use case of this kind of closure slash querying stuff. Another side of functional programming is that things need to be declarative, right? We want to basically just say, hey, this is our list of things that we want to do,
15:25
and the language will figure out how to do it for us, basically. It's doing it on the background. So remember our earlier example? It was basically the same stuff as this. We have a filter function, works on users this time, and it's basically gonna say, okay, if the user satisfies our predicate,
15:41
we're gonna add the user to the output and then return it. And then we could call this, and we could have, like, okay, we want to have all students who are above the age of 21, whatever, and we want to return a list of these students. Well, that's nice because it's declarative programming. And then you realize, hey, we're writing Go, right?
16:02
There is no generics, and there are no contracts as of yet. So that means that this filter function works for one type. I have users, but I also have cars. Okay, yeah, it doesn't really work for cars, so I create a function again for cars and for all my other types. That's gonna be quite hard to maintain. At some point, you're basically gonna realize, okay, I did make a bug in the filter function,
16:24
and I have to fix it in 20 places, which is kind of unfortunate. And you're also never gonna be entirely declarative, right? You're just hiding the loop, we're putting it somewhere in a filter function, or in a map or a reduce function, but we still have to implement the logic ourselves.
16:42
And there's actually quite a few libraries that aim to make Go more functional. So one of them is Gleam. And that's using Reflection to kind of introduce these filter functions for any type. So it's using Reflection to figure out what type it is, and then to apply the appropriate methods. And then there is Pi and Haskell.
17:02
And basically, so Pi works on slices, right? And they generate methods using the Go generate tools. So they generate filter methods and reduce methods for any type. And you run the code generator, you specify what type you want to run it for, any type in your system, and it will create a file with all those functions entered for you.
17:24
And so Elliot made that, it's a really nice thing, and I basically stole that idea, and I made Haskell, and I said, okay, but I only want Haskell functions that do this stuff. So you can create any type of function Haskell has for Go, and you can just generate it with some other useful functions.
17:42
So this is a library that enables functional programming in Go. It makes it a little bit easier. So you generate functions, it's declarative, it's pure, it's nil-safe, like most functional languages, they don't deal with the concept of nil in the same way. This library, if you pass it as nil, it will basically assume it's a default zero, and it will still work.
18:01
So Go also helps here, of course. And everything is immutable. So throughout the pipeline of filters, mapping, reducing, your data is never destroyed. You're always working on a copy, basically. And then you can use this kind of stuff in Go. So you say, I want a range of integers from minus ten to ten.
18:21
It's going to give you all the numbers, give me the absolute values of those numbers, filter them by an isEven function that you have defined somewhere, and then return the sum. So you don't have to write any loops, and this basically will work magically for you. At the moment, we can generate about, like, 40 to 50 Haskell functions for any type that you provide.
18:44
So you basically hit any struct, type T struct, and we can generate all these functions for you, and we check that, you know, if you're requesting string operations, that your struct is actually of a type string, or that we can work with it. So, in conclusion, yeah, functional programming is perfectly possible in Go.
19:02
Actually, it works well, but there is no syntactic sugar. So, in most languages, like in Java, they introduce the arrow lambda operator, which makes things a bit more readable. Well, you have lambdas in Go, right? But you don't have the syntax for them.
19:20
So that's kind of a downside, I guess. It makes the code more readable in a way, more easy to understand, but also very, very robust at some point. But it can be quite useful, depending on your use case. And, well, you should kind of use some libraries if you really want to be completely declarative, for example, because otherwise you're going to have a lot of fun maintaining a million functions for a million structs.
19:45
So, yeah, that's my takeaway. So, thank you. If you want to follow me, basically, that ad tag everywhere where I'm available, it's the same tag, so it's that name. And the library is also on GitHub, so you can play around with it.
20:00
So, thank you. So, we have some time for questions. So, are there any questions? Raise your hand. Okay. I'm going to go in priority of who's closer, so I'll go to you up there in a second. There was a hand here.
20:24
Hey, thanks for the talk. Very interesting. Maybe just a remark. There are certain types in Go, like even if you pass them by value, they're always passed by reference, like a map. So, if you pass a map, you can't rely on the fact that it's not been modified.
20:46
Yeah, yeah. Repeat the question first, and then answer. What is the concrete question about it, though? That some values are not passed by reference. I was just saying, you can't rely on the fact that something is passed by value, because some things are always passed by reference, like a map.
21:07
So, even though it looks like it's passed by value, it can be modified by the whole function. Yeah, yeah. Yeah, that's true. So, you can still modify them.
21:20
Do you have a way around the lack of the conditional operator? Because writing functional programs without that seems difficult to me, but it can be hard. So, did you hear the question? No, I can't. The question is, and please, people who are just leaving, try to be a little bit more quiet. So, the question is, how do you deal with the lack of ternary conditional operator in Go for functional programming?
21:48
Yeah, the fact that you don't have a ternary operator in Go. Well, I don't really miss it, to be honest. It's a matter of being used to it, I think. You probably come from Java or something, right?
22:02
Now you're used to using it, but in the end, it's just basically branching, so it doesn't really matter if you use the operator or not. Quick question, where is Maya? That's our next speaker, Maya Rasheesh. Okay, good. Any other questions? Okay, so thank you very much.
22:24
We'll start the next talk at ten.