Haskell Weekly


Type Applications

Listen on Apple Podcasts
Listen on Google Podcasts

You can also follow our feed. Listen to more episodes in the archives.

Come @ me bro! In this episode we review Zac Wood’s post about the type applications language extension.

Episode 37 was published on 2021-02-08.



>> Welcome to the Haskell Weekly podcast. This is a show about Haskell, a purely functional programming language. I'm your host, Taylor Fausak, the lead engineer at ITProTV, an ACI learning company. With me today is Cameron Gera, one of the engineers on my team. Thanks for joining me today, Cam.

>> Thanks for having me, Taylor. Pleasure as always I'm just really excited about today. I think we've got some great content ahead of us going to be talking a little bit about a language extension called type applications. So, uh, if you're familiar with Haskell, you've probably heard of type applications. If you're new to Haskell, well then you're in for a special treat today. We're gonna kind of walk through type applications. You know what? What's helpful about it? What's, you know, maybe confusing about it and really dive in to a post that Zach Wood, uh, wrote called Haskell's at symbol type applications. So, uh, to kick off, he I think, probably works with, uh, IHP, which is integrated Haskell platform. And he is trying to help explain, um, some of the extensions that are uh, kind of on by default when you're using the the IHP platform. You know, uh, framework, I believe, is really what it is at the end of the day. Um and so, yeah, tell us a little bit about type applications. Taylor.

>> Yeah. For context. Like you mentioned, the I H P is a framework for building Web applications with Haskell. And if you're familiar with Ruby on rails, I think it tries to be a lot like that. So it aims to be very developer friendly, and one of the things that you often want to do with Haskell is have a function that is polymorphic that could be used in a lot of different contexts. But then you need to pick which context you're using it in. So the example he gives is if you're like, querying for something from the database, that function should clearly allow you to query for many different types of things. Let's say like a user or an episode for us. That's one of our data models. Um, but you are gonna wanna pick which thing you're querying for, so that's where type applications can come in as a solution to that problem where you have this polymorphic function, you say I want to use it with this specific type.

>> Yeah, and you know, I think type applications is a great way to fix this one for us. We actually had this kind of similar problem where we had a query that could return all kinds of different things. And if we didn't use it in another context, it couldn't figure out. You know what type we were trying to query there. So we actually used to do just kind of type assertion at the variable Declaration. So that was you know, all right, we have user whose colon colon type user and then that's bound from the query that we were running. So you know, that's why another way to do what type application does

>> and funny note about that that requires a different language extension called scoped type variables, to be able to give a type signature when you bind a variable so either on the left side of one of the arrows in do notation or on the left side of unequal sign in a let statement. Otherwise you have to either give the type signature on some other line later in the function where use the thing or on the right side, you would give, like colon colon, like io user or the type of the whole expression where the thing comes from rather than just that piece you care about.

>> Yeah. True. True. True. Yeah, thank you for that correction. But yeah, that was also something we used to do before we even had scope type variables. So yeah, I think type applications cleaned up the code a little bit over scope type variables because, you know, we could kind of read the functional, revered writing and then say, Oh, hey, here's what type that thing needs to return Or here's that, really. Here's that type that needs to be filled into the polymorphic piece of this function.

>> Yeah, and one of the reasons I think that we didn't use type applications or scoped type variables for a while is that in general, I try to avoid using language extensions, but these ones, we had enough polymorphic functions where the utility was just undeniable. And instead of ending up writing like a find user function and find episode function and find this, find that you just have one find function and you pass the at type name for the next thing. And it feels like you get a lot of the same benefits as if you had a lot of different monomorphic functions,

>> right? Right. And you know, I mean, I know you. The habit is to shy away from a language extension before, you know, going to it until you see that use case. And I think for us, we really did find that use case, Uh, you know, and the nice thing about type applications as a language extension is it's pretty well known around the community. And, you know, it's even gonna be in the release of GHT 2021 which we've talked about in previous podcast. So you know, there's ah lot of community understanding on tap type applications and you know, it's something. There's a lot of, you know, tutorials on what it does and how it works. Um, you know, and this post is another opportunity to kind of show what power it has. Um, but, you know, we've been praising type applications a lot, but I know there is one thing that's kind of tricky when it comes to type applications. And that's what like when you put in that symbol in some type, you think it may be a variable to the function when it's really not, Uh,

