- MCP server with stdio transport for local use - Search episodes, transcripts, hosts, and series - 4,511 episodes with metadata and transcripts - Data loader with in-memory JSON storage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
349 lines
31 KiB
Plaintext
349 lines
31 KiB
Plaintext
Episode: 3535
|
|
Title: HPR3535: template Haskell
|
|
Source: https://hub.hackerpublicradio.org/ccdn.php?filename=/eps/hpr3535/hpr3535.mp3
|
|
Transcribed: 2025-10-25 01:06:33
|
|
|
|
---
|
|
|
|
This is Hacker Public Radio episode 3535 for Friday the 18th of February 2022.
|
|
Today's show is entitled Template Haskell.
|
|
It is hosted by Tuku Toroto and is about 47 minutes long and carries a clean flag.
|
|
The summary is Tuku Toroto's how she's using Template Haskell to cut out a mount on
|
|
code she writes.
|
|
Hello and welcome. This is Tuku Toroto and you are listening to the Hacker Public Radio.
|
|
Today's episode is about how I'm using Template Haskell to hide some boilerplate code.
|
|
This episode will be quite cold heavy and it will be hard to follow without show notes.
|
|
So if you have a chance please have the show notes at hand.
|
|
I'll try my test to explain the code feels speaking but it will be easy if you can also follow via text.
|
|
I'm using a new type to create a lot of new types in my code and the idea is that
|
|
instead of passing around for example text or intake I give them a further name to tell them
|
|
apart. So I have a planet name and star system name and item amount and such things as a
|
|
pipe. So if I have a function where I pass in person's first name and last name it is
|
|
immediately obvious when you're reading the code which one is which if they were both text
|
|
then you would have to keep track which variable is which but if they are of different types
|
|
then the compiler does that for you. And like I said I'm using new type for that and I'll be
|
|
using a planet name as an example throughout the episode and the new types are just a really quick
|
|
recap. New types are really a way to define a type that is checked during the compile time
|
|
but that is optimized a way during the run time so there's no any performants count on this one.
|
|
And it's a three line to define one there's a for example in case of the planet name there's
|
|
a new type planet name equals M key encore planet name curly brackets and planet name text
|
|
deriving showread EQ and this defines a new type called planet name that's the type
|
|
constructor that is what you what you use when you have to tell a type like when you have to tell
|
|
that for example this value here is of type planet name or this function here accept
|
|
parameter of type planet name it has a data constructor mk planet name that one is used when
|
|
we need to create a value of that type like this this has a value of earth you say that mk
|
|
planet name earth or call this function with value earth and I'm using mk as a prefix to avoid
|
|
naming conflict with the database columns because if I have the data constructor planet name and
|
|
then I have a database table called planet and it has a column name tentat column has a data
|
|
constructor also called planet name and then there will be a naming conflict because you have two
|
|
planet names so you have to keep telling that this planet name comes from here and that comes from
|
|
there and that gets bit tedious so I'm just using mk as a prefix for all of my data constructors
|
|
for the new types and then it has a underscore and planet name that's a name of the field because
|
|
our new type wraps a field so underscore is prefix because of I'm using a lens library and
|
|
prefixing things with underscore let's me to create lenses automatically I will talk about those
|
|
maybe at some later point they are they are really fascinating topic but they are very proud and
|
|
there's actually a whole book written about that so I'm not going to try to squeeze it in now
|
|
and the unprefix comes because we are kind of unwrapping the value and then the last piece the
|
|
double column text means that that unplanet name is of type text and then the last line
|
|
deriving show read eq means that we are automatically deriving some instances show let's me do
|
|
turn our planet name into the plain text and read allows us to read that text back and
|
|
construct a planet name out of that that's handy when you are doing interactive development
|
|
but usually usually that's not that often used and eq gives us access to equality and not equality
|
|
if this is a index then I would also have to order and map instances here they let you do all the
|
|
things which one is created and which one and now gives you a basic numeric operations like plus
|
|
minus taking the absolute value and such okay so that's that's the recap on the new types so basically
|
|
yeah just creating a new type and giving it a name so we can give track of things easier in the code
|
|
and I create those basically whenever I have something called name I'm usually creating a new type
|
|
for that I'm not using text and quite often if I have in value I'm creating a new type for those two
|
|
like if there's a amount of some items or level of something or count of something or such things
|
|
and then for the purposes of my program there's a 1, 2, 3, 4, 5 things that I have to create instances to
|
|
there's a string to change and from change and persist field and persist field SQL and these are
|
|
these are the ones that I end up writing all the time and these are the things that prompt me to
|
|
look into the template SQL. Your string lets me to use string literals in the code so I don't
|
|
have to use mkplanet name earth in the code if I just want to create a name earth I can just
|
|
put the quotation marks earth and the Haskell compiler understands from the context that
|
|
here we are trying to create a planet name without a data constructor so it does that
|
|
behind the scenes to change and from change is for turning our planet name into the change and
|
|
back basically it just creates a change and string that is transferable between server and client
|
|
persist field is for storing and retrieving our planet name into the database and back from
|
|
the and persist field SQL tells the persistent that the that's the database library that I'm using
|
|
it tells what kind what type of field should be created in the database for storing this value
|
|
okay so that's that's like I need to write quite a lot of code to do that all and it gets
|
|
repetitive because it's the same almost same order time and there's a there's an example of
|
|
this implementation like if I let write all this by hand that that is in the show notes if you
|
|
want to have a look it's a I think this 15 lunch or so okay so template Haskell so template Haskell is
|
|
they to generate code via program so you you can have a Haskell function that is executed
|
|
during a compile time it evites some code that is inserted into the source file and then the
|
|
source source file is compiled so it is a bit like least macros for example and at the very very
|
|
easiest lowest level or simplest level you are not you can just have a basically copy paste
|
|
this content as it is to the source source file type of template but with a little bit more
|
|
advanced or complicated you can in you can pass parameters to your template function to guide
|
|
how your how how the code should be generated you can actually even pass code into that template
|
|
and then the template can introspect that code and that generate code that somehow in the
|
|
compile time it seems that it is modifying the pasting code so you could have add some error
|
|
handling for example in your code so how you call this is basically you're going to have our
|
|
function is called make domain type and it takes two parameters so to call it I'll just write
|
|
dollar open back and make domain type planet name as a string and two ticks text close
|
|
barren so we are calling the make domain type function with a two parameters one is string
|
|
planet name another one is name text that's a name of a type called text and I'm going to
|
|
introspect that pasting name a little bit to see what if I if I need to generate the
|
|
hard domain type for int or for the text because there's a one this function handles both cases
|
|
and they need to generate a slightly different code so let's start going through the code example
|
|
there's a first function make domain type takes a string and name as a parameters and return
|
|
a written school list of tech so this tech is the our created code the code that this function
|
|
generates I tried to find out where that navigation or shortening comes from couldn't find anything
|
|
so I'm telling myself that it's a shortening of description it's a description of code it
|
|
probably is a wrong idea but that's the test I could find or come up come up come up with actually
|
|
and q is the monot that we are you that the template has to use is it does a lot of things that are
|
|
not completely sure actually but one of the things that it does it it keeps track of function names
|
|
and variable names actually I think it only keeps track of symbol names but anyway you can ask
|
|
it to create unique identifiers for yourself for you and it will create you always unique ones
|
|
because even if you ask the same same kind of symbol or either identifier files
|
|
it can it keeps track of what has been created and can give you a new one that doesn't conflict
|
|
with the previous one okay so we start by calling rafi function to the that type my name that
|
|
was passed in and rafi is something so it's a function that does the intersection it looks up
|
|
what the what that name is what it stands for and create your variable or description of
|
|
what we what you have so in our case when we passed in a text it will find that okay this text
|
|
is a type constructor and create a respective data structure it can create your information
|
|
about functions and what not and then we are going to do a that and match because there's a lot
|
|
of different cases so and here here you can see what's a a bit annoying thing about type Haskell
|
|
because there's a lot of different kinds of code elements in the Haskell
|
|
and they are represented as a data structures in the template Haskell there's going to be a lot
|
|
of abbreviations so here we are doing the pattern match that case tq of and then this is
|
|
tycoon i and parents so that's a type constructor that we are pattern matching and then there's a
|
|
data D bunch of underscores and D name so these are for because there's a lot of different
|
|
kinds of data constructors even so here we are just making a pattern match to the simple path
|
|
of data constructor as a type constructor that does not have any parameters and then we call
|
|
to select domain name command type for that so if he is the path in name was a simple type constructor
|
|
that doesn't have any type parameters or isn't any higher kind of time type or anything
|
|
then we are calling select domain type and let that to do the code generation for us otherwise
|
|
we are going to use report error to tell that only simple times are supported so if if somebody
|
|
were to call or make domain type and pass in for example a function name then it would during
|
|
the compile time it would signal it would signal you are line where this error happened and it
|
|
would signal you the text only simple times are supported you could even report even more detailed
|
|
error but because this is I wrote this mainly for myself the function I just I'm happy that
|
|
it's just says that you only simple times are supported I that should be enough for me to
|
|
remember what is going on if I were working on a little bit of team then of course it would make
|
|
sense to make a more detailed error report okay select domain type that's a function that takes
|
|
the string name and returns our constructor data code sorry and we are using a card
|
|
code clause clause here so if the f type that's the type in or text if it's a text then we call
|
|
make text domain type and pass in the name of the type we want to create that is a planet name
|
|
in our example if it's an int then we call make into domain type type and give that
|
|
are passed in domain type name otherwise we are again report an error that is an unsupported
|
|
type this is because somebody could call our function and say that I want to create a domain type
|
|
double and that would pass the first check on the previous function and it would be called in
|
|
this check I didn't know I looked a little bit I thought a little bit about how I would handle
|
|
doubles but there's the there's the trouble that the double one in this relative is because it's
|
|
a there's only 01234 and so on and negative numbers so it's easy to represent on the database
|
|
but when you use double then you have to start thinking what the what precision you want to use
|
|
in the database and I didn't want to make those decisions ahead of time so if if I have a one
|
|
double new type in my code that is saved in the database so I figured that I can just leave it
|
|
as it is not used this make domain type function to create that and just write all those instances
|
|
by hand because I have to decide the precision in the database for example but anyway here we just
|
|
see which one which one we are using and because we are using a because we are making a
|
|
planet name that is a type of text or will be type of text we are going to make text domain type
|
|
function okay and that takes a stream as a parameter and returns a description again and
|
|
are actually lists of descriptions and here we have several functions that we are calling one
|
|
one by one to create first one creates the new type definition and then the following ones create
|
|
instances to declarations of instances for that new type I could have generated everything in one
|
|
one big function but then it would have been a big function and it would have been quite unwieldy
|
|
to handle in the long run so I broke it into logical pieces so one creates a new type and other
|
|
ones create instance definitions one by one and then we just in the end take all those definitions
|
|
and then we can create them together they are lists so we end up one big list of code and
|
|
redo them that and that gets spliced into the source code in the compile time and some of those
|
|
functions are general like the new type definition it's almost identity identical between the
|
|
int and text so they are passing an extra parameter to tell that I want a text new type
|
|
and some of those like make is string instance is specific to the text there's no point
|
|
of making a is string instance for the int so that can that that term need an extra parameter
|
|
and to make new type definition this is the function that starts generating a code for us and
|
|
this is there's two ways of creating a code of generating code in the template Haskell1 is
|
|
creating a a st by hand like abstract syntax tree by hand and another one is using
|
|
code lessons and I like using quotations a lot more because
|
|
there you don't have to fight that much of code you basically fight your
|
|
code that you want to generate as a v's and then you can splice in
|
|
variables of variable parts there but with the abstract syntax tree you have to actually create
|
|
the syntax tree by hand and it gets tedious but for some reason I couldn't get the new type definition
|
|
working with the quotals quotations so I have to use the a st method here and
|
|
well now you get to see the both ways of doing that so here we are returning a list
|
|
of descriptions and the first one is a new type d that's a data constructer that creates a
|
|
new type and it takes a bunch of parameters so first one is in the documentation referred as a
|
|
CXT it's related to the confluent kind but that's a feature of the Haskell that I'm not familiar with
|
|
so and I figured out because I'm not familiar with that I can just give in a empty list there
|
|
I'm not using constraint kind so I'm using a giving an empty list there so open bracket close bracket
|
|
next is a name of the new type we are generating this is a and this has to be a correct type
|
|
so I'm using mk name to turn our string into the name then the 10 is a list of type variable
|
|
findings that we are not using so empty list again then there's a maybe kind that I'm not
|
|
again quite sure what it does so I'm passing nothing there we are not using kinds there
|
|
this is that like very very reassuring when you're generating code and you are going like I don't
|
|
know what this is I don't know what that is so I'll just pass in I'm not using that but such a slice
|
|
I tried to look into some of those things but it started looking like I would have to do quite a
|
|
bit of reading to learn those and decided that I can do that at some other point okay and then
|
|
the after the client yeah then there's the con that's a data constructor we'll cover that just
|
|
in a moment because that's the meat of this thing that's the line that starts with wreck
|
|
and the last one is derive closer that's a list that of where you can automatically derive some
|
|
instances like show read and e2 in our case and we are using a function called
|
|
derive close for new type I'm sorry yeah yeah using the derive close for new type function
|
|
for deriving that I'll go out that in a little bit later it's a handy little thing I wrote
|
|
but then the data constructor wreck so all these up to this point while this is a parameter for
|
|
the new type the data constructor that creates a new type and this wreck is a annotated data constructor
|
|
that creates a record type because new type has a record type inside of it it's a it's a three
|
|
after all so it tells that it has a data constructor and it has a field like it's a record but it has
|
|
only one in our case so wreck an mk name is the first for the wreck the first parameter is the name
|
|
of our data constructor and we are using mk name function to create a name and we are taking that
|
|
planet name as a string an appending mk in front of that so this is what creates mk planet name
|
|
data constructor for our new type this this function called and the next line is is a list of fields
|
|
the record record holds and because we are using a new type we only have one one field there
|
|
and the first one is a first parameter it's a double and the first element in the double is the name
|
|
of the field we are using mk name again and we are appending underscore un into our planet name
|
|
string so this is where we got the underscore underscore un planet name as a name of our field in the
|
|
in the record and then controls two things it controls the source and darkness that I don't know what
|
|
it does I try to look into that completely without and then the source strictness that controls
|
|
when the value should be evaluated or computed should it compute that immediately or only when asked
|
|
default is only when asked that's how high scale is working but sometimes you have to
|
|
instruct high scale that don't don't don't delay computing of this value until it is needed but
|
|
computed immediately because while while you cannot get stack of flows in a Haskell you can run
|
|
out memory quite easily well not quite easily but sometimes you can run out of memory or you can
|
|
get a space leak very simple looking program consumes tons and tons and tons of memory and that
|
|
is related to the evaluation of the Haskell program how it is computed when the values are
|
|
computed and one way of controlling that is controlling when when the value should be evaluated
|
|
with the source strictness but we are we are just no source using the no source strictness
|
|
so using the defaults here and the last one is a pipe of the of the field in the record so
|
|
quantity is a constructor at the data constructor for the type constructor that sounds confusing but
|
|
we are creating code that says that this is this type constructor is a type that is the value that
|
|
was passed in so you know always it's a text so that's a quite lot of the right and keep track of
|
|
and it's quite a lot of when you are getting started to look into like trying to find all these
|
|
recti conti then new type t all these constructors it's it's quite tricky to do I didn't I didn't
|
|
enjoy writing that that piece of code that much so there's a type template Haskell documentation
|
|
page that I had I had open all the time while I was doing that and going back and forth
|
|
and trying tracking what needs to be there and because then you are writing anything bigger
|
|
remember this piece piece of code creates three lines of Haskell code that actually could be
|
|
just a one liner but it's a lot to keep track and if you have a more complicated code
|
|
then you are going to get more and more nested ASD the abstract syntax tree so and it is hard
|
|
to read afterwards so I mentioned that derived clause for new type function that we are using to
|
|
create that derived clause that so that's a function that I wrote by myself and that again just
|
|
does checks what type we want to handle is it text or int and then it just
|
|
used a returns a list of type constructors in a in case of text it returns show read and
|
|
ego and in case of int it returns show read ego or numb and if somehow we had gotten this
|
|
for with some other type it will again report an error like it's an unsupported type it shouldn't
|
|
happen but it could could have it to write your functions in a way that they handle every possible
|
|
case so we report an error and how we do this is basically we have a list of strings and then we are
|
|
using a function that we created by using function composition for the conti and mk name so that conti
|
|
dot mk name can be read as a conti of mk name so we are taking two functions and packing them
|
|
together and this creates a new function that when you give it a string it uses mk name to turn
|
|
that to string to the name and it's that result in immediately to conti that uses that name to
|
|
create a type constructor and then we use the smaller than dollar created and that's our
|
|
application we are using that to call that this newly created function to each element in the
|
|
list so we use that function to we we call that function to show and then to read and then to
|
|
make you and return the all the created values in a list so we get a type constructor show type
|
|
constructor read and type constructor for ego and uh okay then test the make is a string instance
|
|
this is only for the in case we are creating a text domain type and here you can see the difference
|
|
between the constructing the code with the abstract syntax tree or quoting because here we are
|
|
doing open bracket d pipe bunch of code pipe close bracket and this means this is a quotation
|
|
so the text inside of the quotations is not treated as a it's not treated as a code in a
|
|
way that it's not executed when this function is called but it is treated as a text that is turned
|
|
into the code and returned as a AST so we can just write what we want to do and Haskell Haskell
|
|
will take care of turning that into the AST this is a lot easier a lot nicer way of doing
|
|
and then there's a part that we need to worry for example we need to create is string is string
|
|
instance to a planet name and we are passing in a planet name so we are just using that dollar
|
|
open parent con t dollar mk name name close parent here we are first calling mk name to that planet
|
|
name string and turn that to a name planet name and then we are using con t to turn that name
|
|
into a type constructor so then when this part is done this this line reads instance is string
|
|
planet name where so that we are executing a tiny piece of code and splicing the result into
|
|
the quotation this is this is how list does things this is very very similar how list does things
|
|
if you are familiar with the list and then on the next line there's the from string function
|
|
again we are splicing more code there and here this con e mk name mk plus plus name
|
|
what what is that it takes the it takes the planet name string and pretend that with mk so we get
|
|
mk planet name turn that turns that string into a name and turns that to the expression
|
|
so we are calling a function a type data constructor mk planet name and then there's the dot from
|
|
string but in the end so that's the function composition so what we are doing is first we are
|
|
calling from string and what that returns are fed to the mk planet name data constructor
|
|
sorry and this is this is what is used to turn spring literals into the into the planet name
|
|
types and this is like I said this is a lot a lot nice a lot easier way of doing things
|
|
not so much of coding and you can a lot easier follow what is what is being done
|
|
and in the same way we are dealing with the the test that make to change an instance and make
|
|
from change an instance for text they are working in the working in the pretty much in the same way
|
|
as the make is string instance function the general code is a slightly different but the I mean
|
|
completely different but the idea behind is the same so we have we have a quote that has the
|
|
code and then we have the then we are splicing in the variable parts the parts that we need to
|
|
var i var i okay uh person feel instance so we have a function make person feel instance for text
|
|
because there's a two implementations there's one for the text and one for the int that's
|
|
likely different and this instance has three functions so if I were to generate that with the
|
|
ASD approach it will be a quite a handful of code and while the here the idea is the how you
|
|
generate the code is the same but here here the generated code is somewhat more complicated
|
|
so for example test this line const path equals config const name open bracket var p mk name s
|
|
closed bracket so this creates a pattern that we use for pattern matching so and that const path
|
|
value is used in the two-persist value function so when two-persist value is called with a pattern
|
|
that is mk planet name s we handle it the s now contains the text that is we want that the system
|
|
wants to persist and that's the how the persistent takes care of it's the persistent leverage task
|
|
put in that now but then when the two-persist is called with the mk planet name s which
|
|
was take the planet name new type apart so that we can have access to the to the underlying text
|
|
that is stored in the s that's why we are using the pattern matching here and from
|
|
persist value this is even more interesting case here here we are constructing a pattern p text but
|
|
this time it's a is a persist text s so then this from persist value is called with a
|
|
value that is persist text s so the system has loaded a value from the database that is persist
|
|
text so it's a text column in the database and chords this then we are constructing a
|
|
our planet name and passing that s as a parameter and returning that as a right value
|
|
the the from persist value function returns either so it can be right when everything
|
|
works well fine it's this case so right mk planet name s but then there's the second line from
|
|
persist value underscore this is a pattern match that matches to everything that has not been matched
|
|
so far so if we call the from persist value with the persist text then the first function takes
|
|
takes care of that but if we call it what for example persist plot so somebody has loaded the binary
|
|
plot from the database and tries to turn it into the planet name we return left fail to deserialize
|
|
so we are signaling an error that this we cannot deserialize this value and then it's the persist
|
|
libraries persistent libraries task to handle that error so it probably just traces and
|
|
error somewhere and says that there's a problem and we cannot we cannot turn this binary plot into the
|
|
planet name because because when we are when we are saving the data in the database we have a
|
|
we have a complete control of what might we have at hand but when we are loading the data from
|
|
the database we don't we don't have full guarantee that we are actually that somebody hasn't edited the
|
|
database by hand and changed the column definition for example or if the husband back and the column
|
|
definition does not what the code says it should be that's why we are that's why we have to be
|
|
paranoid and tell what's going on and the make persist field SQL instance function that creates the
|
|
instance for the sorry persist field SQL and that is a that is a fun instance that tells to the
|
|
our database library what should be the type of the backing field in the database so here we are
|
|
just saying that we are using the select backing SQL that function that I wrote to select
|
|
depending on the type that we are creating if it's a text then we are creating SQL string
|
|
data constructors and if it's in then we are creating SQL in 64 data constructors this
|
|
this will indicate to the our library what is the type of the backing field and when the
|
|
the periscence is actually really really nice when you when you start the program and you don't have
|
|
the database at all it will automatically create that for you and if you it will use this
|
|
information to create your correct type database column types
|
|
okay so that's a lot of a lot of code reading code allowed and especially the part that makes
|
|
the new type definition is a bit of a pain to work with even even when you know what it is
|
|
supposed to do if you come into the code and you have no idea what's going on here probably is
|
|
kind of kind of puzzle to figure out so that's the that's the that's the thing like
|
|
with this system I can with a one line create a new new type and associate that instances
|
|
and that will cut a amount of code that I have to write and maintain down quite a bit
|
|
imaging that I have made a mistake somewhere and I have a back there's only one place I need
|
|
to fix it and it it is automatically propagated everywhere where this is used the draw side is
|
|
of course that if I have back then that's propagated everywhere where this is used but at least then
|
|
it's hopefully easier to find and you can have a higher abstraction level you don't have to talk
|
|
talk about new types and how it's saved into database and how it's turned into the string and
|
|
what not but you can just say that okay I have a domain up here and we know that this can be
|
|
saved into database so no need to worry about the details and so it's really nice when it's
|
|
working and when it's not working then you have to start debugging and that's a that's about
|
|
annoying thing to do one one thing is that if it generates incorrect code then then the
|
|
it might be a bit tricky to find out what's going on here not because you you cannot see it
|
|
you don't normally see the generated code but the compiler has a flag ddump slices and ddump to file
|
|
that lets you it instructs the compiler to generate temporary files
|
|
where it shows that in this file the following template has the function was run and this was the
|
|
result and that is that is useful when you are trying to debug the problems that generate code
|
|
so ddump splices and ddump to file
|
|
and okay I guess that's it it's a long enough episode already anyway so if you have any any
|
|
questions comments or feedback you can catch me by email or facebook and to do it at tech.lgpt
|
|
or even better record your own episode and let us know what you like or hate about template
|
|
haskell or if you are using a list you could talk about how list macros are done because
|
|
they are bit similar but they are bit different and I personally I think list macros are much
|
|
nice at work with but that might be because I've been working with those quite a bit more than
|
|
with the template haskell this is the first ever template haskell I ever wrote
|
|
so I probably should have said this in the beginning but a bird of warning if you are trying
|
|
to figure out how template haskell works and what are the good ideas and good buttons
|
|
this episode might not be the most trustworthy source of that information because this is the first
|
|
template haskell code I have ever written so there might be something funny going on here
|
|
but it should give you an idea what you can do with the template haskell it's not nothing else
|
|
okay that's it I'd ask for a
|
|
you've been listening to Hecker Public Radio at HeckerPublicRadio.org today's show was
|
|
contributed by an HBR listener like yourself if you ever thought of recording a podcast then click
|
|
on our contribute link to find out how easy it really is hosting for HBR is kindly provided
|
|
by an honesthost.com the internet archive and our sync.net unless otherwise stated today's show
|
|
is released under a creative commons, attribution, share like, flea dot o license
|