 Okay, we have two minutes till the talk starts. I'll wait for those two minutes, but in those two minutes, just a quick poll. How many of you have used Haskell in production? How many of you have done this? Two people, awesome. Three, wow, four. How many of you have dabbled or struggled with Haskell? Awesome, absolutely right crowd, preaching to the choirs. Just again, filling in the time here. What was the most difficult or frustrating part about Haskell? Entire type system. That is why we didn't use Scala documentation. Yeah, go ahead. Anything else? So monads didn't come up, surprisingly. Shall we start? Cool, so guys, thanks for coming. This is a bit of a journey talk, right? We put 34,000 lines of Haskell into production. And this is about the good, bad, and ugly removed from all the funky type theory, category theory, all of that stuff. Very real life account of what you would face if you're using Haskell for extremely real world apps. In our case, it was a web app, very large web app, right? So firstly, what is the source of all of this Gyan? Why am I standing here today in front of you, trying to be this Gyan guru, right? We've A, people say that Haskell doesn't have side effects because no one really runs it. Actually, we've run that code in production, right? So we've got a firsthand experience. While writing this app, we touched almost every single part of a web app. Database, ORM, logging, instrumentation, HTML, emails, SMTP, send grade, whatever you can think of. I mean, we picked this problem, this module that we built in Haskell, we picked it because of the fact that it touched upon everything, right? This was our first Haskell module, and we wanted to discover everything in the first go itself, right? We used it for everything except the front end, right? In the front end, we are using TypeScript and AngularJS, you know? It's sort of an F word in the Haskell community, TypeScript and AngularJS, but we used that and we had our good reasons for that, right? We didn't use ProScript, we didn't use GHCJS. So that's the actual CLock output, right? That's 39, 3,910 lines of Haskell code, right? You can see about 5,000 lines of TypeScript, and surprisingly, JSON is like some 50,000, which was some stupid package.huge package.json file. So we have not written anything in JSON, but it was all auto-generated, right? Well, core question, why Haskell? Why did we put this bull behind our bumps, right? And we had, I had this side conversation in the morning as well, why Haskell? And I have like one simple response to that. Good question, right? I'll just give you a context like why, why Haskell? So what are we building, right? What is our business? We are building a travel commerce platform, right? It's enabling travel companies with e-commerce technology, right? So for example, to give you a quick analogy, you've got a Shopify, Magento, BigCommerce, WooCommerce. Those are products that are enabling physical product commerce companies. We have a complete platform enabling travel companies. They can get their own website, they can get a mobile responsive website, booking systems, payment gateway integrations, mobile app, distribution systems, agent logins, everything, right? Up till now we've been building it in Rails and AngularJS version one. This started out in 2012, 2013, so it's an old app, right? And this is what it looks like. This is the C Lock output of our app. Ruby is about 155,000 lines of code. JavaScript is about 130,000 lines of code. ERB is about 54,000 lines of code. Now this is very important, right? We are powering websites. A lot of our stuff is in HTML-ish kind of templates, right? This was important for us. We needed type-safe templatings as well, right? SQL, look at the amount of SQL over there. That is actually our schema.sql file getting counted by Slock. So heavily into RDA members usage, right? So this should give you some context of where we were coming from. What were the kind of things that we would be touching in Haskell, right? Very typical RDBMS kind of, very typical Rails app, right? And you know, we are proud of this legacy product, right? It's what one is called as legacy software, right? We are proud of it. It existed, and it is still existing long enough, providing enough value, providing enough revenue to become a legacy. It is a legacy of its own, right? So we are proud of it. We're not dissing Rails in any way. We're not dissing AngularJS one in any way, right? It brought us to where we are, right? But it also brought with us some problems, which is why we reached out for something better. We picked up Haskell. So what were those problems with those Rails and JS code, right? As our code base grew, you know, our understanding of the market changed, we wanted to refactor code data models. We were unable to refactor these code data models with any degree of confidence, even though we have a 60 to 70% test coverage, right? Always afraid, yeah, this is working. Let it be. We don't know what this is going to break, right? Even though we have tests, it never gives you that confidence. Unable to remove dead code. Five years, four years of building, building, building, building, building. There's a lot of craft that builds up. But you're always afraid of removing that because chal rai, working, let it work. No point fiddling around with it. But it adds up. It adds up to your technical debt over the years. And then the third thing is you are unable to review code effectively. And I'll give you some quick examples of each one of these taken straight from our gate repo. This might not be readable, so I'll just read this out. Unable to review code effectively. Some controller code, where is Adderate trips coming from? What does it have? Someone was reviewing this code. Yeah, what am I reviewing? What is in this variable? What is happening? Is it even the right thing? Because that variable is being set at some other place. It's an instance variable. It has something. And the reviewer doesn't even know. He's reviewing it and doesn't even know whether this is the right thing. Second example, so this started happening as we start, when we started, Postgres didn't have very good JSON support. And over the years, JSON support and Postgres just became absolutely brilliant. So for large parts of our schema, which was variable in nature, we started using JSON in the SQL, in a PGA itself. And the problem with JSON is once you start using it, specifically in a dynamic language, first when you're writing it, you know everything. You're holding the shape of the JSON in your head. But two months later, when you're reviewing the code or someone is making a change, they don't know what the shape of that JSON is. You don't know whether the transformation you're doing on that, is it correct or not, right? And that started happening. This is what, how are confidence boosters being stored in some, that's a JSON column? What's the shape of the JSON? The reviewer doesn't know. What the hell is going on over here? Third thing, unable to remove dead code. Is this really required? Someone figured out while reviewing, is this line required anymore? No, no, it is not required. We can remove it. But, it was not actually removed. Because no one was confident. Let it be, it's running. So, these are the core problems. There were more as well, but I will not tell them right now because I have to also give a Ruby Conf talk. What will I say there, right? So, this our journey started a year ago. I was here last year in Functional Conf. Got really excited about the power of functional programming, yeah. And then I went back and started, after going through all of that Functional Applicative Monad nonsense, whatever, started trying to build something. And after about two to three months of same struggling, struggling, struggling, I wrote this blog post. That's probably the title of the blog post. Building web apps in Haskell is harder than it ought to be, right? This is how I felt a year ago. You can read it, probably some of you might be able to relate to it over there. You know, it's been a year. It's taken us a year to get Haskell into production, right, with its ups and downs, right? Now I'll explain to you after we've put it into production, what were the good things that we found in Haskell, right? So we had a problem with our Rails and JS code. We went with the most anal, statically typed language out there, right? We had a big discussion internally, why Haskell, why not Scala, et cetera, et cetera. And we were like, hey, we are making a move to type functional programming. If we pick Scala, we might end up writing Java and Scala. We will end up going through this huge technology transformation without even adopting the paradigm that we wanted to adopt in the first place, right? And we might not even know it, right? Sure, you can write pure FP code in Scala. You can write pure FP code in Scala, in JavaScript. You can write pure FP code in TypeScript also, and I can go to the gym every day also, and I can eat healthy food also, but it doesn't happen, right? You need that anal Haskell compiler to bloody force you to do things in a certain way, right? So that is what we did. Now first, I'll talk about the good parts, yeah? That's a, I mean, there are like talks and talks about that, pros and cons of that. I'll take that question at the end. Dynamic, otherwise I won't be able to, I have lots of slides to cover. So the core question was, why static, why not dynamic? So Haskell, first the good parts, right? So the, I mean, one of our problems earlier was refactoring, and I can attest to the fact that refactoring in Haskell is brilliant. It's absolutely brilliant, right? I've made a video of a core bug that I had fixed in one of our database access layers. It's a 45 minute bug fixing session, spread it up to five minutes with text annotations, fix the bug, ran it in, and it was correct the first time it ran after the bug fixing. You should look it up, it's on YouTube. So refactoring in Haskell is just amazing. While you're writing the code itself, you've written the first draft, then you realize that the data structure is not correct, I need to change something, you change it, compiler will tell you five different places where you need to go and fix the errors. You will never do that in Ruby. You will never do that because once it works and you change the code data structure, then you will probably take the equal amount of time to getting the code in a working shape again, right? And if you've not written tests, then it's even more time, right? So refactoring is just superb, full marks for that. Next good thing, type safe JSON, right? So the way we handle JSON in Ruby and Rails is very different from the way you do in Haskell and I'm assuming that it's different from the way you do in any statically typed language. What, I can't talk about other statically typed languages but at least in Haskell and TypeScript, you're sort of forced upfront to define the shape of your JSON, right? And that helps, that helps big time, that helps in refactoring, that helps in the next psychopath who's reading your code to understand what the hell you've written, right? It helps in many ways. And the good thing about Haskell is that it's got this superpower called JSON. It's a JSON library and it is absolutely brilliant. All the boilerplate code that you need to write JavaScript codecs, right? Coding and decoding, it just auto-generates. It auto-derives all of that. On the other hand, we are also using TypeScript in the front end, we are right now forced to write JSON codecs by hand, right? It doesn't have that auto-deriving function. So this part is absolutely brilliant although you have that extra layer of defining data structures for your JSON shapes but beyond that, there is very little boilerplate to write the codec. The HTTP web app or whatever you want to call it, that's server, it's called, why is the web application interface? From people from Python and Rails backgrounds, that's the equivalent of WSGI and RAC. And warp is the implementation of that, right? Snowman, Michael Snowman has written that. It's got a pretty good performance. It starts one lightweight thread per incoming request. It's those green threads that Rahul was talking about in the earlier talk. It's got a solid concurrency. And if I understand this correctly, I don't claim to understand everything correctly about Haskell right now. It does it without an event-driven callback model which is I think what Node.js uses, right? So without having to deal with all of that callback hell, you get that solid concurrency, right? Now, for the bad parts, right? So these five, there are a lot of small, small, small, tiny, tiny nitpicks that we face but these are the five big problems that we faced firsthand, right? I'll talk about each one of those and we'll try to give examples of why I'm saying this. The library ecosystem, the lack of established best practices, editor tooling, records, and lack of an ORM library, right? So let's start with the first one. It's a big claim out there. The library ecosystem is effectively, and effectively is the keyword, effectively broken in Haskell. It's effectively broken, right? And don't get me wrong. Lack of libraries is not a problem in Haskell. The opposite is true. There are just way too many libraries. It's like new library hanging on every tree. There's just so many libraries that you, you, I mean, for a simple task, say I have a follow-up slide, testing. It has 243 libraries in the package. Hackage is the package repository. 243 libraries for testing. So what? How many tests will you do? So fragmentation is a huge problem. Non-standardization of libraries is a huge problem. There is no single repository of blessed libraries. I think Python has that. There is a Go has that, where there is a set of core libraries for the core operations which is maintained, documented, kept up-to-date effectively. That just doesn't exist in Haskell, right? No, Stacket's also a different problem, but over there also you have like testing has, it might not have 243 libraries, it'll have 143 libraries, but sure, problem still remains, right? So in order to put it in different terms, Hinduism has this concept of trinity. Brahma, Vishnu, Mahesh. Brahma is the creator. Vishnu is the maintainer who writes tests, who writes documentation, and who makes sure that stuff is running. And Mahesh is the destroyer. He's the God of death, Shiva, right? The problem with the language, the problem with the library ecosystem is that there is no concept of Mahesh. Every single idea that someone has ever thought of is still there lying on hackage. It may work, it may not work, it may be the worst idea, and you might land up on it while you're doing your research, and you might end up using it and wasting your time. So this concept of death, of culling out ideas which didn't work, is very important. It just doesn't exist, and it heaps and heaps of craft on hackage, right? Now I'll give you examples, why am I saying this? This graph is from, and not only me, there was a state of Haskell Ecosystems away, just released a couple of days ago. One of the questions, I can easily compare Haskell libraries to select the best one, right? 44% responded negatively, 44%, almost half, right? Neutral ones might have said neutral out of just being nice. It might be even more, right? So it's a real problem, it's a real problem. Now I'll give you some examples, first-hand anecdotes. First-hand anecdotes, right? Emails, simple thing. Emails, you know, you have tons of email libraries. Haskell also has them, SES, Postmark, Mandrel, Royce, MTP. Whatever you can think of, it's there. Someone or someone has written it. It might be maintained, might not be maintained, but you'll get some code. If it's not working very well for you, you can fix it and get started. You'll not hit a dead end. You'll not hit a dead end, essentially. But here's what our experience was. In the first phase of our app, we just wanted to deal with the raw SMTP, right? We weren't dealing with SES or anything. Like the normal persona, Haskell, Hackage, Control, F, SMTP, we found SMTP mail. This is the library, let's use it. And we used it, we used it in development. In development, we were using this tool called MailTrap, which doesn't actually deliver your mail. It traps it and shows it to you on a UI. It worked fabulously over there. Then we pushed it on production, and everything broke. Why? Because SMTP mail does not support TLS. And in production, we were using TLS. The library called SMTP mail, which is the most generic name anyone would reach out for, when they're using it. It does not support TLS. And we were like, oh, shit, okay, what next? Then there's, we found this library called HaskellNet. It supports to have POP, IMAP, it says that it has support for POP, IMAP and SMTP. Okay, let us use that. Does not support TLS. There's a sister library called HaskellNet SSL, which supports SMTP. At which point, you were like, why? Why is this split into two libraries? What purpose is being gained by splitting something as core as SMTP into a TLS and non-TLS? And it has, it surely the maintainer had to do more work to do that, right? We had to do more work because our SMTP was configurable. So we wanted to call the same function to deliver mail, whether it was TLS or non-TLS. We had to write a wrapper function on top of it, which looked at the mail config and then was calling either the TLS version of the function or the non-TLS version of the function. I have no clue what great idea or purpose this serves. Another example, oh, sorry. This is a plug. So there is a HaskellSendGridLibrary, so first we tried with raw SMTP, then we moved to SendGrid as well. HaskellSendGrid exists, someone's already written it, but it's not complete. We ended up writing our own SendGridLibrary. It is absolutely fully featured. We almost implemented every single endpoint, the API library for that in Haskell. And if anyone is interested in helping us open-source it, which would include, which would mean sort of cleaning it up, writing some documentation and then putting it up on Hackage, please ping us, right? We can even offer a bounty to do this work, right? Next thing, testing, as I was saying, I was so sorry, I was wrong, not 243. Yeah, please. No, Ruby, you use ActionMailer, it works. It works. You, no, it will not be there. I can guarantee you in Ruby it will not be there because the first tutorial you will read will say how to use ActionMailer to send mails and ActionMailer will solve the broadest set of use cases. It is not true for every language. It is true for only those kind of, and actually it is true for some other kind of languages. There's one more ecosystem where we found this to be true, which is the reason why we didn't adopt it is React, same nonsense of micro libraries which you have to keep constantly gluing together, solving no purpose, right? So it is true, it is true for some, it is true for not some, but there is clearly, so you can't go like, there's a middle path somewhere, right, where building increasingly smaller units of libraries solves no purpose, and there is a point where increasingly larger bloated libraries also solves no purpose, there's a middle path somewhere. We need to find that middle path and adopt that. So testing, I was wrong, I said 243 earlier, there are 287 packages, right? There are four families of libraries for property testing, QuickCheck, SmallCheck, Hedgehog, GenValidity, solving the same problem, and this is not one library, these are families of libraries. There'll be a QuickCheck-something, QuickCheck-something, I mean, what, how different are they really, that they need four libraries? Which one to use, right? Sorry, okay, so, but I have another follow-up slide for that as well, just wait. So unfortunately, we wasted a lot of time in testing, figuring out what to work, none of these libraries we found solves for what is a pretty common use case in web apps is running tests in parallel in isolated database transactions. Out of 287, we obviously didn't look at 287, but whatever, beyond some point, we ran out of R&D time, right? So testing, another example, DB libraries, persistent, Hedgehog, HS SQL, Postgre Simple, Opal Eye, okay, my SQL Simple is a bad example because it's a connector, Haskell DB, and they're probably five more. Oh, sorry, Hedgehog is a test, yeah, Groundhog, sorry. That's Groundhog, that's Groundhog. Which one to use? Everyone takes a slightly different take on the problem, is equally incomplete, is equally undocumented, and you're going to struggle with each one of those, right? So again, if I had to give a recommendation, if I had to give a recommendation, the most complete and well-documented library out there is persistent, but it has its own set of problems, which I won't go into right now. All right, so second point, which I want to talk about, lack of established best practices, right? For a lot of people, I think for almost everyone in this room, barring a few, the concept of programming in Haskell is a completely new paradigm, right? We've not done this before, right? And there is absolutely no repository of established best practice. I mean, there are threads and threads on Reddit or on IRC where someone comes, hey, what's the term in Java they use? Design patterns? What are the design patterns for Haskell? And people will go buzzerk. Haskell does not need design patterns. Design patterns are an outdated Oop concept. I mean, doesn't make sense. It's essentially established set of best practices of doing common tasks that you're going to see. To the point where the community keeps arguing about core stuff itself, and if you as a newbie are trying to approach it, you get absolutely confused. What is going on? I'll have some examples over there. Stack versus Cabal. So Stack and Cabal are probably approximately equivalent to Cargo. And what's the Cargo from Rust? It's a build tool. And what's the go-to-go? Any go-programmers here? There's some standard tool. In those ecosystems, these things are absolutely standardized. No one thinks twice about how to use Cargo. It works well. It's also a broad set of use cases. Haskell community is divided right in the center on build tooling. Stack versus Cabal. Every single thread on Reddit, if it is long enough, will devolve into a side thread about Stack versus Cabal, right? MTL versus free Monads. Even as if Monads were not enough, now you have free Monads and MTL and some complexity around that, which 10 people will understand. So error handling. Is template Haskell good or bad? Constant arguments about that and you as a beginner will get absolutely confused. Examples, installing Haskell, right? Surprisingly, I think Haskell would be the only language which has two homepages. Haskell.org and Haskell-lang.org. Because of this deep rift in the community, as to what should be the default method of installation for Haskell. Currently, there are four different ways, right? Haskell.org tells you all the four ways. Haskell-lang.org, which is maintained by this company called FP Complete, which does open source, a lot of their work has written some solid tools around Haskell. They have written this tool called Stack and if I had to give a recommendation, just use Stack. Just use Stack, ignore everything else. It is tried and tested and it works brilliantly, but no, there is still no consensus around that. If you are, and this has happened to us. Some newbie joiner told me I just downloaded, today your first day job is install Haskell and he's gone and installed some Godforsaken build which is no longer working and then I delete everything, just install Stack first. So it's bizarre, it's bizarre. So there was a huge, huge, huge, huge debate on the Haskell Cafe mailing list about what should the homepage look like. Download getting started, Haskell, what should it look like? Lots of pros and cons, but I have a better take to it. It should be this, right? It doesn't matter where you download Haskell from, you are going to need shitloads of RAM. It, like the developer environment, like drinks RAM, right? I have a follow-up slide on this as well. So yeah, error handling, you won't believe it. There are, someone else has written this blog post. There are eight different ways of handling errors in Haskell, eight. And each library uses its own way, right? And I have an anecdote for this also. There is this error handling library called Accept T, right? And there is this extremely sexy DSL called Servant for writing web APIs, nice, beautiful, type-safe APIs it gives you. It uses Accept T as its base, right? And we thought Accept T, huh? This is for some error handling. Let us start using it. So we started using Accept T throughout our own code as well, right? And we were in for a big surprise, because look at this piece of code. Run Accept T, this is just dummy code over here. You are throwing an exception divided by zero, right? And then run Accept T is supposed to catch that exception and give you either a left value or a right value. Left value indicates some error happened and right value indicates whatever you were doing happened without any errors. What would you expect this thing to print? Exception caught? Nothing. The error does not even get caught. The error, and when we realized this, it was like a big WTF moment for us. Because except there are two kinds of exceptions now in, it's just, it's hard to explain. But then we finally started from this library, control.monite.catch.try. And this thankfully does the right thing, right? So if anyone of you is ever doing that, do not use Accept T, it has its use cases. But if you do not know better, those are probably not what you want. Error reporting. Now, it just seems that everyone thinks that Haskell does not throw errors. That's just not the case, right? It throws different kinds of errors. It changes the way you write your programs, and it moves the errors from deep within your program to the boundaries of your program, right? Remember, whenever data is JSON parsing, right? Whenever data is coming into your system as JSON, you are parsing a JSON, and the correct practices are you define your type, right? So if the incoming JSON does not confirm to the type that you're expecting, you will get a parse error at the boundary of your program. Once it parses properly, then whatever you're doing in your Haskell program, almost you will never get an exception, right? Unless you're using some partial function, raising errors manually, or you have a non-exhaustive pattern match, right? So it pushes all your errors to the boundaries of your programs, where data is coming into your Haskell pipeline, or Haskell R, whatever, kind of hard to explain. But errors do exist, because at every boundary, you can have a parse error. Your ingesting data, it might not confirm to the type that you're expecting the error to be in. So errors exist in Haskell, but they are in a different form. Once your data parses, there's, you can, and you're not doing something terribly stupid, you will not get exceptions in your Haskell code, right? So there's this hype, if it compiles, if it runs, that is not true. The reality is, if it was compiling earlier, and you refactor it, and it still compiles, then there is a very high probability that you have not broken anything. That's the truth, right? Now, we did our first build, wrote everything, blah, blah, blah, blah. I was working on the Haskell backend, someone else was working on the TypeScript front end, and I said, yeah, this backend is ready, install stack on your machine, do a stack build, and run the binary after that. Stack build, stack exec, whatever, run the binary, and it'll run. It was running on my machine, and he did it. This is the error he got. This is the actual error that happened. Open file does not exist. No such file or directory. Which file? Where is the file name? It's like Haskell is asking for the adharkard. No adharkard, no error message through you. After a little bit of debugging, it turned out FastLogger was trying to open a log file in a non-existent directory in the config. I had hard-coded my home directory as a log file path. It obviously wasn't existing on his thing, but this is the error message you get. Another example. These are all real-life cases that we've faced. This happens a lot. We're dealing with a lot of database. We end up using PostgreSQL simple. PostgreSQL simple. If it's trying to load a row, again, remember, all the errors have been transferred to the boundary of your program. It's trying to parse data. You are actually parsing data from the DB, right? It is expecting a certain type. The data didn't confirm to that type. Unexpected, this is the actual error that it gives. In fact, it is worse than this because it is not even pretty printed. I have sort of formatted it properly. Unexpected null, SQL type, some table OID, some Askel type, error message blank. Figure out which row, what were you parsing? At least give me the row that you were parsing. What SQL was being run, right? I mean, if you've written a web app which is doing SQL query one SQL, blah, blah, blah, blah, your types might be out of sync with your schema, right? You might end up with a parser which you need to fix. It's an easy fix. You will almost always hit it during testing or development, very easy fix. But absolutely painful to debug. Stack trace, no concept of stack trace. But you do have the OID. Yeah, and then you have to spend 20 minutes understanding where to get the, how to convert this OID to whatever data type it was. And in fact, it already knows what that OID is. It's there in the underlying Postgres library. It's there. It's just not surfacing that. Another case. This is after we hooked up everything together. We even put instrumentation in place. We put error break, error logging and all. And I was stress testing the system. Load testing, okay? How much load can this take? This is the screenshot of our error break. Error message. Invalid argument, bad file descriptor. Figure out. It wasted a hell of a lot of time. And just because I had done stress testing earlier with other frameworks, I just occurred to me that what could be going wrong? Otherwise, there was absolutely no way for anyone to debug this. It was actually that the DB pool size was larger than what the database could handle. And the database was killing its child, whatever, processes because it was running out of memory. But Haskell was not reporting where this error is coming from, which file descriptor, what library, what stack, nothing. If you end up ever getting this in production, God save you from debugging this. So there's hope. There's light at the end of the tunnel. This, as I mentioned, Haskell doesn't have call stacks that for all practical purposes is true, for all practical purposes. But this is a situation that can probably be fixed. There is this thing called has call stack in Haskell. You just put that annotation in your function and it starts recording the call stacks. And when you throw an error, it actually prints the stack trace. This has been there for some time now. We actually went in and did a performance measurement of that. It turns out that it has doing, maintaining that call stack has an almost zero perf penalty in IO heavy code. In computation heavy code, we benchmarked it with Fibonacci, which is like one tight loop after the other. It has, in that kind of a code, it has about a 1.2x perf penalty, right? But that's not probably where you need your call stacks. You need call stacks when you're doing a lot of IO. And in that, the perf penalties, you couldn't even, I couldn't even measure it properly. It was coming across as noise. Unfortunately, while this exists, it's not being used by IO heavy libraries. Almost none of the libraries are still using this. But this is a situation that can easily be fixed. You can, I mean, we've started raising some PRs. You can start raising some PRs. There's also a discussion on GHC track, which is talking about inferring this automatically, that you don't even have to go and annotate it. The compiler itself figures out where it has to maintain the stack trace. So this is a call for contribution. Testing. Now, I was here last year. I heard John Hughes talk on QuickCheck testing. I was super pumped, boss. Silver bullet, Milia. I will go and ready random test my shit out of my code when I go back. And I actually, we actually drank the QuickCheck cooling. The problem with QuickCheck is, the problem with QuickCheck is that beyond, lack of a better word, beyond textbook examples of, seeing if a pure data structure is being transformed properly or anything, there is absolutely no documentation around how to use QuickCheck for a real world, large scale, stateful app. Almost all the examples of QuickCheck that I've seen are these pure examples. Let's do a circular queue. Let's do, how do you use QuickCheck with a database? How do you generate a complex object graph where you have a company, where you have multiple users, where you have bookings? And after that, you want to run an API and see whether it is running properly. There's absolutely nothing around that. So my belief, we spent two months on trying to figure this out. You go to QuickCheck, GitHub repo. There is a huge thread that we started about, please give us a tutorial about how to use QuickCheck to test complicated stateful systems. That resulted in QuickCheck test machine, which even after multiple tries, we could not understand how to use that. That's just so complicated types. So after that, I have a belief that only two people know how to use QuickCheck properly. John Yokes and God. I could not figure it out. And QuickCheck, QuickTest, whatever. We finally had to give this up. We wasted so much of time on this that we had to ship without tests. Our code has shipped without tests right now because of this, right? And if we would have just done normal aspect, R-spec, whatever, testing, we would have gotten something at least, but this just wasted so much of time. Again, lack of established best practices. So much of hype around QuickCheck, but where's the real-world example? Editor tooling. The sad truth is that nothing works as well as it should. Nothing. We've tried everything apart from WIM-based plugins. We don't use WIM. We've tried everything on Emacs, Interode, Dante, Haskell mode. We've tried Atom. We've tried VS Code, Haskelo, Haskely, HIE. Nothing works and finally understood why. The underlying GHCI. GHCI is the interpreted mode of Haskell, right? Which you would end up almost any tool will end up using to write their backend, right? That leaks memory and is slow, right? I'll, remember I said the customers who also downloaded Haskell, downloaded so much of RAM. This is an actual screenshot. GHCI and Interode running, four GB and four GB each. I've had cases where each was using 88 GB. So, editor tooling is a problem. But, sorry, just before I move on, this is an important caveat about memory usage. We have not seen Haskell leaking memory in production. This memory leak is only about GHCI, which is used in development, right? Please don't go with the takeaway that Haskell leaks memory crap. It's GHCI, I think it is even an open bug in the GHCI issue tracker, right? So, just we also, when we found this out, we, so our production Rails apps is using about, production Rails app is using about 360 MB of RSS. Production Rails Haskell app is using about 78. So, here's a recommendation for editor tooling. Anyone who's starting off, this is the first thing that they hit. Either use SpaceMax plus Interode for small projects when you're just starting out, it'll work. It's only when the project size increases that these cracks start appearing, right? The other thing that you can use is VS Code and HIE, right? If when your project size starts becoming bigger, your best friend is use GHCI in a new terminal window and use these three commands. Set F object code, load and reload. So, after you make any change, just go and hit colon R. It'll show you your error message over there. That works perfectly fine, right? This set F object code, anecdotally we found out that once you set this, your memory leaks reduced drastically in GHCI, right? Shout out to HIE. Again, call for contribution. HIE is a Haskell IDE engine. It is the LSP, Language Server Protocol Implementation for Haskell. It is not completely ready yet. But if you can, if it works for you, please use it. Please try to make it work for you. There's only one way to make this better. Use this. You find bugs reported. If you can, please contribute. This HIE is the way forward for Haskell IDE tooling. Next issue, records, right? This is running out of time, but we are the last talk. Shall I rush it up or, yeah? Okay. So, records in Haskell are just beyond horrible, right? There's, I think, Matt Parsons, yeah. Matt Parsons has a talk where he's claimed Haskell does not even have records, right? I'll explain to you why. So, let's take this standard, even the talk before, this pure script talk I was talking about. You basically, you have your database schema. You map, you want to map it to a clean record and you want to use it or variations of it throughout your app, right? It makes life simpler. So, please excuse this user, user, user prefixes over here. That's the only, because in Haskell, realistically, there are some extensions which allow you to do that, but realistically today, you cannot have two records share the same field name. So, you have to prefix the records with something, right? So, you have this, but the thing is, you can't use this in your create user endpoint. Why? Because when you're creating a user, you do not want the front end to send the ID. You do not want the user to send the created ID. You do not want the user to send the updated ID. Either you make all of them maybes, but if you make them maybes, then you're dealing with a different problem because when you read it back from the database, like you make them maybes just because, while when you're calling create user, they can be nothing because the front end will not send it. But if you make them maybes, then every time you read it from the database, you'll have to keep unwrapping the just, just constructor, you'll have to keep pattern matching and in the nothing case, where the nothing case doesn't exist because when you're reading this from the DB, these three columns will always exist. So, you are being unable to model your types as per your domain, right? And there's no easy way to create a sub record from this even, right? You can't say, I want a record, wherein this is the base record, but this new record type has only these three fields, name, age, and email. You can't do that in Haskell, right? But we, this was a critical problem for us. This was, our entire app was needed to work around this. So we built our own template Haskell library to solve this problem. It's called record splicer, right? Wherein you can say you define your master record and you say, I want a new record called new user, which has only these three fields, right? And this generates the template Haskell code for you and it gives you a lens-based functions to sort of take a new user and make a user object out of it, take a user and make a new user object out of it. It gives you a new data type called new user delta, which captures all the fields apart from these three fields so that you can do some sort of record level arithmetic on that, right? There is another library which exists to solve this problem. We found that slightly more complicated for your use case. It's called metamorphosis. Exact same problem that person faced. Pretty much the same solution, but just a slightly more complex way of doing things. Compared this with TypeScript, has this sorted, absolutely sorted. You define your interface, read only, read only, read only. We sort of have a linter which forces us to use immutable stuff, so if you don't put read only, it'll give you a linterer. And then you make a typesafe sub record. You have a type new user, pick out of user name, email, and age. Works. Making a typesafe super record. Suppose you have an admin which extends user, read only permission, array of permissions. It has all of these, plus a permission. Sorted in TypeScript, I think it is sorted in PureScript as well. I think it is sorted in F-sharp as well. I think, I'm not sure. It is. So big, big problem. Template Haskell, we found template Haskell to be the only pragmatic way to solve this. We spent, yeah. So that's exactly what this is doing. It's taking one persistent user and deriving an unperturbed user from it. Yeah, but this is exactly what this is doing. This is exactly what this is doing. I think this is the last thing now. Lack of ORM, right? So generally, not only in Haskell, I think in other FP languages, I've seen this felt, this unsubstantiated hate towards ORMs, right? And I have tried to, you know, once I've met that, I tried to understand what it is, what it is like to build a large scale app without an ORM layer of some sort. Some people have used Hibernate. They absolutely hate it. Hibernate might be a bad example. Active record is just brilliant. I'm hearing good things about Ecto as well from Phoenix or Alexa, right? I've gone to Stack Overflow, Reddit, IRC, tried to finally give me, is there any real life mid to large scale project, RDBMS backed, which is built without using an ORM? None of the answers are convincing because almost all of them have an ad hoc implementation of an ORM. Because when you're accessing the database, there's a lot of boilerplate that you have to write. Find by a PK, find by certain matching columns, find a row, but only give me these certain columns. Either you use an existing ORM or you end up writing your own, right? Associations, no DB library deals with associations in Askel. Except, and now I want to kick myself after I discovered these two new discoveries, Beam and PostgreSQL ORM. In that big mess of craft on Hackage, these two libraries were hidden and I didn't find them. But we are going to sort of check these out in some time. Beam is coming, it's a new kid on the block. It's coming out fabulously well. Brilliant documentation, right? PostgreSQL ORM is pretty rails-ish like. Solves almost all the right problems, but I have a thread on Reddit where I've asked this question, what is the best way to deal with associations in Askel? No one mentioned PostgreSQL ORM. And once I found it, I realized it is solving the exact same problems that I wanted to get solved. Right? Again, just finding the right libraries is such a big problem. Validations, no DB library deals with validations. Right? The rails and probably Django also and probably Ecto also or Elixir also that that. Just five more slides. One more talk. All right. So, can I get five minutes for questions? Yeah. So, yeah, I've run out of time, but covered pretty much all the five points that I wanted to cover. But, yeah, this is what we built. We also built a job admin, which we are going to open source shortly, a delete job clone, essentially. Yeah, we wanted to give away some t-shirts to whoever gives a PR to Askel documentation. So anyone who wants to fix this situation with respect to documentation, small fix, any fix that you have felt while learning Askel, just raise a PR, tweet us the link, we'll give you this t-shirt. And yeah, we have a Haskel bounty program. All of these issues that we keep coming across, which can be solved in two to three short weeks, we launch bounties for them. A couple of them have already been picked up and we are doing our bit to make the ecosystem better. Have about five minutes for questions. Absolutely brilliant. There's only one way to fix this is to come in and contribute. Okay, that is not completely true because the reason you're using acceptees because it's a monad transformer, you have an underlying monad, which in all probabilities IO, in our case we were using servant. The underlying monad was IO, we are building a web app and it can throw exceptions. The database, so I'm sure, I mean, if you can have better ways to throwing errors, but it was just an example of saying someone wrote code where somewhere in the library, a file was trying to be opened, file was not found, and while emitting the error message, they didn't put the file name there. Anything else at all?