Files
Lee Hanken 7c8efd2228 Initial commit: HPR Knowledge Base MCP Server
- 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>
2025-10-26 10:54:13 +00:00

224 lines
21 KiB
Plaintext

Episode: 2848
Title: HPR2848: Random numbers in Haskell
Source: https://hub.hackerpublicradio.org/ccdn.php?filename=/eps/hpr2848/hpr2848.mp3
Transcribed: 2025-10-24 12:00:52
---
This is HPR episode 2848 entitled Random Humber in Hakell and is part of the series Hakell.
It is hosted by Tuku Toroto and is about 33 minutes long and carries a clean flag.
The summary is Tuku Toroto's how to generate random numbers and other values in Hakell.
This episode of HPR is brought to you by archive.org.
Support universal access to all knowledge by heading over to archive.org forward slash donate.
Hello, you are listening to the Hakell project and this is Tuku Toroto talking about random number generation in Hakell.
Hakell functions are pure meaning that it is data in, data out and when given the same set of parameters you always get the same results you won't get a different response.
And how are you going to have a random numbers in that kind of system?
It's not actually that tricky after you do some digging.
There are two type classes involved here.
Type class is sort of like an interface in the object-oriented programming.
So it defines a set of functions that you can run using the data that you have the instance of a type class for.
So first type class involved here is the random gen that defines three methods.
It's a first one called next.
It has an interface, g, arrow, double of int and g.
And g is in here, the g is the data that you are defining the type class instance for.
So this task is given a random number generator.
If you call next to the random number generator, it will give you a couple where the first item is int and the second item is the new random number generator.
So then there's a chain range that has type instance of g, arrow, double of int, int.
And this is that gives you the range of the values that the generator will again return.
And the last one is split that has a sequence of g, arrow, double of g, g.
So if you have a random number generator, you can split it in two and then you have two distinct random numbers generators.
So, and that's it. That's the source of randomness that you need in your in generating random numbers.
So, how this works is that the random number generator is in this case is a pseudo random number generator.
It will start from some given state.
It will generate a sequence of numbers and that sequence will always be the same.
So, if you have a random number generator, you call next to that same random number generator.
Five times you're going to get the five same results.
So, if you want to use the have a sequence of different types of numbers, you call the, you can call the next.
And then on the first instance of purple where the first item is your random number.
And the second item is a new generator.
And then you call next to that new generator and you get a new random number and new generator.
So, this is how you get around the fact that you will always have the same response to same set of parameters.
But this is a, I mean, this is definitely useful, but it's a bit unwieldy because unwieldy is maybe not the correct word,
but you need to do a bit of arithmetic if you want to better, for example, values from one to six index.
That is, oh, if you want to get BooLands, for example.
But for that, there's another, another type class called random.
So, it has much more defined functions.
So, the first one here is a random R.
So, the definition goes like class random R where, so the A in, A in the following path sequence is that your data that has instance of the random.
So, random R has a signature of random chain G, set arrow, couple of A and A, arrow G, arrow, couple of A and G meaning.
That, meaning that the G in touch signature is something that has instance of random chain that we told just a second ago.
And A is that that has been the instance of random.
So, given a couple of A's and a random chain G, you get a couple of A and G.
So, what does that mean in practice?
This means that if you are called random R and give it a range, you know, as a table from, for example, one to six and the random chain,
you're going to get back an answer.
That is a table, first element is an int from one to six and the second element is a new generator.
Then there's the random, lowercase, this is a function that has a type signature of random chain G, set arrow, C, arrow, couple of A and G.
So, this is the similar one, with the previous one, but you're not going to give a range that you want to value.
You're going to get a, in case of intercase, you're going to get a minimum value speed with minimum and maximum value that the index can hold.
Then there's the random R, which is similar to the first case.
It has type signature of random chain G, set arrow, couple of A and A, arrow, G, couple of, arrow, open bracket, A, close bracket.
Meaning that when you're given a range from one to six and random chain G, then you're going to get a infinite list of random numbers or random values between intertrace in all these numbers from one to six.
And then there's a random, which works is the same with the second case, type signature of random chain G, set arrow, G, arrow, open bracket, A, close bracket.
So, this will, when you're given a random generator, random chain, it will produce infinite list of values for you.
And then there's two, two, two more. These are random R, I, O, that is double of A and A to I, O, of A.
And then there's a random I, O, that is just a I, O, of A.
And these are special cards, but if you're, you probably noticed already that this don't take the random chain as a parameter.
The first one only takes the range and it returns of I, O, E, O, of A.
Meaning that this one, you can call without supply your own random chain.
But you can only call this inside of I, O, monot, meaning in a, you know, effective code essentially.
I'll try to explain those briefly just in a moment.
So, in short, the random chain is our source of randomness, it produces int in some, some big, in some big range.
And then random, then uses random chain, the generated random values of A in manner that is specific for A.
So, if you're working about int, there's no, there's not so much of difference there between random chain and random.
Only the random uses random chain to get some random values.
And then offers you a slightly further interface where you can say that I have 100 numbers from 1 to 6.
But nothing stops you making an instance of random, for example, for Boolean, that exists actually, I think.
And in that case, you're not going to get, you're not going to get numbers anymore, but you're going to get Boolean values.
So, you can say, you can call random R, you can call random IS, supply it with a true and false as a range, and the random chain G, and you're going to get an infinite list of truth and false.
Or you can even call randoms, and just give it your random chain, and you will going to get an infinite list of true and false to infinite list of Booleans, essentially.
Or you can define your own data type, whatever that means, maybe, and make an instance of random for that.
For example, if you had a, I don't know, data that describes curve, for example, it would have a model and make and name, for example.
Then you could, well, actually, that's not a password code, for example.
If you had a, if you had your own data type, four colors, that is, you have four colors, red, blue, green, yellow, then you could make an instance of random for that, and then you could use that to generate values.
So, instead, in your code, instead of having to, I mean, of course, you have to do it somewhere, but in the code that then uses your random instance, you wouldn't have to do it any more in a way that you would generate values from one to four, and then not those results to the colors.
But you could just say that I want random colors, and this, this machinery will make you random colors.
And if you then later on added, added a new color, pink, for example, then you wouldn't have to change any other code, but that single piece of random, random instance definition.
And then all the places they are using, where you are using random to generate colors would automatically work.
So, this, I found that when I realized how this thing works, it was like, this makes so much sense to things this way, what did you know, I think of it earlier.
So, way to get the random, like random journey, yeah, the way to get the random journey, that's a good question, because random is a, you can make an instance of for your own data, or you can use breaks, breaks instances for that.
So, basically, data types for random, but then you still need this source of randomness, the random gen. So, where, where, where you get that form.
There's a function called get std gen that has type of IO std gen.
And that std gen has instances, read show at random gen. The random gen is the one that we are interested in. Read and show why, nice to have.
Because read, read is the way that you can turn string into a correctly forwarded string that is into the std gen and the show is the one that can you, you can use to turn the std gen into the string.
For example, if you want to look what the data looks inside of it, it's just two numbers, but the random gen is the one that we are interested in.
So, we can just call that get std gen, and we get the std gen, but because this is the type of IO, IO std gen, this means that you, you cannot use this area, you can only use this inside of the IO model.
Because if you, if you could create a, if you could create your random number generated anywhere, and use it anywhere, then it wouldn't be really, you, you functions would, wouldn't anymore be returning the same values from the same parameters.
So, a lot of data is effective, effective functions, I mentioned earlier. So, for example, if we have a function called std, that takes two indexes and does something to them returns third index.
This is, this is, this would have a type of int, arrow, int, arrow, int, so two int parameters and int as a result.
And if we have an effective, effective one, it would have a type of int, arrow, int, arrow, IO, IO, int.
So, it would, this, this type of thing, this type of sequence would say that it takes two indexes and it has full access to outside world, but it's not confined inside of the function anymore.
And it returns an int, so it has access to network and it has access to a hard drive and it has access to current time and everything.
So, IO gives you access to that outside world and anything that has type of IO something can only be called from other IO functions.
They can call pure functions, that is functions that don't have access to the outside world, but they cannot be called from the pure functions.
So, IO functions can only be called from other IO functions, but they can call, they can call pure functions, pure functions cannot call IO functions.
And, yeah, so basically you have two options.
So, you have to go that needs random numbers in effective functions so that you can call that get STD send to get your random number generator that is seeded with a, well, it doesn't really matter what it is seeded for.
But it could be a hard time or it could be that if the operating system is offering a standard random number generator.
So, it's seeded with value from there.
Another option is to use the get random trend in effective function and then type it to a pure function and then use it there.
Because after the random number generator has been seeded, then it's not random anymore. It's wholly deterministic.
It will always produce the same sequence of numbers.
It's the seeding part is the thing that needs the source of real randomness of more real randomness.
Randomness is sort of hard to get, absolutely correctly right.
But, yeah.
So, example, this code will be in the show notes.
It is hard, but there's a bit of stuff.
I want to get an unique entries from given list of random in random order.
And if n is created and the length of the list, all items of the list will be written, obviously.
So, our function is called getR.
And it has touch signatures of getR, colon, colon, random gen g, setR, gR inR list of a, error list of a.
So, when you are given a random gen and the amount of items you want to get and the list of items, you're going to get the back list of items.
And the definition, actual definition of this is getR, g, and xs, these are the parameters, equals next line.
FMAP, open parent, xs, exclamation, exclamation, close parent, ids.
So, what does this part do?
This is running up, this is using FMAP to run a function to each and every element in that ids, ids.
And that function is xs, exclamation mark, exclamation mark.
So, double exclamation mark is a function that you use to get an element by index from a list.
So, the left side is a list and on the right side is the index of the element.
Because we are omitting the index here, we are having a partial function, meaning that it's a function that is writing a single parameter.
And then we'll take that in from the list, list, xs, you will get the item of the index.
And this is a sort of, this is a function that can crash.
If you apply your access outside of the boundaries of the list, it will crash.
Oh, I think it will actually throw an exception, but it won't handle those cases.
So, you have to pay attention what you are doing there.
Ids. So, these are coming from today. The definition continues.
Where? Id equals take, open parent, mean, end, dollar, length, xs, close parent, dollar, nav, dollar, random i's, open parent, zero, comma, length, xs, minus one, close parent, g.
That's a mouthful, but I'll try to track it. So, if you start from the right side, random i's, couple of zero to length, xs, minus one, g.
This will give you an infinite list of numbers from zero to the length of our list, minus one.
So, if you have a list of ten items, this will give you numbers from infinite list of numbers from zero to nine.
And it will actually be infinite. So, you will try to come to items on this list or print it on the screen.
It's not time for that. Then there's a nav function that we are going to run to that infinite list of numbers and this.
This will, this will, this card, duplicate. So, given infinite list of numbers from one to ten, or sorry from one zero to nine,
a nav will make sure that there's an infinite list from zero to nine with only, only, only those items won't in the list.
And this is another, another dangerous part because, sort of dangerous, because while it is possible to do that, otherwise I will, we won't be doing doing it here.
If you try to print out that list, it will again take forever, but it will, it will print ten values and then it will try to find the next value.
And it will never find the next value because, just that infinite list of numbers from zero to nine and nav is cropping all the numbers from the list that it has already seen.
So, they won't be next number. It will keep going forever, but they won't be next number.
So, now we have an infinite list of numbers of zero, from zero to nine. Then we are going to use take that has the first parameter,
is minn, dollar length xs. So, it is the minimum, the smallest one of two, so n, that's the amount of numbers we want to take from that list, or the length of the list.
Because, if we have, if we have a, that, that, that, that is, xs list isn't our list of random numbers, it's a, it's the original list that was given to us, that we want to get items from.
So, if we, if, if we have a, uh, ten items on that list, then the length xs will be ten. And if you want to take five from there, that min, will mean that minimum of five and ten.
That's five, so it will, it will return five, but if we are, if we have a list of ten items and we want to take twenty items from there, then it's a min, twenty, then.
And it will, we don't, the value will be ten, because if we don't want to try to take more items from our infinite list, then there is unique values, because that will, that will stay in a way that it will keep going forever.
So, we have an infinite, infinite list of numbers from zero to nine, and then we have the amount of, we want to take from there, and we are using like that is to be using take for that.
So, take, uh, amount of numbers we are creating from that infinite list, and that's our ideas.
And that, that is what we are going to F map over. So now we have a, well, for example, if you are taking five ideas, I have five numbers from our list, from one to ten.
Let's say there are one, two, three, four, five, because some fake occurrence, then we are using the F map, xs, couple x corners mark on the list of one to five.
So, and then the, we are returning a list of those five items.
So it's a, there's a, I mean, it's, it's a short definition. It's just, uh, it's not that complicated, but it's a mouthful when you, when you first see it.
And then, right, there's a couple concepts that were new to me, when I was writing this, and I didn't write it.
This need, need small and need in the, in the first go.
I wrote quite a bit longer definition, and then I, stir, stir it, add it for a bit and started seeing an experimenting on how you can make it a bit more elegant.
Uh, there's a couple of eight, eight cases that I take them for taking care of at the very Lou, the standard library of the Haskell.
For example, if you are applying empty list to this one, if you take, if you try to take elements from the empty list, you automatically get that empty list attack.
No, no matter how many elements you try to take, also if you try to take from a list of five items, you try to take ten items.
You're just going to get five items back.
And another one is that if you try to take a negative amount of items, that for example, take minus one, you're going to get that empty list back.
And how would one call and use about this?
So this one is a pure function, given the same parameters, it will always, it will always return the same end result.
And this is how to use it.
It's a presently for you have a function called test.
It will be test, double colon IO list of int.
And test equals do.
Next line.
Next line.
G arrow left get SDD gen.
Next line return do.
Get R G for open bracket one dot dot can then close bracket.
And that's it.
So it's a successful function, IO function that returns a list of int.
And G arrow left get SDD gen.
Initializes.
Oh, get get your random number generator.
Random gen that we talk in the beginning of episode.
And then get a G for open bracket.
Then dot dot one dot dot then close bracket.
This will call call our get R function and use the random gen that we got previously.
Take four items from the list of numbers from one to then.
And then the return there is to just the rest of the value in the IO monitor.
In the end, that's not it's not that difficult.
And you know what you're doing as always with the programming.
Of most of them in the programming that way.
And I really have really.
I find it really cool that they split the generating randomness in two parts like this.
The random gen that is source is your source of randomness.
And then the random that is your data type specific generation of those random values using the random gen.
So the randomness doesn't require IO person.
So you're getting started, but it's the random random.
After that, it's a deterministic sequence of values.
Simple computations with only few random numbers or infinite sequence of random numbers are easy enough with this.
But if you have a long one where you have more logic and where you're throwing where you are generating more random numbers, more than just one or two.
Then it's a bit of a hassle to keep carrying on that then random number generator.
Always when you create a new random number, you have to remember to take the new random random generator, random gen.
And that's it.
You use that because if you accidentally use the old one, you're going to get the same value again.
But so the long ones where you need more random numbers or more different kind of random values, there's a monotrandom monot for you that basically takes care of that random random gen.
I mean, it is you don't have to care about that.
It is implicitly carried along sort of the computation.
So that's all.
If you have any questions, comments, ideas, any kind of feedback, they're welcomed.
You can catch me on email or at Fediverse.
You can do it at MasterDonted Show.
Okay.
Thanks for listening.
Adopted.
You've been listening to HecopublicRadio at HecopublicRadio.org.
We are a community podcast network that releases shows every weekday, Monday through Friday.
Today's show, like all our shows, was contributed by an HPR listener like yourself.
If you ever thought of recording a podcast, then click on our contributing to find out how easy it really is.
HecopublicRadio was founded by the Digital DovePound and the Infonomicon Computer Club, and is part of the binary revolution at binrev.com.
If you have comments on today's show, please email the host directly, leave a comment on the website or record a follow-up episode yourself.
Unless otherwise stated, today's show is released on the creative comments,
attribution, share a life, 3.0 license.