>> it's gonna be really confusing, because, uh, the at sign you might think it could be used as an operator. So if you had some identify at sign some, some other identify that could be a normal operator call like, plus or you know, something like that. But it actually it can't be in Haskell because the at sign is reserved for this special thing when you bind variables or when you match on them. So, like if you want to match on a list and destructure the head and the tail using colon, but you wanna hang on to that overall list, you can do that with the at sign so you do the like whole term on the left and then at sign and then some destructuring pattern on the right. Um, so that's one way that type applications could be a little confusing is that this at sign is overloaded. But fortunately, uh, the like space around the operator is what disambiguates it. So if you have a space before the at sign, you know that it's gonna be a type application,

>> right? And then, yeah, I mean, and that's that's one thing. The other thing, too, is you know, the order of passing these type types through type applications matters because, you know, if there's multiple polymorphic variables in a function, you have to make sure you pass this in order or you probably get I mean, I imagine the compiler errors were pretty helpful, but it could still be pretty confusing, so

>> they might be pretty gnarly, especially if you have types of different kinds. So, like if one of your type arguments is meant to be some monad, so it takes another type as an argument, and you try to pass something like into that the kinds aren't going to match up. And I think most Haskell programmers probably don't run into that type of error very often, so it might be a little mystifying. Um, but yeah, the order of the argument is important, and usually the order that type variables appear in a type signature is OK like that's the order that you would want to supply them, but sometimes it's not right. You want to change the order. So how would you do that?

>> Yeah, so that's our good friend, the for all statement, right? Isn't that thing

>> yet? Another language extension?

>> Yeah. Which we don't tend to use it very often on our day today. So for me, that's a little fuzzy. So I'm gonna actually bounce that back to you and ask Sure about it

>> So the way that I like to think about for all is sort of a type level lambda. I think pretty much any Haskell programmer is gonna be comfortable writing a lambda with a bunch of variables and then an arrow. And for me for all is kind of the same thing on the type level, where you have the for all and then a bunch of type variables and then a period. And then you have the type signature after that. So it's a way of explicitly listing all the type variables you're going to use so that you can give them in a particular order.

>> Mhm. Okay, I knew it was a thing in Haskell, but I didn't really ever understand the reasoning for it, Um, way generally don't have super overly polymorphic functions where we have tell. Yeah, and where the order always matters or something along those lines. So for us, you know, we haven't really run into that too much personally.

>> Yeah, and it's kind of funny because the four all is actually always there. It's just implied that if you mention a type variable and you haven't quantified it, if you haven't given it by using one of these for all things, then ghd will just do that for you. And sometimes you'll see this on an error message where it'll print out a type signature and we'll have a four all there. But it's not actually in your source code, and you may wonder, where did that come from? Ghd Just put it in there for you. It's

>> magic, you know, feel like today we could even talked about G 9.0 point one being released to know we have Yeah, it's got that

>> got some fancy type stuff in there, but maybe we should do a whole whole separate episode on that one. There's a lot

>> of time. That's where you're interested in. It's out there, just, you know. Ah, friendly, Haskell reminder for those who want to dive on into that,

>> I mean, I just assume people are already reading the newsletter right there. Already subscribed to the Haskell weekly newsletter. I mean,

>> if you're not, I would definitely go do it now. It's just an email once a week. That's it. You don't get too much information. It's not like he's We're trying to sell you anything. Just trying to show you some Haskell love, man.

>> Yeah, but on the topic of type applications, is there anything else you wanted to cover here?

>> Uh, yeah. So, um, what else did I want to say? I wanted to say something, but now I'm forgetting, which is great. Uh, do you have anything you want to add? Because I'm blanking at the moment. It's Friday afternoon, everybody. I'm sorry.

>> Yeah. So we talked about some other ways that we did stuff that type application could do for us. So, like scope type variables, they're just manually giving a type signature. One other very common pattern that type applications replaces is passing around proxy arguments. So if you're not familiar, proxy is a data type that doesn't convey any information at the value level. All of its information is in the type level. So if you wanted to pass like the pass a type to a function, you might do that with a proxy. This is very common with the servant library. That's how it passes around all this, a p I stuff and lets you hold on to that as a value. Um, but with type applications, you don't really need to use proxy at all. You can replace that with an explicit type application. So that's nice. You just get rid of this concept altogether.

>> Yeah. I mean, for me, it's not. I mean, I'm all for type applications. I think it's been a great quality of life improvement for our code base. Um, and as a, you know, engineer helps me work faster and more effectively. Um, and also read code faster for the obviously. At first it was really weird. I remember the engineer who kind of like all about it. Cody, he was like, Hey, let's do this. And I read that PR and I was just like, Okay, I think I get it. But at that point You know,

>> it takes a minute to wrap your head around, but I think once you know what it does, there's not really a downside to this language extension. Some language extensions have a little bit of give and take. This one is just give it lets you express something that you weren't able to do before. Mhm.

