CoRecursive: Coding Stories - Tech Talk: Purescript And Avocados with Justin Woo
Episode Date: April 4, 2018Tech 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)
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
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.
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
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.
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
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.
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.
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,
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
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
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.
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?
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.
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,
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.
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?
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
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
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
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
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
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
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
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...
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.
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.
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.
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,
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
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.
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.
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?
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,
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.
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
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
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.
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
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
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
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
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
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
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,
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
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?
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?
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,
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,
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.
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.
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
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
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
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,
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.
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,
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,
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
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.
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?
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,
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
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
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
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
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.
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
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
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?
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,
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,
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
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.
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.
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.
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,
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
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.
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.
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.
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,
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
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.
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.
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
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.
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.
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
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
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.
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
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
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.
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.
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?
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.