spotifyovercastrssapple-podcasts

Getting started with elm-review

elm-review lets you extend guarantees that Elm provides before running your code, by inspecting your project with custom rules that you write in pure Elm!
April 8, 2020
#3

Transcript

[00:00:00]
Hello Jeroen! Hello Dillon! How are you doing today? I'm doing very well, how are you?
[00:00:06]
I'm doing pretty good, I'm curious to hear your thoughts on this topic because
[00:00:11]
I think you have a lot to say. What are we talking about? Today we're
[00:00:15]
talking about Elm Review and the first episode we did was on Elm pages, that
[00:00:20]
was your baby. Yes. This is my baby. Yes, I think there will be a lot of words
[00:00:26]
coming from you today. Yeah, I'm excited to talk about this. Yes, I'm excited to
[00:00:33]
learn about it. Yeah, so okay, Elm Review, I believe it used to be called ElmLint.
[00:00:40]
You had a package called ElmLint before and this is like a new incarnation of
[00:00:44]
that. Basically, so I spent a lot of time working on it when it was called ElmLint
[00:00:52]
and right before publishing it, releasing it, I renamed it Elm Review. So I never
[00:00:58]
really announced it as ElmLint. I did a talk about it in Elm Paris, the meetup,
[00:01:07]
in 2017. So it's a very old project, it's dated before Elm Analyze, but it's never
[00:01:16]
been released as ElmLint officially, although I did publish some packages with
[00:01:22]
a name. So if you see those, ignore them. I can't publish them, unfortunately.
[00:01:31]
So people are probably familiar with the concept of linting and with linters, so
[00:01:36]
you want to give maybe a little bit of the background of why you
[00:01:42]
chose the term review instead of lint. Evan came to talk to me about it.
[00:01:47]
It's a good reason. Yeah, I sent him the link to my blog post before I released
[00:01:53]
it. I was thinking, hey, what do you think of this? Do you think this is a good idea
[00:01:57]
for the Elm ecosystem? And he said, yeah, I think it's okay. I don't remember
[00:02:03]
exactly the words he said, I'm just imagining. The main thing he said to me
[00:02:07]
was like, are you sure you really want to call it ElmLint? You should use some of
[00:02:11]
those quotes from Evan on the package documentation site, just like, yeah, I
[00:02:18]
think it's okay. Yeah, well, I don't have the course anymore because it's on Slack.
[00:02:26]
That was six months ago, so they're lost forever. Right, but there's this sort of
[00:02:33]
literal naming concept in the Elm community, right? So Evan was trying
[00:02:38]
to think about, like, what is linting anyway? What does
[00:02:43]
your package really do? Yeah, exactly, and I actually didn't know what linting is. I
[00:02:48]
was just calling it a linter because that's what people call these kind of
[00:02:53]
tools. So I looked it up a little bit because lint was not in my English
[00:02:58]
vocabulary, and it turns out it's removing pieces of dust from clothing, so
[00:03:05]
it's basically cleaning it. And that's kind of what most linters do. They try to
[00:03:12]
polish your code, make you adapt to a certain code style, and that is not what
[00:03:18]
Elm Review is about. So I decided to rename it. I had some project names to
[00:03:24]
choose from, and Elm Review started to sound good to me, and now I'm very happy
[00:03:28]
with the choice. Yeah, I like it a lot too. It seems very clear, and you know, maybe
[00:03:33]
for somebody who's familiar with the term linting, that might seem easy to
[00:03:39]
understand at first, but so does Revu, and to someone who's not familiar with
[00:03:42]
linting, Revu makes a lot of sense too. Yeah, linting, you have to know the term.
[00:03:48]
Someone needs to have explained to you what the linter is, but my colleagues
[00:03:52]
still call it a linter. So it's hard to get rid of the name, like, oh, Elm Review!
[00:04:00]
Well, it's just kind of a linter, isn't it? Yeah, well, in a way it is.
[00:04:07]
You can do linting with it, but I am to make it something a bit bigger.
[00:04:13]
Right, okay, so let's get into that. So let's kind of get into a
[00:04:17]
definition of what Elm Review does. What is it, besides this sort of vague
[00:04:23]
notion of a linter, what exactly is it? Yeah, so Elm Review is a static
[00:04:30]
analysis tool for Elm, written in Elm. So static analysis just means we're gonna
[00:04:37]
look at your code, and we're gonna detect patterns, and then do something about it.
[00:04:43]
In our case, we're going to report some patterns that we do not
[00:04:48]
want to stay in the code. For instance, something that most linters do is say,
[00:04:54]
hey, this variable is not used, remove it. Or in some languages like JavaScript,
[00:05:00]
remove some features or forbid some features, like don't use pattern
[00:05:05]
matching, like don't use switch case, don't use goto, things like that.
[00:05:10]
Things that you do not want to see in your code. Sometimes it's as simple as just
[00:05:13]
forbidding patterns, sometimes it's forbidding a pattern in a certain
[00:05:19]
context, like don't use switch case with a number, for instance.
[00:05:26]
Right, it's an interesting problem of how do you identify looking at the
[00:05:32]
syntax, statically analyzing the code, how do you identify potential problems or
[00:05:36]
red flags or things that basically could indicate a code smell, right? That you're
[00:05:41]
kind of trying to detect a code smell and use this automated tool to put it in
[00:05:45]
front of a human being who can look at it and say, oh yeah, what do you want to
[00:05:50]
do about this code smell? How do you want to change this?
[00:05:54]
What are some examples that you think are really valuable or some
[00:05:59]
cool examples that you've seen? I know you've been working with some people on
[00:06:02]
some rules they've been building for Elm Review. What are some cool examples?
[00:06:07]
That was mostly me, unfortunately. Well I know, I saw someone was using it, maybe
[00:06:14]
this is sort of like a beta testing phase, but somebody was playing around
[00:06:17]
with an RGB rule to see if something is within a certain range.
[00:06:22]
Yeah, Ruf, I don't remember what he was. Yeah, Ruf. Rinar. Okay, yeah, he built it
[00:06:30]
himself and then he showed it and then I gave him some comments, but he did it on
[00:06:35]
his own. That was pretty cool. So what he did was publish a package which
[00:06:40]
looks at function calls that are called RGB or RGB 255. So those are the
[00:06:46]
functions that create colors in Elm UI. This rule just looks at what arguments
[00:06:51]
are given to it and then it checks whether those are in the proper
[00:06:56]
boundaries. So for RGB it checks whether the value is between 0 and 1 and for RGB
[00:07:02]
255 between 0 and 255. Because what happens if you call RGB with
[00:07:09]
minus 1? That doesn't make sense, that would probably be a bug and we don't
[00:07:15]
want that. And the RGB function is not strict enough to limit the values that
[00:07:20]
you give it. Do you know if his rule checked that it had to be a literal
[00:07:25]
value rather than a parameter? It only checks for literal values. Right, right.
[00:07:31]
You could try to evaluate the variable or function name, function call, but that's
[00:07:40]
something that is pretty hard with static analysis. At some point it will
[00:07:44]
get very hard and sometimes it's just too dynamic and you can't say anything
[00:07:49]
about it. Yeah, I mean sometimes it's coming from user input and there's no
[00:07:54]
literal value in the code to look at. So why don't you tell us a little bit about
[00:07:59]
like let's say we're building this RGB range checker. What does that look like
[00:08:05]
writing that with Elm review? So you said it's a review tool that's built in
[00:08:11]
Elm for Elm. Yeah. How do you build it in Elm? What does that look like? So Elm
[00:08:16]
review is both a CLI and a package. So the CLI runs the review and shows you
[00:08:22]
the errors. We've got the package which gives you some very nice APIs to work
[00:08:28]
with to create rules, what I call a rule with other knitters call that checks or
[00:08:36]
yeah checks or rules. So you got a package which creates those rules and
[00:08:42]
basically what it works with is an AST, so an abstract syntax tree. And what the
[00:08:48]
package does for you is it lets you visit every node of the tree and then
[00:08:54]
lets you do something with it. So either report a pattern like you notice
[00:08:59]
something that is wrong so you put an error and that will be shown to the user
[00:09:03]
or you record some data that you might use for to report errors later or to not
[00:09:11]
report errors later. So basically when you create a rule you say I want to
[00:09:16]
visit this kind of nodes, I want to visit imports, I want to visit
[00:09:21]
expressions, I want to visit declarations and based on that, based on
[00:09:27]
what you see in there, you're going to say hey this this does not look good
[00:09:33]
report an error. So for instance if we do the RGB rule we're going to look at
[00:09:39]
expressions, specifically we're going to look at function calls expressions where
[00:09:44]
the function name is called RGB or RGB 255 and then we're going to look at the
[00:09:50]
arguments and see if they are literal integers or literal floats and if they
[00:09:57]
do not match, if they do not fit in the correct range then report errors. So
[00:10:03]
that's basically what Unreview allows you to do. Right and so to paint a
[00:10:09]
picture for people listening, you know we said at the top that this is built in
[00:10:16]
pure Elm, you build the rules in pure Elm. Yes. Let's paint a picture a little bit
[00:10:19]
of what that looks like. So you say you can visit these different nodes in
[00:10:25]
the AST of your code base. So you're going to have some sort of function
[00:10:31]
that you call to say I'm defining this rule and then you get a parameter
[00:10:38]
which is like each node that you're visiting in the AST. Right and then
[00:10:44]
you're doing like a case statement that says is this a function call, is that
[00:10:47]
right? Yeah basically. And then if it's a function call then you get okay well
[00:10:52]
which function is it invoking like what's the module name and the function
[00:10:56]
name? Yes. And then what are the parameters that are being passed and
[00:11:00]
those parameters are expressions and then you can check is the expression of
[00:11:05]
type literal int? Yes. So that's all it is right? That's all it is. In this one
[00:11:11]
this is simple. This is a simple one right but it you have the full power
[00:11:17]
of doing whatever static code analysis you could imagine through this API. So
[00:11:23]
you're exposing sort of a platform for building your own customized rules.
[00:11:28]
Exactly. And you completely have the power at your fingertips of analyzing the entire
[00:11:33]
AST in Pure Elm which is really really fun. Yeah I have a lot of fun running
[00:11:38]
all those rules. Yeah so maybe this is a good time to sort of talk about Elm
[00:11:45]
analyze and contrast these two approaches. You want to talk a little bit
[00:11:50]
about what distinguishes the the different like philosophies or approaches
[00:11:55]
of these tools? Yeah so Elm analyzes at the moment the de facto standard for
[00:12:00]
static analysis in Elm. So the way it works is it gives you a set of checks
[00:12:07]
because it calls the checks but it's the same thing as a as what I call a rule
[00:12:10]
and they are all opt out so you get a full set of those to start with and it
[00:12:17]
just goes through your AST and reports the problems. So the thing with Elm
[00:12:22]
analyze is that you cannot customize those rules or very very little. You can
[00:12:28]
disable it and that's pretty much it. And the idea is that they want to enforce
[00:12:32]
high quality Elm code so that's what they try to do so they detect unused
[00:12:38]
variables and use imports. They detect ways you can simplify your expressions
[00:12:44]
like concatenating two lists so you could if it's if they're all literals
[00:12:50]
then you can just create one list. But in their contribution guidelines they say
[00:12:56]
that they do not want to be able to customize the rules like they do in
[00:13:00]
Iceland and that is exactly what Elm review wants you to do. When I write Elm
[00:13:06]
review I want you to be able to customize your rules. I want you to
[00:13:10]
create your own rules because I think that's where the power of static
[00:13:15]
analysis is. So in the JavaScript world where I come from you had jsLint, jsHint
[00:13:23]
and later came eslint. The difference between all these is that eslint
[00:13:28]
allowed you to create your own custom rules and that created a whole huge
[00:13:33]
ecosystem of rules maybe too big in their case and they made it
[00:13:38]
now the de facto standard. And the reason why I think that's important
[00:13:43]
is because the most interesting rules are not about code qualities, the ones
[00:13:49]
that you want to make for your own team. One thing you can do is enforce the
[00:13:54]
conventions of your team. Instead of saying hey we have a mockdown file here
[00:13:58]
that explains how we structure our Elm code, just enforce it. So you can
[00:14:05]
create an Elm review rule that does exactly what that guideline would
[00:14:10]
say and maybe with some auto fixes or very helpful error messages that tell
[00:14:16]
you hey we do not do this this way in our project because this and that reason.
[00:14:22]
You should try this and that to make it work the way we want to.
[00:14:28]
Right, so I've had the experience with Elm analyze. I've gotten a lot of
[00:14:34]
value from throwing that into projects before but also I've found that
[00:14:39]
sometimes the rules don't do exactly what I want. So one example of
[00:14:44]
that is I can't remember if it's unused symbols or something like that
[00:14:49]
but sometimes I'll find I'll import something. What I basically want is I
[00:14:54]
want to import something the same way every time I use it. So if I say import
[00:14:59]
dict exposing dict or if I say like import HTML dot attributes as adder
[00:15:07]
exposing attribute. I'm not sure if attribute is in HTML. I guess it's in
[00:15:13]
HTML but you get the idea right? Every time I import HTML I
[00:15:20]
want to expose attributes. I might not use it in a particular file but I
[00:15:28]
want it to be exposed for when I do use it. That's just what I want. I want
[00:15:33]
my import HTML exposing attributes to look the same in every single module.
[00:15:40]
So this unused symbol rule doesn't quite get that and I'm like no this is how I
[00:15:47]
want this code to look and so there will be this sort of mix of some things that
[00:15:52]
it finds that I'm like oh good catch I don't want to be doing that and then
[00:15:56]
there are some places where it's just pestering me about something that I'd
[00:16:01]
like to be able to tell it exactly what I want it to do. So I guess
[00:16:05]
that's your thinking here is that I can teach it the way that I want to write my
[00:16:11]
import statements. Yeah you definitely could. I also think there are plenty
[00:16:16]
plenty of cases where you do not want to enforce something. So maybe you do not
[00:16:21]
want a rule for that. Just tell people what to do because the thing is what I
[00:16:27]
noticed is that a lot of times when you enforce something that people don't like
[00:16:31]
they will try to disable it. Like in ESLinted they do that with a comment. You
[00:16:36]
will see disabling comments all over the place or they will try to find a
[00:16:41]
workaround which often makes the things worse. So in this case I think it would
[00:16:47]
be fine. Yeah that makes sense. I've had this thought before actually if I could
[00:16:51]
go down this path a little bit more of this particular idea of like having a
[00:16:56]
standardized way of importing things. I've thought it would be cool if there
[00:17:00]
was like a configurable way to say okay this is how I import HTML attributes.
[00:17:06]
Like I always want if I import that at all then I want it to be import HTML dot
[00:17:11]
attributes as adder. Yeah. Because I don't want to have it like in some places it's
[00:17:16]
imported as HTML dot attributes and some places it's as adder and that can be
[00:17:22]
confusing sometimes because you're going back and forth between different modules
[00:17:25]
in your code base and they're imported different ways. It becomes confusing and
[00:17:29]
so I've thought it would be cool if there was a way of enforcing in the
[00:17:33]
entire code base here's a certain set of import statements that should be aliased.
[00:17:39]
You should import as this and you should always expose this in addition to
[00:17:46]
whatever other specific things perhaps you expose or maybe you want to enforce
[00:17:51]
that or whatever but that seems like something that could be an interesting
[00:17:54]
tool and since it's just Elm code so since the rules are just pure Elm
[00:18:00]
code you could make it configurable by passing in your specific configuration
[00:18:05]
and saying these are my imports these are the module names and these are what
[00:18:10]
I expect them to be aliased to so I could imagine that could be a pretty
[00:18:14]
cool rule that you could share with people and let them define their own
[00:18:17]
list of import aliases. Yeah exactly I was actually thinking of doing that in
[00:18:22]
my workplace. Oh cool. Like for instance sometimes we import JSON encode as
[00:18:29]
E and sometimes as encode and sometimes without an alias and I think would be
[00:18:34]
just nicer to enforce having the same one everywhere. Very cool. So is there a
[00:18:41]
way currently in Elm review to apply fixes? I think there might be some
[00:18:47]
functionality like that is that right? Yeah there is. So in Elm review v1 you
[00:18:52]
can only apply fixes one by one so you tell the CLI to apply fixes and
[00:18:57]
it will prompt you with hey this is the error this is my suggestion do you want
[00:19:02]
to accept it do you not and then if you do then it gets it fixes it applies Elm
[00:19:08]
formats all the time that's a design choice I want people to use Elm formats
[00:19:14]
that might be wrong but my choice and in Elm review v2 you also have a fix all so
[00:19:23]
you can just fix all the errors and then have a giant diff and it prompts. Is it
[00:19:29]
the errors of a particular type or just all errors? All the errors that provide a
[00:19:34]
fix. Okay. And that is not something you always want to do. There's something
[00:19:41]
that detailed in my documentation. You basically want it when it's you can fix
[00:19:46]
it always when you know how to do a proper fix and when it's it doesn't
[00:19:50]
bring anything to the user and it will not surprise them. Right so do you think
[00:19:55]
that would be a reasonable place to apply that rule to say hey if you're
[00:19:58]
importing JSON dot encode as E actually the way we do that is encode. Would that
[00:20:08]
be a reasonable place to apply an automated rule perhaps in a code base?
[00:20:11]
Well yeah it would but the thing is you would need to change every call to E dot
[00:20:19]
something to encode so if you do that then it's all good to me. So you and
[00:20:26]
could you do that? Yeah I think you could. Because you get access to the syntax tree
[00:20:31]
and you can define a way to transform that syntax tree and it will output that
[00:20:36]
as the fix? No the fix is a bit lower primitive you basically just play with
[00:20:42]
ranges so remove characters insert characters or replace characters.
[00:20:47]
Interesting. With the range like start and with the row in a column. Yeah I
[00:20:52]
could have chosen to do it with an AST but that would have been more
[00:20:56]
complicated and that hasn't been my focus yet. Right. Maybe we could do it but it
[00:21:01]
does makes a lot of things much harder so I think it's nice to have at least
[00:21:05]
this primitive and for now it's it's worked alright so I haven't needed to
[00:21:11]
do anything harder and basically everywhere you see E dot something and you
[00:21:17]
know that it's the E module not something else that ends with an E you
[00:21:22]
replace it with encode dot for instance and that would be it. Right yeah that
[00:21:28]
seems that seems pretty doable very cool so if somebody wants to get started with
[00:21:34]
this how do they dive in? To get started with Elm Review you basically want to
[00:21:39]
add a review configuration to your project so as I mentioned before there
[00:21:44]
is a CLI so you do Elm Review init and it will create a new application inside
[00:21:51]
your project we do that so that the review codes your your rules and the
[00:21:57]
review dependencies so all the packages that contain rules that you might want
[00:22:00]
to add to your configuration that they're separate from your package or
[00:22:04]
application so the init creates the project so an Elm JSON and a review config
[00:22:10]
dot Elm file that one comes empty by default there are no rules enforced
[00:22:14]
there's also design choice that I made I don't want to enforce best practices
[00:22:19]
because I don't feel like I should tell people what to do so it comes empty and
[00:22:25]
then what you do is either you add packages that contain rules and use the
[00:22:30]
rules that are in there and you add them to review config or you write your own
[00:22:34]
rules and you add them to your review config so yeah I would start out with
[00:22:39]
Elm Review init and then adding some packages that like I wrote for instance
[00:22:44]
the ones that remove unused things or do a lot of simplifications. Right and just
[00:22:50]
to just to make it clear what you're saying is when you do Elm Review init
[00:22:56]
and you say it creates a separate app it's just creating a subdirectory which
[00:23:02]
the CLI is going to go into that subdirectory that review subdirectory or
[00:23:06]
whatever it's called yes and it's going to use the source folder in that
[00:23:11]
directory which is just an Elm code base it has an Elm dot JSON which lists the
[00:23:16]
dependencies for running the review rules yeah which are independent of the
[00:23:22]
dependencies for the main Elm projects that it's running the rules on. Exactly
[00:23:26]
yeah that's it. And then you just got another Elm code base within your main
[00:23:32]
Elm app for running the rules. No that you don't that's what the CLI does so
[00:23:39]
every time you run Elm Review it creates a new application it compiles an
[00:23:45]
application with your code with your dependencies and then it runs that one.
[00:23:50]
Right right but you you have this sort of folder that defines all of your
[00:23:55]
rules in pure Elm and you don't have to worry about the specifics of visiting
[00:23:59]
the files and those details the CLI takes care of that for you. Exactly yeah
[00:24:04]
yeah yeah cool yeah you have very little things to do on your own. Right.
[00:24:09]
Even just running your own rules there a lot of complexity has been removed for
[00:24:14]
you. So is there a good starting point to sort of copy paste as a starting point
[00:24:21]
to play around with creating your own rules? Not right now I'm probably gonna
[00:24:26]
do something before I release v2 because at the time that we record this it has
[00:24:31]
not been released yet hopefully in the next few days so hopefully by the time
[00:24:35]
you listen to this episode yeah probably gonna make something to make it easy for
[00:24:39]
people to copy paste because that's what people tell me a lot they don't want to
[00:24:44]
think about which rules to enable they just want to copy paste something which
[00:24:48]
I do not think is great but as long as they stay critical of the review rules
[00:24:52]
I'm fine with that. Yeah yeah you could have some sort of commented out set of
[00:24:58]
possibilities so people can see what the options are as a starting point and but
[00:25:02]
so if you want to define a custom rule sometimes that can be intimidating to
[00:25:08]
figure out just like the wiring for creating a custom rule but I guess it's
[00:25:11]
just calling a single function and then that creates a rule value. Yeah basically
[00:25:16]
yeah so I've got a lot of examples in the documentation so you basically can
[00:25:23]
just copy paste an example and add it to your review project your review
[00:25:28]
application and add it to your review config and that's it. So basically if
[00:25:33]
someone wanted to try out writing their own rule they should go to the projects
[00:25:37]
readme and then find an example rule and they can copy paste that to play around
[00:25:42]
with it. Yeah well to the review rule module but yeah basically. Right right
[00:25:49]
they should create a self contained module for their rules just as a good
[00:25:53]
practice of abstracting things away. Yeah I would always put it in a different
[00:25:57]
module yeah one rule one module. Right nice well are there are there any other
[00:26:03]
resources that people should know about for getting started? I would probably
[00:26:07]
say just the blog post about releasing version 1 and version 2 yeah that would
[00:26:15]
be it for if you want to get started. Wonderful. So I released Unreviewed
[00:26:20]
version 1 about six months ago and I'm ready to release v2 and there's
[00:26:26]
something that I really really wanted to get out and I'm very happy that I got
[00:26:30]
something pretty cool with it. It's the ability to look at whole projects for
[00:26:35]
when you analyze your project your code. So with most static analysis tools like
[00:26:41]
Elm Analyze and ESLint whenever you look you go through the AST so you do it one
[00:26:50]
file by one file so when you look at module A you go through the whole file
[00:26:55]
you report some errors and then when you're done you go through module B
[00:26:59]
and then you forget at that point you forget everything about module A even
[00:27:05]
if that was interesting to you so if module B imports module A you do not
[00:27:11]
know what happens in A so you lose a lot of information and that is a shame
[00:27:16]
because there's a lot of interesting information that you can get there
[00:27:19]
especially in Elm in JavaScript with ESLint that would be useful too but
[00:27:23]
sometimes just too complex to analyze. When you don't have type signatures
[00:27:26]
helping you navigate through it it's too much to manage. Yeah exactly and
[00:27:31]
handling all those details. Yeah and especially when things are too dynamic
[00:27:35]
that's when you just abandon hope. Because Elm is a static language it's
[00:27:41]
not just statically typed it's a static language so you know when you hit
[00:27:45]
compile you know the functions that will exist you know the top level values and
[00:27:51]
functions that you'll be working with and you can't just eval something and
[00:27:56]
then something comes into existence at the global scope. Yeah that makes it really
[00:28:01]
really nice to work with. As someone who wrote a lot of ESLint rules that was a
[00:28:07]
pain it was very frustrating to say oh well in this case we really can't know
[00:28:12]
just well. But with this version what I introduce is what I call project rules
[00:28:19]
which can look at the whole project so when you have finished visiting A you
[00:28:26]
can keep some information the information that you want and then when
[00:28:29]
you start to read going through module B then you can use the information from
[00:28:35]
module A. So would that be sort of like the difference between like a map or a
[00:28:40]
fold where if you map then you have to say okay take this value and turn it
[00:28:48]
into a new value whereas a fold you get to keep track of this accumulator where
[00:28:53]
you can store some running value that you get every time you visit a new thing
[00:28:59]
and you can add additional context into that? Yes yes that's a if you're very
[00:29:05]
familiar with maps and folds yes that's exactly how you can view this. There was
[00:29:12]
probably my initial implementation a map and now it's a fold that is true. So
[00:29:16]
what that opens up to is like detecting unused variables or unused exports
[00:29:23]
everywhere so if module A exposes a function well with when you can only
[00:29:30]
look at one file at a time you can't ever tell whether it's used or not and
[00:29:34]
you can't tell the user hey this is never used. With what I call project
[00:29:38]
rules you can you just go through the whole project and oh well it's never
[00:29:44]
used okay remove it report it and that opens just a whole lot of possibilities.
[00:29:49]
This is actually the reason why I didn't promote Unreview too much during
[00:29:54]
these last six months because every time I was thinking of a very cool rule to
[00:29:59]
demonstrate what you could do with Unreview I was like yeah as soon as you
[00:30:04]
need to get a value from another module or expose things well you can't tell.
[00:30:10]
So one example is for instance you know the problem where you have a list
[00:30:15]
of all the custom types of a type like if the custom type is A, B or C you want to list
[00:30:21]
with A, B and C for testing purposes or things like that. The solution people
[00:30:26]
usually go for is having a type check reminder like a case of which lists
[00:30:33]
all the cases which that does nothing with a comment that says hey now you
[00:30:38]
should add it to the list below. Well with Unreview you can just say hey we
[00:30:43]
know all the custom type constructors and in this list there are some missing
[00:30:47]
ones report it. I think it's very cool but if the type was defined in a
[00:30:54]
different module then you can't tell anything. Now you can. That's why I
[00:31:00]
was not promoting it. Yeah that makes sense it opens up a lot more use
[00:31:05]
cases I can imagine just having that contextual ability to define rules that
[00:31:10]
have context that they carry with them. So okay this is perhaps a tangent but
[00:31:15]
that makes me think of would it be possible to define a rule that auto
[00:31:21]
fixes that has an automated fix where you can say if you have an enum where an
[00:31:28]
enum is you know just a custom type that has no parameters for the type so you
[00:31:33]
have you know a fruit is an apple or an orange or a banana could you have an
[00:31:38]
auto fix that says a module that has that maybe there's a certain naming
[00:31:43]
convention for the custom type or something that it's like enum fruit or
[00:31:47]
something and then that module must define a to string and a from string and
[00:31:54]
maybe an all list which has all of the all of those enums mm hmm could you
[00:32:00]
define an elm review rule that checks that those to string from string and all
[00:32:06]
values and functions are defined and if they're not defined creates them for you
[00:32:11]
you could definitely do one that checks for them an auto fix yeah I guess you
[00:32:17]
could just have to handle the case where the functions already exists because you
[00:32:23]
don't you probably don't want to override them yeah you could hmm yeah
[00:32:29]
because that could be pretty powerful because at that point you have a
[00:32:32]
guarantee that if you in your build pipeline run elm review before you do a
[00:32:40]
production build then you can almost consider it a piece of the pipeline just
[00:32:47]
like the elm compiler where you know if you if you have something where your
[00:32:52]
types make impossible states impossible right then you don't have to check all
[00:32:58]
over the place that you're enforcing those constraints because you've done it
[00:33:01]
with a type and the elm compiler will catch it so your your production code
[00:33:05]
won't go live with those impossible states yeah and so if you have your elm
[00:33:11]
review rule and the elm review checks as part of your production build where the
[00:33:16]
production build will not go live unless these constraints are met then you could
[00:33:20]
guarantee that these enum values will always be up to date which is kind of
[00:33:25]
cool you could just build a review rule that just sort of extends the
[00:33:29]
guarantees of the elm compiler in a way yeah that's that's the whole point of a
[00:33:33]
lot of rules that you can do with them right yeah I'm not sure whether that
[00:33:39]
example would be fitting because how would you implement the to string like
[00:33:45]
what name do you give to the banana to the apple would it be uppercase like and
[00:33:49]
that's not those are not choices that you want elm review to do for you so
[00:33:54]
I could do it in my code base and I could just have that be my custom rule
[00:33:59]
that I define that right yeah if you have something very organized and you
[00:34:05]
exactly know exactly how to fix it sure go ahead right yeah it seems like you
[00:34:11]
could make some conventions like that I I'm also thinking about I mean elm
[00:34:16]
review gives you this essentially an elm project that you can run the CLI against
[00:34:22]
this source code for your elm review rules and I'm thinking like it could be
[00:34:27]
interesting to ship this as a part of other tools like for example elm graph
[00:34:32]
QL has you know it does this code generation like there's a feature in elm
[00:34:37]
graph QL that I've had a really hard time getting the developer experience
[00:34:42]
that I want for this feature the feature is custom scalars and I mean I think it's
[00:34:49]
I think it's a really strong feature actually I don't I don't think there are
[00:34:52]
any other graph QL tools out there that have this feature which is but I still
[00:34:57]
feel like the developer experience could be a lot stronger so the feature in a
[00:35:01]
nutshell is you can take a custom scalar for example like a date time or currency
[00:35:08]
value you know money and you could say well if you have this custom scalar on
[00:35:13]
the server if there's something of type money in this graph QL API then when you
[00:35:19]
get money use this decoder to get the money and then use this encoder to
[00:35:24]
encode the money and so it's just automatically going to apply a decoder
[00:35:29]
and if the decoder fails so so for example you could parse a date time
[00:35:33]
using an ISO 8601 decoder and your graph QL requests will fail if that
[00:35:39]
constraint isn't met because a custom scalar in graph QL represents some
[00:35:44]
guarantees about that data you know so like if a graph QL server is serializing
[00:35:51]
something in a date time format then it's it's giving you a promise that I
[00:35:56]
will serialize it in the ISO 8601 format which means I can make that assumption
[00:36:00]
when I decode the data so Elm GraphQL gives you this ability to just say for
[00:36:06]
this type of custom scalar decode it using this set of assumptions which
[00:36:10]
turns out to be a really nice way to work with with code to put those
[00:36:14]
constraints into a single module where you define this is how I deal with my
[00:36:19]
custom scalars so anyway I haven't been perfectly happy with the developer
[00:36:23]
experience because what you need to do is you need to like expose a certain set
[00:36:28]
of types or type aliases that you define in your custom codecs module and then
[00:36:35]
you need to define a custom encoder and decoder for each of those and I kind of
[00:36:42]
compile the code and it gives you these errors which are a little bit confusing
[00:36:47]
if you did it wrong but I'm thinking what if I had an Elm review rule and
[00:36:51]
package that as part of the Elm GraphQL command line interface yeah run Elm
[00:36:57]
review and I could have an improved developer experience where I can look at
[00:37:02]
it and I have the syntax tree for that custom codecs file and I can basically
[00:37:07]
get as fine grained as I want rather than relying on the Elm compiler to give
[00:37:12]
me sort of some general messages I can give some more precise messages and I
[00:37:17]
can give the messages in the language that makes sense for the Elm GraphQL CLI
[00:37:22]
yeah exactly you actually wouldn't necessarily need this CLI the Elm review
[00:37:26]
CLI because you could simply use the Elm review package you would have to do the
[00:37:32]
wiring yourself yes but though you could use it to do something like that yeah
[00:37:37]
definitely interesting interesting I love the idea of having it be rather
[00:37:41]
than you know like you said JS hint or these other tools in the JS ecosystem
[00:37:47]
that say here are the rules this is how you write JavaScript right right this is
[00:37:53]
having it be a platform that's extensible and gives you the tools to
[00:37:58]
look at the AST and do your own static analysis it's kind of interesting
[00:38:04]
because like so one of one of the best features of Elm format is that it's a
[00:38:11]
zero configuration formatter right yeah so you don't have these constant
[00:38:15]
arguments in different Elm code bases about should it be two spaces or four
[00:38:21]
spaces should it be a trailing comma or a leading comma actually don't know if
[00:38:26]
it's two or four spaces I don't care four exactly we don't care right and
[00:38:31]
that's a that's a feature yeah so how do you see this as different like why do
[00:38:36]
you think that it's the right choice to put the power and the configurability in
[00:38:41]
the user's hands in this instance even though this is such a strong feature
[00:38:45]
with Elm format to have zero configuration what's what makes that
[00:38:48]
different do you think I don't think it's right to compare it to Elm format I
[00:38:52]
would more compared to types in a way like in Elm you've got types you got
[00:38:59]
billions strings numbers etc but that's not where Elm's power comes from the
[00:39:06]
power of Elm comes from custom types so you types that you're tailored to your
[00:39:11]
own need so to your own business needs so if you want to build a user you build
[00:39:19]
your own user type which has this and that fields and it wouldn't make sense
[00:39:25]
to only use the built in types for you that's Elm provides you and I think
[00:39:30]
that's kind of the same thing with Elm review sure you have a set of rules that
[00:39:34]
tell you how your code should look like you remove unused variables do things
[00:39:39]
this way which is nicer do not import everything from module things like that
[00:39:44]
but a lot of times you really want to have something tailor made to your needs
[00:39:49]
right so for instance if you want to limits how your application works you've
[00:39:55]
got domains you've got let's say you have a whole section of your project
[00:40:01]
which deals with how you fetch data how you interact with GraphQL another one
[00:40:07]
that deals with business logic and you do not want to link those two you want
[00:40:11]
you don't want to have the one inputs the other then that is something very
[00:40:16]
custom to your needs to your application to how you built your projects same
[00:40:22]
thing with Elm conventions every team has coding conventions if they do not
[00:40:28]
they will soon I think yeah it makes sense to have customizable right so okay
[00:40:36]
so it seems like the way you're talking about on review it seems to me that you
[00:40:42]
look at it as fundamentally different than just a linter in a way because
[00:40:47]
you're talking about it like this is not just a way of enforcing standardized
[00:40:53]
code conventions yes it is not it is a way of enforcing constraints about your
[00:40:59]
code so it almost sounds like you're talking about it like an extension of
[00:41:03]
the Elm type system as a way to to catch certain constraints that you can't encode
[00:41:09]
with the Elm type system by itself exactly the way I see it is if Elm the
[00:41:14]
Elm compiler is your assistant Elm review is your second assistant and
[00:41:18]
incidentally Elm review can do linting but that's not all that it can do it can
[00:41:24]
be much more I think so if if I had a review rule for RegEx you know how you
[00:41:32]
know with the latest API for for RegEx and Elm you do RegEx dot from string and
[00:41:38]
then you have to pipe that through maybe dot with default RegEx dot never yes
[00:41:45]
that's how people usually do it yeah the older Elm API had this bug in it that
[00:41:51]
you could have a runtime exception if you passed in an invalid RegEx and so
[00:41:55]
okay that's nice it returns a maybe if there's something wrong with it but you
[00:42:01]
only notice later right so having a maybe there just feels like the kind of
[00:42:06]
thing that we should we want to know more at if we can't know it at compile
[00:42:11]
time can we know it at build time can we know it as a build step that analyzes
[00:42:15]
our code static analysis right and in a way the Elm compiler is just a type of
[00:42:20]
static analysis tool so in a way it's not really any different it's the same
[00:42:25]
stage it's like a compile time check it's just another tool to run alongside
[00:42:29]
the Elm compiler so could you do something that I mean it seems like you
[00:42:34]
could pretty easily just write a review rule that checks when you do RegEx dot
[00:42:39]
from string and then you look for instances of that in the AST in your
[00:42:45]
review rule and then you say okay if it's a string literal then take that
[00:42:50]
string call RegEx dot from string with that string yeah and then case just okay
[00:42:57]
good you succeeded nothing hey your RegEx is invalid and then you know that
[00:43:04]
you will never have a RegEx that doesn't work yeah exactly a drew plan on the
[00:43:10]
publishing of packages that does that nice so this is like a bigger topic than
[00:43:16]
just reviewing your code styles it's looking at I mean when you're able to
[00:43:21]
look at the literals in the code base then you can make certain guarantees
[00:43:26]
that you can't with the type system alone you could even have like a
[00:43:30]
positive integer yep type and you could say oh I can say positive integer dot
[00:43:37]
from literal and if I call that my Elm review rule will fail if I call it with
[00:43:44]
anything but a literal or if I call it with a literal that is negative yeah I
[00:43:49]
was going to say that you just stole it for me I was going to say it about the
[00:43:53]
RegEx you could have a create RegEx function that yeah it needs to be passed
[00:43:59]
a literal which is not something that you can enforce in Elm with a compiler
[00:44:03]
yes but yeah force it to be a literal and a literal that is a correct correct
[00:44:10]
RegEx I mean this out it sounds like a rabbit hole that's why I've been
[00:44:16]
spending nine months on this project this sounds like a rabbit hole that we
[00:44:21]
could do like a whole nother discussion on where yes I mean how do you use Elm
[00:44:26]
review to do dependent types wouldn't that be yeah wouldn't that be an
[00:44:32]
attention grabbing headline dependent types in Elm that sounds good to me
[00:44:39]
yeah awesome well thanks for sharing this this is super interesting and I'm
[00:44:44]
looking forward to discussing it more in the future yeah me too so if people want
[00:44:49]
to know more about it so just read the blog posts on my website and there's an
[00:44:55]
Elm review channel so elm dash review on the Elm Slack come talk to me come talk
[00:45:02]
to us in the in Slack and I really hope you enjoy it give me any feedback you
[00:45:07]
have even positive that's helpful for me and let us let us know what you build
[00:45:12]
with it because it's always helpful to see what people are actually building
[00:45:15]
with it and it's great for other people to see examples of what people are doing
[00:45:21]
to get inspiration definitely yeah that would be awesome yeah great well thanks
[00:45:28]
as always this is a lot of fun and talk to you next time yeah talk to you next
[00:45:32]
time