>> Yeah, I think it's great. Um, is there anything else from the Post that you were, uh, you know, interested in or intrigued you? Because obviously, we talked a little bit about the post, but not a ton. He was Yeah. This is a great opportunity for us to have a good jumping off point. Um, but we didn't really dive too much into the actual post.

>> Um I mean, it was a great post. I really like reading. Zach has put out a handful of posts, and I like reading all of them. Um, this also gave me some good insight or like, kind of day in the life slice of life type stuff about I h p. I haven't used it myself, but I've seen a lot of people excited about it in the community, and I like what it's trying to do so. I like seeing posts like this that give me a taste of it.

>> Yeah, I know. I think he's gonna have to do some follow up post, too, because he's got two other language extension. He's use in the posts that he actually had comments in the post about which is really and he was very quick to react. So I'm sure if you have more questions. I think Zach's got you

>> Exactly.

>> Exactly.

>> Dude you can't escape the puns. I didn't mean to. Wow, just subconsciously punning all the time.

>> That's life.

>> Um, but yeah, Cam, like you mentioned, I think next week we'll have to talk about GH c nine. But I think that will do it for us this week. Unless you get anything else.

>> I don't think so. I mean, it's a great quick topic. Um, I mean, I think the only other thing I was thinking about talking about with examples of you, but you think we should talk about some examples like it's really good

>> he gives a couple in the post, right?

>> Right. He uses reading show is a great examples because, you know, read takes a string and returns today and show takes a name, returns a string. Uh, you can for the read function, you could assert it, or you could always write a wrapper function that gives it explicit types. But if you don't wanna do that, you can just type applications to say, Hey, I want this string 1234 or 1234 to become an int and you just do read at ent this string. So it'll transform it and obviously create a run time error if you can't do that. But you know it tries which, you know, I think that was a great example. Some more for us that we see on a more day to day basis is, you know, from from integral or realToFrac to transform one integral type to another integral type

>> these are Like the Swiss Army knife conversion functions, they're used for so many things, like from a machine sized integer into some specific sized incised, unsigned integer, or like from a database type into a normal type or from a time. Or, you know, just all kinds of things have surprising instances where you can convert it with from integral on, and a lot of times I encourage people to write monomorphic rapper functions like specifically from this type of that type. But with type applications, you can say from integral at this type at that type and then you don't need to write that extra function. You just kind of define it in line as it were.

>> Yeah, which, you know, pros and cons. But if you're kind of familiar with the extension, I think it's a easy thing to grok. You know, obviously, if you're new to a code base and you've never seen type application before, yeah, you would be like wait a second. Why is this that way? We should just made it a separate function.

>> But that actually reminds me, um, one thing we didn't touch on yet that I think we should is that with type applications, sometimes a function will have, Let's say, to type variables, and you want to only supply the second one. So you're like whatever the first one is. If you want to infer it, that's fine. But the second one needs to be int, and you can do that with type applications, where, instead of saying at some type name you say at Underscore and that tells G h c go ahead and do what you were going to do here. Anyway, Just pretend I'm not here and I'll do the next thing,

>> right? And that's you know where that order matters. Because if you try to just apply the second one it would infer it as the first one, right?

>> like with from integral. If if in your context, the first type variable is already picked for you. So you don't wanna specify it again, You would say from integral at underscore at int. And then that would say, I'm converting into an integer.

>> Right? But that's Yeah, super nice little thing that type applications gave us so much. Appreciate it. Thank you, programmers. Who created this?

>> Great. Yeah, that's a good one. Uh, so, yeah, I feel like we've we've said we're almost done for a couple times here, but I think we're actually almost done now.

>> Yeah, I think so. I mean, you know, I wanted to give us some good content. I was like, What? We need examples, So yeah, Glad we got that opportunity. Sorry, but yeah, the emotional roller coaster off our listeners.

>> Are we over? Are we not? We were over. Thank you. Okay. No, no, there's no more. Uh, yeah. Thank you for listening to the high school weekly podcast. I have been your host, Taylor Fausak. And with me today was Cameron Gera. If you want to find out more about Haskell Weekly, you can go to our website haskellweekly.news And if you enjoyed the show, please rate and review us on apple podcasts. And if you have any feedback for us, tweet us at Haskell Weekly

>> And Haskell Weekly is brought to you by ITProTV and A C I learning company and our employer. They would like to offer you 30% off your subscription by using promo code HASKELLWEEKLY30 at check out. And that about does it for us, huh? Taylor?

>> Sure does.

>> Well, thanks for joining us this week on Haskell Weekly, and we'll see you next week.

>> Bye.