CoRecursive: Coding Stories - Tech Talk: Purescript And Avocados with Justin Woo

Episode Date: April 4, 2018

Tech Talks are in-depth technical discussions. Purescript is a functional programming language that compiles to javascript.  It is a strict haskell dialect that can run anywhere that javascript does.... Justin Woo is a self described Purescript evangelist and enthusiast. We talk about purescript vs elm and working with expressive type systems.  Justin also had some great metaphors about phantom types and masking tape as well as avacados and testing.   Contact Justin: twitter github website Show notes: Purescript purescript training videos     My team at Tenable is hiring.  We are a distributed team of scala developers working on static analysis of docker containers (among other things). We are a team of smart people, working fairly autonomously on interesting problems.  We are one of many teams working on interesting problems at Tenable.  I think its a great place to work. I am in Peterborough, in Canada, and our team has people working in the US, Ireland and the UK as well. Here is the job posting:  https://www.linkedin.com/jobs/view/586241797/ Tell them Adam sent you, or you can email me directly at work abell at tenable.com or use this link to apply.

Transcript
Discussion (0)
Starting point is 00:00:00 For me, it's like truly a language that actually becomes better and better the more you use it. Whereas I don't think I can say the same about most I've used. Today, I talked to Justin Wu about PureScript. It's a strict Haskell dialect that compiles to JavaScript. We also talked about cutting avocados. Before we get into the interview, if you are a Scala developer, my team at Tenable is hiring. We are a distributed team working on static analysis of Docker containers for security vulnerabilities. Tenable itself is a great place to work, and we are looking for smart people to take on interesting projects
Starting point is 00:00:45 i will put a link with some details in the show notes justin mu you are a bit of an evangelist for pure script so welcome to the show i keep hearing about pure script so so what is it at a high level? Yeah, it's a functional programming node that's similar to Haskell and compels to JavaScript. I guess that's like the short bit. And the most distinguishing feature of it from Haskell is that it supports real polymorphism firsthand.
Starting point is 00:01:26 So yeah, in that way, it's like an easier Haskell to use. What brought you to PureScript? For a long time, I'd been doing only JavaScript and before that, I did
Starting point is 00:01:42 some corporate work. So I wanted to get into some kind of language that could teach me more and wouldn't just break if I wrote something. So I wanted some compiler to actually be able to give me useful information for writing programs. Is PureScript strictly Haskell for the client side, or could you use it with Node.js? Yeah, I mean, right now at work, I use it with Node.js, and I've used it with Node.js for, well, the whole time fair to say that PureScript is a general programming.
Starting point is 00:02:26 It would just like gives you easy FFI and into JavaScript. And it really lets you firsthand fiddle with the runtime representation of your data. Is PureScript just, you know, Haskell in JS? I mean, it's definitely easier. And the IDE tooling being built into the compiler is a big factor for why it's easier. But being able to work with anonymous records also just makes it much more easier to write than to like learn about like weird Haskell product type records and such so I don't know I mean I guess you could say that it
Starting point is 00:03:13 basically is Haskell and JS but I find that I find that the row type features just make everything a lot easier to use. So let's, let's dig into that. So, um, like record types, um, record types are kind of hard to do in Haskell, but, but something that's very easy, I guess, in, in like an OO world, right. Where you can have like a, a person and an employee and so on and so forth. So our records, how does PureScript handle records? In Haskell, if you want to work with records, you have to learn about how you have these, you have to have a data constructor associated with it. And the position of the arguments actually matters.
Starting point is 00:03:58 And then about how the labels generate these weird static functions outside of, I mean, like within the module. Whereas in PureScript, when you work with records, the fields can be in any order because it's row type. Like these fields are completely, the ordering of the fields is completely ignored and only the structure coming together and unifying as the important part.
Starting point is 00:04:25 So I think this is like the big thing that makes it feel more familiar to work with. I was coming from a dynamic background, so I wanted to work with basically hash maps that were heterogeneous and guaranteed for equality, that makes any any kind of sense yeah i think the i think bringing up hash maps kind of kind of you know put some meat on it well maybe we could go through an example um so i was mentioning like a you know like let's say you have a a person record and a and an employee record and they they you know they they both let's say you have a, a, a person record and a, and an employee record and they, they, you know, they, they both have like first name, last name, but employee has like a start date. So, so what does the, what does the row polymorphism give us here? Yeah. In this case,
Starting point is 00:05:21 you could model it such that any functions where you work with these... Sorry, the employee was the subtype of person, right? The structural subtype. Or was it the other way around? Well, do they need to be subtypes? Well, not necessarily so you could write a function that says okay I can take any record which has this given label
Starting point is 00:05:50 name with the type string and then it's extensible by like any other rows so you could like define functions that work like this and it's like kind of what you really wish you had when you work with normally type languages
Starting point is 00:06:06 where you say like oh i wish i could work with like functions that could say i just need a class that has like two stream in it instead of like this whole weird uh object hierarchy and and then say if you're working with a person record and then you have an employee record that's a super type of that structurally. So it has all the same fields, but then more. where there is some kind of union between the fields that are in person and some kind of open rows that can be defined by the concrete context. And this is like the type you want to actually work with.
Starting point is 00:06:59 I don't know if this actually makes sense when I put in words, but with like a crayon diagram, it makes more sense. The part I understand is that I could have this employee, which has first name, last name, person, which has first name, last name, and I can have a function that operates on anything that has a first name and last name, right? And that's the structural typing?
Starting point is 00:07:25 Yeah. So it's like if you had functions that work on crayon boxes, you could define them as you need any kind of crayon box that has blue in it, or you work with a crayon box where blue and red are in it, and there's a complement of colors that could be like an empty set of crayons, and all kinds of fun stuff you could come up with.
Starting point is 00:07:52 You can work with these properly statically typed records, but you can use them sort of like they were like a hash map. Yeah. For me, it's like what I've always dreamed about being able to use hash maps as i don't know like um like if you define react component properties it's like there's like a there's like a total set of like maybe a hundred different attributes you could find but i want to be able to pass in like records that I have like for them. But then after I passed that,
Starting point is 00:08:28 that in, I want to like have that concrete type information still so that I can do like other things. Like say if I wanted to, I don't know, for whatever reason, if I want to take the same type and and use it to decode JSON or get the keys out or compare between two different records, like the fields that have the same type, I can do that.
Starting point is 00:08:55 And if you have just some nullable types, then it's just a whole bunch of runtime checking, and you can actually implement a lot of these as being concretely typed. You have a whole bunch of maybe something everywhere in your record if you can't do this. So how is PureScript implemented?
Starting point is 00:09:22 Like the language? I'm the wrong person to ask about that right because I'm like a enthusiastic consumer I mean it's written in Haskell and it's a fairly small code base as far as I know I think there's only like a couple
Starting point is 00:09:38 dozen thousand lines it's not like a very big project and I mean as a result there's a lot of features from Haskell it doesn't have. And then some things that Haskell has that it's kind of changed in some ways. So what's the debug story look like in PureScript? Like if you, I'm assuming that at some point, you know, you have to debug something that it emits, like the JavaScript that that is created at the other side of the compiler. Yeah. And for me, I run into two scenarios where
Starting point is 00:10:16 I do that, right? Like, so if I write my own JavaScript bindings, like FFI, then in those cases, there are some like surprising bugs that come up just because of my own mistakes bindings, like F5, then in those cases, there are some, like, surprising bugs that come up just because of my own mistakes. And it's, like, things that aren't, like, completely typed. Even though, like, I could, like, treat it as foreign and parse the output and everything, there are some times
Starting point is 00:10:37 where I don't care about the validation, or I want to, like, validate it with tests instead of, like, having the validation cost every time I run it. So in those cases, I debug it by building the JavaScript output and running it through some debugger, like VS Code debug mode or something. And then because PeerScript is a one-to-one output, well, not strictly one-to-one because there is like possibility for code synthesis
Starting point is 00:11:06 through type classes and such, but because the output is predictable and it's basically like one-to-n, like you can actually follow the execution and you have to basically the same names and everything and the same functional structures everywhere. So debugging just the raw output is quite easy, whereas something with the runtime is going to make a lot of assumptions and it's going to give you this garbled machine code that only specifically runs with internal representations of data. So that's like the main first case. And also
Starting point is 00:11:47 personally, I don't quite use it very much, but there are options for generating source maps for these. So you can source map some of these debug errors back into your original PureScript source code with the
Starting point is 00:12:03 source maps, like if you're using Chrome DevTools. But yeah, like I said, some people do it, but I personally don't have very much experience in it, and I personally don't really have to debug JavaScript very much. And the second case that the debugging comes up for me often, or not too often, but sometimes, is if I'm writing a parser and I accidentally, like, blow the stack or I accidentally write some kind of, like, loop or something. Because PureScript is strictly about evaluating language, like, you just have these situations sometimes and you have to figure out, okay, where did I mess up and
Starting point is 00:12:45 do I need Monad Rec or something to prevent it from blowing the stack or what kind of horrible design did I come up with that needs to be fixed or something. So in this case, it's like I debug the output. But even then, it's...
Starting point is 00:13:02 I don't know. I mean, it's like, how often do you write parsers is the question. I mean, like, sure, some people have the philosophical question where, like, anytime you do anything with strings to something, that's parsing. But, I mean, like, if you're writing, like, code using, like, PeerScript string parsers, which is, like, a, it's kind of like Parsec for PeerScript.
Starting point is 00:13:28 If you're writing like Parsec combinators and such, it's like, yeah, you're going to run into that more often than others, but usually you only need to write a parser like a couple of times a month or something at most. I feel like I, but I don't know.
Starting point is 00:13:43 Hmm. So, yeah, you mentioned you mentioned uh stack overflows so is that does that just happen with recursion or you know is there is there some sort of abstraction difficulties with going from uh you know pure script to to js at when evaluated I mean no it's it's literally the same thing as what you would write in JavaScript. So if you just if you have too many function calls, then you're going to have sag or flow.
Starting point is 00:14:14 Especially if it's recursive. It's more like a problem of like about three, four months after I started learning PeerScript, I started using Haskell. And so sometimes you write things in Haskell,
Starting point is 00:14:30 and it's just magically, like, performant and nice because of lazy evaluation. So, like, coming from lazy evaluation parsing to, like, eager parsing is, like, well, going from, like, pretty smooth experience to kind of a rough one so those are like where i run to some stumbling blocks pure script is is strictly evaluated do i mean is that a good decision are you are you happy that it's strictly evaluated it sounds like you're missing laziness
Starting point is 00:15:00 i mean it makes sense if you want to have minimal representation runtime, right? And, like, there is no runtime for PureScript. So, like, that's an actually, like, incredibly nice thing about it. Like, it enables me to debug the output, like, directly. It allows me to, like, write code that, like, uses the JavaScript representations more, like, directly without, like, worrying about some of the details. Overall, it's quite nice. I mean, and there are plenty of people who will disavow laziness and talk about how it's confusing to debug and whatnot.
Starting point is 00:15:38 But at least for the main casual software writers' point of view, because I haven't used like haskell in actual work for me like it seems like the lazy evaluation stuff would be a lot nicer to have um like in my experience i i'm no haskell expert but i did find lazy evaluation to be, to be a source of confusing behavior sometimes with like space leaks and, and things. So, I mean, I can see the, I can see the beauty of a strict evaluation in a lot of cases, especially when you're, you're trying to do a one-to-one mapping down to JavaScript. I think that, I think that makes a lot of sense.
Starting point is 00:16:25 Yeah. But like, I, I miss it every time I have like these alternative cases and it's like both branches get evaluated because of like my own laziness. So I'd like to find workarounds for that. It's, I don't know. It's, it's not very costly in the end. It's, it's usually quite cheap, but it still feels like a bummer. So you mentioned FFI. So calling out to JavaScript, how does that work?
Starting point is 00:16:59 It's fairly transparent in that you write some JavaScript in normal ES5, and you just use the normal common JS stuff, so you say exports.whatever equals, and you give it a function. The only, like, caveat, if you can even call it that, is that PureScript effects, like the IO in PureScript, is a temp function in a representation. So if you want to have a function that has an effect, then you shouldn't run the effect eagerly,
Starting point is 00:17:33 but return a function that does the effect. But once you do that, then it's just quite normal that you're kind of responsible for yourself in getting type signatures correct. But if you don't trust yourself about that, then if you just get the arity correct, then you can use this foreign type and you can bar set the output as you need.
Starting point is 00:17:58 I see. So you mentioned the effects system. So Haskell has an IO monad and everything that side effects is wrapped in that. How does PureScript work? Yeah, I mean, it's about the same thing. There's this F type. And right now the F type is a parameterized type
Starting point is 00:18:21 with a row of your effects and then your actual type coming last. But, like, so the idea is that it's a final type where you can actually, like, declare, like, what kind of effects happen when you run a function. In practice, this hasn't been so, like, nice, and it's been more of a nuisance for many. In the next version
Starting point is 00:18:48 of PureScript, we're planning on going into this effect type that gets rid of this row type parameter. At least this part will be gone, but then overall PureScript will still be a purely functional language, just like Haskell.
Starting point is 00:19:07 Well, minus like other bits well i mean it has it has pure right in the name so it's putting down some uh some stakes right there so i had a previous interview with uh with john to goes and he was saying that um like when he was talking about about scala i think but he was saying that like rather than having everything, you know, like a function that returns an int with some side effect rather than it being just like IO int, that he prefers to like kind of separate this a lot more so that he might have, you know, a type class for random and it might return like random int
Starting point is 00:19:43 and kind of splitting the the io up into a bunch of different types of of side effects that could happen um is is this what the what the f the eff uh effect is about no no i mean so f is just uh well it's just a phenomenon type. So it doesn't do anything, like, different. Like, there's no, mechanically, there's nothing different. And there's just this type parameter that you use for some, like, simple, like, checking in compile time. But the approach for using, like, type classes and other things to like actually like slice down like what specific operations happens that that kind of mtl approach does exist in pure script and there's like
Starting point is 00:20:34 various like practitioners of this approach and there's like some helper libraries for that but the main f itself is just this phantom type and you can coerce the f type row into anything else like unsafely coerce it and it's just representationally it's the same thing like there's no difference i thought that the f type let you kind of subdivide the the types of side effects you were doing is so that's wrong um it well it basically lets you write like write notes in sharpie on a masking tape and tape it over something but it doesn't actually mean anything right so you can unsave the course and just like tear off all the tape and it's still the same thing whereas like the really cool thing about like mtl based approaches is that like you can have
Starting point is 00:21:26 the compiler synthesize the code for you that like gives you the code for running effects okay yeah i understand i guess if you're doing unsafe course though can't i mean you could do anything right yeah well well unsafe course F will just coerce the effect rows, and then the type of the actual item inside will still be checked correctly. But this
Starting point is 00:21:55 will go away with 0.12 where we'll get rid of the F type, and it'll be named effect, and there'll be no more of this weird row type parameter. But if you do want to look for something that like hooks up like the correct effect handlers with these effect rows, like say if you wanted to have console effects that you could send like messages to like what kind of like console effects you want to have console effects that you could send messages to what kind
Starting point is 00:22:25 of console effects you want to have when instructions for what should happen and then have the interpreter for actually realizing these messages into actual actions. You might look into PeerScript Run which is a library that
Starting point is 00:22:41 Nate Fabian made for doing this kind of stuff. Like this actual extensible algebraic effects in PureScript. But personally, I don't have any experience with that. I usually only use MTL approaches or I use Fremont to do whatever. Or often I just write normal programs in F. And it's like writing a program with only I-O, but I have specific functions that are pure that I test or I care about,
Starting point is 00:23:13 and the rest I'm just doing some plumbing to make it all work. Yeah, that makes sense. So you're just trying to limit the the amount of your of your code that actually does side effects and you can kind of test the the rest because it's all pure and and generally i'm also kind of like a believer in like actually clicking through things and doing integration tests so i would rather have a headlash browser open my application and click around than to have like unit tests. Not that like unit tests aren't like, not that they aren't good. It's just that oftentimes I end up writing these meaningless tautologies
Starting point is 00:23:57 in the test. And I really need to like write either property tests for these or have some kind of proof-based system that generates routines and can correctly do these or model it more into types. Usually I need these three things in addition to integration tests. So you think unit tests are a horrible idea?
Starting point is 00:24:22 I got that correct, right? I don't know. I mean, because the three things I mentioned do go into unit tests, right? It's just that most of the time, if I have to write unit tests in JavaScript, usually they're a poor man's type checking. Instead of being able to check an entire range of values, an entire domain of outputs, I can only sample it with, like, some horrible mini samples. It's like if I were making knives and I could only test by cutting avocado, it's like, how do I know if this can actually cut, like, some kind of pumpkin or something?
Starting point is 00:25:07 If that makes any sense i don't know so in this case the avocado and pumpkin are two different types um or two different like values right okay avocado is like something that's like trivial to cut oh i see pumpkin depending on what it is it could be like quite hard like japanese kapucha isn't like the easiest thing to cut in the world yeah the avocado pit though i don't know if you're gonna get through that wow no i i see what you're saying yeah um so um that's funny that example kind of threw me off so uh pure script uh does have some sort of property-based testing library um i think does it yeah yeah there are some various ones um i mean, I usually write code at home, so I'm not using them,
Starting point is 00:26:06 but I probably will end up using them for work at some point. I'm also like a big believer in like writing something really naive at first. And then whenever I notice problems or I run into some problems, if they're like things that should never happen, then I want to model that in my types instead of writing some kind of test. So the problem with like, for me, for writing tests is that it's like,
Starting point is 00:26:34 it's more work to write them than to write more correct types. So I guess one case would be like, say I have this vid tracker thing where I keep track of, like, what shows I've been watching. And, like, I mark, like, shows as being watched and whatever. And at first I read this, like, really, like, naively. It just, like, each handler was, like, whatever effect type. And then it just returned a string.
Starting point is 00:27:04 And then that string i would send through like send back as a response but then the problem became like uh even though if i only returned a string then it's like it could be any string so i would often when i changing code around make the mistake of like applying the same handler to multiple locations or just, like, returning the wrong type altogether. And then my frontend would, like, make these requests and think that, like, they were supposed to be this other type. And then it would parse these. And then I would get these code batches that would always fail with the wrong, like, type.
Starting point is 00:27:40 They would always say, like, oh, we failed to, like, decode this because, like, it had the wrong type and whatever. And then vice versa, the same thing would happen with my front-end code where I would call for resources but then use the wrong URL. So it's like, of course, I, like, I've been gradually upgrading my vid tracker program so that I have, like, more type level evidence about what kind of things I'm looking for and whatnot. So, like, over time, I went from a model that said, okay, I'm just going to request at this string URL, and then I'm going to pretend the output should be this
Starting point is 00:28:26 and then post to that and then handle a successful error. And then moving all the way to, okay, I have a type level, where at the type level I know what request it needs, what response it needs, and I know statically what the URL string will be. So just like I like moving more stuff to the type level because it's like it's both documentation about like what i want and i could write more supplementary documentation on top of that whereas if i write it as a test then it's like something i have to update and i have to like know more about in the future if this long story makes any sense
Starting point is 00:29:06 no it does right uh i forget who who said this but uh like make invalid states unrepresentable right so it's like if it doesn't matter what language you're using if everything just like takes inputs of strings and returns inputs of strings right like but exactly and i mean do you know about my twitter meme where i had the guy bicycling and he like puts a pipe through his spokes and it's like string to string string to string and he's like lying on the ground and he's like holding his knee and he's like saying types are a lie you know it's like if you have like very imprecise types and it's like of course it can't help you you haven't tried to help yourself or like you purposely hurt yourself that makes a lot
Starting point is 00:29:52 of sense and uh yeah and there's also the documentation part you were mentioning um also right like like a unit test can only show i guess the the absence of a of a specific bug right where like a a type like if this is of of type integer like it it just can't be a string coming in here right like the the types prevent a whole class of of problems where the uh a unit test can just check that one thing doesn't happen. Yeah. But then, like, yeah, continuing on that, it's like, if you don't have the refinement in there, like, if you don't say that it's restricted to a non-zero or non-negative number,
Starting point is 00:30:38 then it's like, if you run into those kind of problems, then it's like, of course, it is going to happen. Like, there was no guarantee that course it is going to happen. Like the, there was no guarantee that this wasn't going to happen or rather it's not like, of course it's going to happen, but it's like very well, like it could happen.
Starting point is 00:30:55 And if it does happen, you can't be too surprised. Like another case is say like you have some sort of sum type so so things can be like a or b um but if you have uh code that only handles the a case like it's it's still typed but but at runtime that could explode if it hits a different if it hits b yeah like i so i consider this like a total anti-pattern. And you see sometimes people using languages that aren't as powerful, they write these cases where it's like the other branch just gives you back junk or just says debug crash. This shouldn't happen. But, you know, if you write code that, like, type checks with this,
Starting point is 00:31:45 then it's, like, mechanically it can happen. And, like, at some point it very well might, and it's just by crash. Of course, like, there's, like, different ways of, like, then augmenting it with more types and, like, being able to, like, coerce certain values at certain situations because you know that you've provided evidence for it. But it's like, I think this very much gets into the thing where like,
Starting point is 00:32:13 I usually say that like the problem with programming languages, like type ones, especially isn't necessarily that they don't have the features. It's that they don't have the right culture for it. So if you, if you don't have like a culture of caring about like figuring out these things finding about finding out about these things and like taking inspiration from other places and it's like of course you're going to end up getting like a lot of incorrect modeling everywhere this kind of ties into, um, like pattern matching and, and like totality checking. So, you know, some very few languages do actually enforce, uh, totality so that you have to
Starting point is 00:32:53 to check all cases. Um, how about pure script? Does it have any sort of, um, functionality in that area? I mean, so I have to split that up and say that, like, yeah, I mean, generally as a culture, programmers care a lot about total functions and probably even more than the Haskellers care about total functions, where Haskellers accept that like asynchronous exceptions might happen. And then there is different thoughts about how to handle these. But in PureScript, almost everybody believes that you should always only have these total functions.
Starting point is 00:33:32 But totality here, meaning that, yes, all exhaustiveness checks are done, and then all the obvious runtime errors are avoided, and there's purposely no triggering of exceptions and runtime errors are avoided and there's no purposely no triggering of exceptions and runtime errors. But you'll still have cases, right? Like it's the halting problem where like you literally can't like just do everything on earth, right?
Starting point is 00:34:00 So you're gonna run into these like sometime, like say when you're writing recursive parsers, like, if you don't use, like, monad rec or something, then you might, like, get psychophilus and such, and that's going to happen. And, like, there's only a handful of, like, complete total languages that are used out there. PureScript does some totality checking then,
Starting point is 00:34:30 but not all of it? Is that what you're saying? I mean, it does the standard exhaustiveness checking, but it's just that it can't check the totality stuff that isn't in the type system. Yeah. Right. What were the stumbling blocks for you when you came to
Starting point is 00:34:48 PureScript? I mean, so when I picked up PureScript, even though I used Elm a little bit before, I literally did not know what an ADT was. I didn't know what a sum type was. I didn't know what a prototype was. I didn't know what a data type was. I didn't know what a prototype was. I didn't know what a data type
Starting point is 00:35:05 constructor was. There were a lot of things that people would probably laugh at when they hear this, but I just didn't know them. And I don't know. Maybe part of it is my own fault, but it's also like I was never exposed to the right terminology to even learn
Starting point is 00:35:21 these things. So I kind of saw it as inevitable that I wouldn't learn these things. So I kind of saw it as inevitable that I wouldn't know these things. But yeah, just slowly learning what an ADT actually is, what constructors actually
Starting point is 00:35:36 are. Those kind of things took time, mostly because I didn't ask anyone. But other than that, just like for the everyday coding things, I think it's just experience, really.
Starting point is 00:35:54 Like, the base language, when you use it to write applications, isn't too difficult. It's like just being more patient with yourself thinking through it and like not being too afraid to think about like the symbol substitution that goes on when you like apply functions like map like if if it's like a like map is like it takes a function a to b
Starting point is 00:36:20 and they give it some functor a and it gives gives you the same type of functor back with B that's gone through that transformation. And doing a simple substitution in your head or just writing it out even, I sometimes write it out in that buffer, these kinds of things. It's like, I don't't know it feels kind of silly but it's actually quite useful to like further your own understanding of it so that makes sense i guess i don't know if i have like a good
Starting point is 00:36:55 answer i think it's a good answer but one thing i was just thinking of is is like the fact that you that you knew javascript and you're saying that PureScript, you know, transpiles like one-to-one down to JavaScript. Like, does that give you some sort of advantage wherein you're like, you know, you can look at what this function becomes in JavaScript to like sort of the translation might give you some insight. Is that the case?
Starting point is 00:37:24 I don't know. I don't think that's happened to me. No. I mean, like, I use the output to, like, debug stuff, but, like, when you look at, like, these functions being applied everywhere and passed around, like, looking at, like, looking at how traverse works,
Starting point is 00:37:42 like, the function traverse, like, looking at the JavaScript output, even if you, like, prettify the names, it's, like, this barely makes sense. And, like, it's still the same for me. Like, if I had to write, like, functional JavaScript, it's, like, if it's, like, light functional or whatever that most people do, it's, like, fine. And then as soon as it gets, like, more into code I would write at 3 a.m. in PureScript,
Starting point is 00:38:09 that code I can't write at 2 p.m. with a full cup of coffee. This functional JavaScript stuff baffles me. It's incredibly hard to write all this stuff. And when people talk about doing this, I'm just like, well, why? This is so much more work than you need to do and doesn't have value. It seems to me like maybe there's some contradictory statements here because you made the case that the translation
Starting point is 00:38:41 from PureScript to JavaScript is very simple. But then you said, when you look at the generated JavaScript, it makes you want to cry. Yeah, I mean, the individual statements, you can tell where they are and how they map, right? But then that doesn't mean that the actual function applications make a lot of sense. I see.
Starting point is 00:39:04 So what's the story? It was like we talked about the form function interface um but like what about the type so if i have a list in pure script is that is that like a javascript array or or how does this translation happen i mean so a list is like a normal link list right so it has like a constructor, constructor, constructor, or cons constructor. And it has like a nil constructor, right? So like these are represented in like normal JavaScript classes. And they have like representations in runtime values. So these aren't like too transparent.
Starting point is 00:39:44 Like you can use them kind of directly, but it's not very, like, forgiving or nice. But if you, like, say, if you use an array instead or record or string or whatever, then these are, like, directly represented. So, like, you can write bindings that work with arrays directly, and then you can write bindings that are polymorphic, so, like, they work with arrays directly. And then you can write bindings that are polymorphic, so they work with any A.
Starting point is 00:40:07 But to write actual bindings that work with list, specifically, is kind of impossible. Or just not very ergonomic. But you can use array, and then that's just the JavaScript array. Yeah. And then if you want to write normal algorithms and functions out of it,
Starting point is 00:40:28 then you can convert it from array to list and back. And there's functions that are, say, in the foldable package where you say, okay, I know my array is a foldable and I know that list is a foldable, so I can say from foldable convert this to that and it converts the array to list and then you can do that vice versa so like your conversions are like quite quite easy because like all the instances that you
Starting point is 00:40:58 most instances that would make sense already exist okay are people writing things in pure script and you know releasing them to the greater like no js community like it seems to me that you that you could right that you could use this these guarantees to write something that's really solid and then just release it as a js library yeah but i think i know of a few cases like someone wrote like a generic page scraping tool or website scraping tool and i was in pure script and it has a good deal of numbers and everything but um other than that like it hasn't been too common just because like well pure pure scripters aren't very good at marketing, right? Like, me included.
Starting point is 00:41:50 Like, I don't really, like, market my material, like, very well. So, just, like, even if you made a generically, like, usable library and you did, like, expose a kind of interface that could be used in JavaScript, it's just not going to be too popular. This is what I think about AF, which is an incredible library and it's much more efficient than any other crap I use in normal usage.
Starting point is 00:42:23 But it's just never going to be that popular, even if it has like a very JS-friendly interface because nobody is really going to like advertise it very much. And also like at this point in JavaScript land, everybody just wants to use promises, even if they're flawed. And to get non-eager promises, people are just going to give you a function that has a promise.
Starting point is 00:42:49 So it's like a lot of degenerate cases are already sailed in JS land. So what's AF? It's just Asynchronous Effect Library in PureScript. Well, that's about the gist of it. Okay. And you think it would be a good solution for just general node use, but it's not going to happen, is what you're saying. Yeah, I mean, it's quite nice,
Starting point is 00:43:21 and you can spin off fibers from it and manage and kill fibers. And it's a nice way to be able to work with asynchronous data. But yeah, I think it'll never be popular. And well, understandably so. So do you think, how about the other way? Is it a benefit to you as a PureScript developer that there's this rich Node.js ecosystem like that you can pull things in
Starting point is 00:43:49 and kind of do your FFI stuff? So as much as I like PureScript, I have mixed feelings about that. Like even from vanilla JavaScript and like currently at work, I use some JavaScript libraries from PureScript, and I use them primarily on Node. I don't know.
Starting point is 00:44:15 It's like sometimes the libraries aren't very well made or maintained or designed. A lot of times they are too much API churn where someone decides to just rewrite the entire API a lot of times they are like too much like API churn where someone decides to just rewrite the entire API a lot. And a lot of times they're just like written off like one-off like demos. And that's understandable. But it's like then it's a lot of times like you only get like one or two solutions for something that's a problem.
Starting point is 00:44:40 So I don't know if I really like the node ecosystem so much but it definitely is like convenient i guess so who should use pure script who should use pure script yeah like um you know who you want to grow the pure script ecosystem who should be checking it out what kind of developers is js developers uh java people anybody yeah i guess just about anybody who wants to write anything that needs to run javascript so now if you want to do front end or you want to do node or you want to do some small demos on node on like either server or Raspberry Pi, just anything that will run JavaScript. And also like if for whatever reason you're one of the people who likes like small specialty
Starting point is 00:45:33 languages and like developing backends for them, PureScript does have like kind of some support for being able to write backends generically. So like they have been people who write, like, C++ and Erlang backends for PureScript. So there is, like, opportunities for people who want to just take over the compiler. But, yeah, the general user is just anyone who needs to write anything that needs to run on JavaScript.
Starting point is 00:46:01 And then it's, like, it's programs and, and like synthesize routines that I don't, I shouldn't have to write manually that will be correct. So PureScript has this functionality called type holes, correct? Yeah. And so previously I had an episode about Idris and we were talking about in Idris, you can have, you can have a hole, you know, you say, I don't know what goes here and then you'll be told the type. And then also they have like an expression search.
Starting point is 00:46:34 So it could tell you, Hey, what goes here is actually this function that exists. And I'm just going to write in the definition for you. Does peer script go in this direction or well it can't be as powerful in that there's not as much information to work with but uh like roughly speaking i do use a typo signal uh typo feature a lot i just write like question mark i don't know what this is. And then the compiler can tell you after
Starting point is 00:47:07 searching through your environment, okay, these functions actually meet the requirements, actually are the same type as that hole is. And it can use these functions properly. And then usually it's some suggestion like
Starting point is 00:47:22 traverser sequence. And so I use it quite a bit, but it's not gonna be anywhere near as powerful as idris but also i i don't have very much experience writing idris and also i think pure script has has like type class deriving, is that right? Yeah, some of the type classes can be derived. So, I mean, there are some obviously nice ones, like being able to derive the new type class. So you get these operations that work on any generic new type. Being able to derive equal or odd.
Starting point is 00:48:05 For a lot of like very simple cases it's quite nice to be able to like just drive these and like you throw your data type into a set or something for me the most useful is the type is the ability to drive
Starting point is 00:48:20 generics rep is this like serialization or no, something else? I mean, it's kind of like serialization, but to an actual data type that's derived by the compiler. This whole topic of data type generics where you can generically work with the representation of a data type and just like write generic functions to those representations and let the compiler give you the stuff to translate to and from
Starting point is 00:48:53 those representations to compute data types. Oh, I see. This is like generic programming, like getting the first element of some product type, for instance, and writing it in such a way that it's generic across all product types? Yeah, basically. Like any product type where you derive the generic rep, and then you use the function accordingly to convert to and from.
Starting point is 00:49:22 So like GAC generics, generics rep, and I guess in Scholarland, there's a few libraries that do this. Like Shapeless, I think, is in the similar vein. So it could be used for serialization, right? Because once you have that generic representation, you could use that then to derive some sort of serialization code.
Starting point is 00:49:47 And in Haskell, I've used it to just work with record types and create the deserializers for INI files and also to do type level routes so that I would have the correct types for registering handlers for Scotty server routes. And just a whole bunch of anything you want to do, really. Yeah. Very cool. That's about all I got for questions, sir. Is there anything else you'd like to mention?
Starting point is 00:50:27 I guess I should shout out that I live in Helsinki and it's nice here. I'd like to meet anyone who actually listens to this show from Helsinki because there's just so few people in Helsinki, it feels like. Perfect. So this is a call out for anyone listening to this in Helsinki. All right. Well, it's been great having you on, Justin. Thanks so much for your time. Yeah. Thanks for having me.

There aren't comments yet for this episode. Click on any sentence in the transcript to leave a comment.