Episode: 2808 Title: HPR2808: Haskell function types Source: https://hub.hackerpublicradio.org/ccdn.php?filename=/eps/hpr2808/hpr2808.mp3 Transcribed: 2025-10-19 17:05:18 --- This is an HPR episode 2008 and 2008 entitled Haskell Function Types. It is hosted by Tuku Toroto and is about 24 minutes long and carrying a clean flag. The summary is Tuku Toroto gives over new Function Types in Haskell. This episode of HPR is brought to you by an honesthost.com. Get 15% discount on all shared hosting with the offer code HPR15. That's HPR15. Better web hosting that's honest and fair at An Honesthost.com. Hello, this is Tuku Tuku Toroto and you are listening to the HAKER POTTER CRADY. Today's episode will be about Function Signatures in Haskell. Again, this is something that I should have done ages ago. But once again, better late than never. So Haskell is a static language, which means that when it's compiled, the compiler checks the program for type errors. So, it makes sure that you are not mixing for example strings and numbers. And for that, it needs types. Type is a definition of what kind of values can be used. Typical ones are in the string, text, bool and so on. There's quite a lot of them in the Haskell. And of course, new ones can be defined by the programmer. And type names always start with the upper case level. So then Haskell does type inference, meaning that if you choose so, you can omit type declarations completely. You don't have to tell Haskell that type of this function is something. You just can omit those completely and Haskell will try and figure out what kind of types would work for that. But it's helpful for the programmers if you are writing type declarations out. Because then they can more easily check from the source code. But what kind of type some function takes. And also, it instructs Haskell compiler that this function should have these types. Otherwise, if you don't specify that, Haskell will try to figure out what's the type signature. Sometimes it works, usually it works. But at the point where you make a mistake, then the Haskell compiler will try to figure out the type and it might give you a compiler error somewhere completely different place. So you should write type declarations at the top level of your program, meaning that all your functions should have types in them. And the type declaration is written as a name of the function. And colon, colon, and then types that it takes. So, for example, the simple, I won't be talking that much about the code in this, this, this or that will be talking more about the signatures. And what are the things you can do with those and what they tell you about the functions. So the simple, simple case is that you could have a function called add, that takes an index, two integrals, and produces an integer. And this is, as a type declaration, that declaration is written on top of the function, function definition. So that declaration for this type of function will be add, colon, colon, integer, arrow, integer, arrow, integer. So it takes two integrals and it produces value of inverter. And you don't have to have specific types. Sometimes you can have, you can mark that anything calls here. For example, if you had one another made up example, you would have a function that has signature of choose, colon, colon, a, arrow, a, arrow, bullen, arrow, a, and a is a lower case here. So, it, that means that this function choose takes two values of type a, which can be anything, a bullen, and it will produce a value of a. And a is always the same type in this function. It can be anything, but it's always the same in, like you cannot mix, you're going to put different types in the a. And we don't know anything about the a. It can be absolutely anything, which means that we also can do much inside in the true, of choose function with the a. Basically, we add even two a's and bullen, and based on that bullen, we are returning in the line of those a's. So, if we want to add a bit more functionality in that case, in a case where we don't have a specific type, we can use something called a bullen morphism. So, if you remember the at function, that we mentioned a couple of minutes ago, it only works, it only works within that case. You cannot use it with the float. You would, of course, define add f, that works with the float, and then some add i that works with the imaginary numbers and so on. But it would be kind of silly, because then the programmer would have to remember that here I'm adding in that, so I have to use add and here I'm using float, so I have to use add f. So, we can define that by using a type constraint. That means that we are writing it, the signatures would be add colon colon, open current, uppercase num, over this a, closed current, sad arrow, a, arrow a, arrow a. So, this means that a is constrained to be an instance of num, and add function takes two a's and produces a. And num in this case is a by-class that defines some functions that every instance of num implements. So, you know that if you are given, if your function takes an a, you don't know, you don't know exactly what it is. But you know that because it's a num, it has some functions that you can use on it. And num defines plus, minus, multiply, multiplication, negate, ups, sigma, and from index. So, it's some very, very basic number type of arithmetic. And I'm going to talk about type classes, another time that really need a concept, but grabbing them into this episode will make this a bit too long, I think. So, if you have a by-constraint, that is an uppercase num a, sad arrow, means that everything, everything in the type, the type signature a is always a num. And you can have, of course, multiple type constraints. You can say that num could be num. You could say that a has multiple constraints, or you could have that something that a is constrained to something and b is constrained to something. And we are not using b in this case, but you can have multiple, multiple type parameters. So, that is how you can use pretty generalized functions. And the cool thing is that our num, our ad function will work for every num instance. So, if somebody comes with a new data type, that implements, that has an in-the-instance num, our function will work with that, no problem. If, okay, if that num instance is written properly. Okay, then you can have parameterized functions, like, for example, you could have a function called first, that has a signature, signature of first, colon colon, open bracket a closed bracket, arrow may be a. And this is a function then that takes a list of a, that's the a inside of brackets. And a, again, can be anything. And it returns may be a. I talk about maybe in an early episode, with this type that is used to signify, value that might not be present. So, a can be anything, again. So, you are given a list of a's and it returns may be an a. And may be an a can be nothing or just a. So, in case you are given a list of, that is, at least one element, this will presume to, presumably, return the first element of that list. That's just a. But if you are given an empty list, this wouldn't crash, but it would just return nothing. And if you want to use functions, again, this is something really basic that I should have brought up, this would have told ages ago. But when you are calling or applying a function, you don't need parenthesis. So, our at function can be called, just try to simply add one to. So, you don't need parenthesis. And this means, of course, that if you are changing functions, like you are, for example, you are calling one, you want to add more things. You can write it as a, add one dollar sign, add two, three. And the dollar sign is there for the application order. It instructs that, first you evaluate the right side, add two and three, and then you use that value in the evaluation of the left side, at one. So, you are basically, you could use parenthesis, you could write that in the, you know, add one, open parenthesis, add two, three, close parenthesis, and the result will be identical. Six in both cases. And it's a, again, a bit of a personal preference, which one? You two use, I usually opt for using, a lot of times. Sometimes, it is easier to parenthesis. But it's good to know that there are options. And there's a one thing that really took some time to get used for me, is that you don't use parenthesis at all, in a function call. You just write the function name, and then you write the parameters. There's a, another need, concept in Haskell is the part of the application. So, you can, you can call function with less parameters, that it expects, and it won't cause, it won't cause a runtime error. So, example, you could define. If we have defined that, add function, we could define another function. Say, add lots, equal at thousand. So, we are calling add, our add function, that it's set two parameters, the single parameter. What happens here, is that, it will automatically, generate us a, when you're calling add, with the set a single parameter, instead of runtime error, we are going to get a new function back. And this function works, exactly like, add function, with the, that has been, that has the first parameter, thousand. So, now that we have, our add lots, that equals to add thousand, we can use our add lots to, just call add lots five. And this is equal to calling, add thousand five. So, you can easily, easily create new functions. And, for example, sometimes I like to use this, in a way that, I have some, general function, that takes our, some sort of configuration, and then some, some sort of parameters, that instructs, that uses, and then, that parameter, configuration results, to some value. For example, if I have a, if I had a game, where you have a building, and I wanted to know, a cost of a building, I would have a, general function, that would have a, list of those building costs, and then it would take a, name of the building, and result would be the cost of the building, it would call, through the list, find the building, return the cost of the building. I can use, this partial application, to create a, function, that takes the, general configuration, that is used in everywhere in the program, and afterwards, I have to test that, configuration, into the function name, or, because I have that, new function, that has been, sort of configured, to use, that, list of buildings. So I can just call, that, new function, with, name of the building, and it will give me, the cost of the building. Completely made up, explanation, but, that's, that's a one, one way, I could use this. And this, this is called, carrying, so, Wikipedia says that, in mathematics, and computer science, carrying is the technique, of translating, the evaluation, of a function, that takes multiple arguments, into evaluating, a sequence of functions, each, with a single argument. There was some, mathematics, mathematics and code, I think, I think he was called, Howard Curry, who, came up with this, and, a bit earlier, some, another mathematics and code, search single, came up with, the same idea. So, if, if the, Curry wouldn't have, I don't know why, it's the, why, why, this is, named as, Curry, Howard Curry, because, if it were, named after him, we would, we wouldn't be called, talking about, thinking, which, I think it would, yeah, funny, but, in any case, you can do part of your application, and, the, all of the parameters matter, as they are evaluated, from left to right. So, some, sometimes, it makes sense, to, dig the, parameter, order, a bit more, then, then, you, then, what I, was used to, according without, another images, because, if you have to, always, fly all the parameters, you don't, the order of the parameters doesn't really matter, but, here, because you don't have to, fly all the parameters, then, order start mattering a bit more. For example, if I had a, function plant broadcast, that take a list of broadcasts, auto and produces a list of portcasts. I could if that function is called find portcasts. So find portcast has a signature of list of portcasts text list of portcasts. So I could define a new function called search that equals to find portcast, loaded portcasts, loaded portcasts, a list of portcasts that came from some way, not in part of some way. And then I could use that function as a my portcast equals search to turbo. And that would search from that original list of portcasts, all the portcasts are made by me. But if the parameter order would be other way around in the find portcast function, it would first take the name and then the list of the portcasts, this method group verb. Okay, and then functions as types. Functions themselves are also types. So and they can be used as values. So you can you have if you have a function, it can be done a function and if you have a function and also a function can take another function as a parameter. One very common function that task is a filter. And it has a signature of filter, colon colon, open turns, R a, arrow, rule, close turns, arrow, open bracket, A, close bracket, arrow, open bracket. I'm sorry, let me do that again. So filter has a signature of filter, colon colon, open turns, A, arrow, rule, close turns, arrow, open bracket, A, close bracket, arrow, open bracket, A, close bracket. So filter is a function that takes two parameters. First parameter is a function that has type of A, arrow, rule. And the second parameter is a list of A. And it will return a list of A. So you need to have a two functions that have a maxing A. A can be anything, but it has to be same on both of those functions in the filter and in and in that parameter that filter breaks. And what the filter does is that it will apply that function that was given to every element in the list. And for every element that evaluates to, for every element that the function evaluates to true, it will return as a new list. So it has a name implies it is used to filter a list with a given function. It will find all the functions, all the elements in the list that will return true when given to that parameter function. You can use that for example, writing a filter, hot, open bracket, one dot dot, then close bracket. This will produce a list of hot numbers in a range of one to ten. And the last thing I'm going to call it today is the anonymous functions. Sometimes you need function to pass in as a parameter, like in the previous case, but you don't want to define and give it a name. What be that you are going to use it only once, or it is so short that it does not really need a name, it doesn't run having a name. So photos cases we have a long dot, or anonymous functions. And the syntax is for one function. It is a, for example, if you are going to use in the filtering example previously, we will want to filter all the hot numbers that are picket and file. We could write it in a following way. Filter, open parent, slash x, arrow, hot, x, ampersand, ampersand, x created then five, close parent, open bracket, one dot dot, then close bracket. So here we are, here we will be defining an anonymous function that takes a single parameter, x, and checks that the x is hot, and that x is created and five. If those both are true, then the value of the function is true, and this we use to filter all the hot numbers created and five in a range from one to ten. And you don't have to specify types here, has can will figure them out for you. So the syntax is a slash parameters, arrow and body, and you can have multiple parameters in the function. There are just separated by the spaces. And often you have to wrap this thing, you know, parenthesis when you're passing it in so that the hospital compiler knows where the anonymous function starts and where they pay it ends. Okay, that's it for today. If you have any questions or comments or corrections, easy way of catching me is either email or at the fediverse where I'm to tour at masterton.so also, or you can even, even better you can make your own hack-a-pattered radio episode. Okay, thank you for listening. Bye-bye. You've been listening to hack-a-pattered radio at hack-a-pattered radio.org. We are a community podcast network that releases shows every weekday Monday through Friday. Today's show, like all our shows, was contributed by an HPR listener like yourself. If you ever thought of recording a podcast, then click on our contributing to find out how easy it really is. Hack-a-pattered radio was founded by the Digital Dove Pound and the Infonomicon Computer Club, and is part of the binary revolution at binrev.com. If you have comments on today's show, please email the host directly, leave a comment on the website or record a follow-up episode yourself. Unless otherwise status, today's show is released on the creative comments, distribution, share a light, 3.0 license.