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
