233 lines
14 KiB
Plaintext
233 lines
14 KiB
Plaintext
|
|
Episode: 2888
|
||
|
|
Title: HPR2888: Pattern matching in Haskell
|
||
|
|
Source: https://hub.hackerpublicradio.org/ccdn.php?filename=/eps/hpr2888/hpr2888.mp3
|
||
|
|
Transcribed: 2025-10-24 12:47:03
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
This is HPR episode 2008-188 entitled, pattern matching in Haskell, and in part on the series, Haskell, it is hosted by Tuku Toroto, and in about 21 minutes long, and Karima Clean Flag.
|
||
|
|
The summary is, Tuku Toroto talks about one of their favorite features in Haskell.
|
||
|
|
This episode of HPR is brought to you by an honesthost.com.
|
||
|
|
Get 15% discount on all shared hosting with the offer code, HPR15, that's HPR15.
|
||
|
|
Better web hosting that's honest and fair at an honesthost.com.
|
||
|
|
Hello, you're listening to The Hague Public Radio, and this is Tutu Toroto talking about pattern matching in Haskell.
|
||
|
|
This is one of those features that when I saw in action, I was completely sold pretty much immediately.
|
||
|
|
So, the basic idea is that if value constructors are for making data, then the pattern matching is taking that data apart roughly.
|
||
|
|
So, simple example.
|
||
|
|
We have a Boolean, type Boolean, that has two value constructors.
|
||
|
|
True and false.
|
||
|
|
True makes a Boolean and false makes a Boolean. They have different data in them.
|
||
|
|
So, then if we have a function that takes a Boolean as a parameter, we can match against that value constructors.
|
||
|
|
If something, we have a function called Boolean string that takes a Boolean and returns a string.
|
||
|
|
And we could write it in a way that it uses if inside of it.
|
||
|
|
So, Boolean string n equals if n, then Quotation, true and Quot, else Quot, false and Quot.
|
||
|
|
So, when you call it with a true, it returns true as a string.
|
||
|
|
And with a false, it returns false string.
|
||
|
|
And the choice was made by the if statement inside of the function.
|
||
|
|
We can remove that if statement by doing pattern matching on the parameter.
|
||
|
|
So, the thing that is the same, it's still pooled string.
|
||
|
|
But it is written as pooled string, true equals Quot, true and Quot.
|
||
|
|
Pooled string false equals Quot, false and Quot.
|
||
|
|
So, it looks like there's two definitions of the function.
|
||
|
|
One for the true and one for the false parameter.
|
||
|
|
Actually, but in reality, there's a one definition, but two cases.
|
||
|
|
You can have more complex data in the pattern, but it still works in the same way.
|
||
|
|
So, for example, if we have a function called e-speak that is given a maybe int and it returns a string.
|
||
|
|
So, maybe it's the data type that has two value constructors.
|
||
|
|
It has nothing and maybe a.
|
||
|
|
So, nothing signifies that there is no data available and just a indicates that there's a value available.
|
||
|
|
And a is whatever type we have declared, declared it to be in our case, it's the int.
|
||
|
|
So, we can match those two value constructors and write it as e-speak, nothing equals Quot, not at all and Quot.
|
||
|
|
e-speak, open pattern, just one, close pattern equals Quot, just perfect and Quot.
|
||
|
|
And e-speak, open pattern, just n, close pattern equals.
|
||
|
|
If n is less than 10, then Quot, just lightly, and Quot, L is Quot, definitely is end Quot.
|
||
|
|
So, we have three cases, nothing that is covered by the e-speak, nothing equals.
|
||
|
|
And that will handle the case when you call this with nothing.
|
||
|
|
Then there's a e-speak, just one.
|
||
|
|
This will handle the specific case, then we are calling this, and the parameter is just one.
|
||
|
|
And then there's a more general case, e-speak, just n.
|
||
|
|
This will match the case that we have, even match all the rest cases.
|
||
|
|
So, n will capture the value that was given to the chest, and it will be available as a named parameter, named value inside of the function.
|
||
|
|
And order where you define this is important.
|
||
|
|
And if we had that two of these last ones, switch places, we would define just n before the case, just one.
|
||
|
|
Then the just n would take, would trigger when we expected that just one to trigger.
|
||
|
|
So, the program will call functions, prove them in order to find each one, the first one that matches.
|
||
|
|
So, this is an example how to do it.
|
||
|
|
How to match to some value of constructor and catch a data that was used.
|
||
|
|
Use to construct the data into a parameter.
|
||
|
|
So, e-speak, open-paren, just n, close-paren, e equals, and then the definition.
|
||
|
|
The pattern matching isn't limited to algebraic data types, those examples that we went through before, for earlier.
|
||
|
|
But it works for records to records itself.
|
||
|
|
Data types where we have one value constructor, and then there's a multiple one or more records, sorry, fields with names.
|
||
|
|
So, if you haven't been following the show notes, now it will be a good time to go ahead and do a few things.
|
||
|
|
This might be a tricky to follow.
|
||
|
|
But, to be clear, I'll record, we will, for example, this could be a customer record.
|
||
|
|
Open-paren data, customer, e equals, customer.
|
||
|
|
Open-procket, curly-procket.
|
||
|
|
Customer name, double-colon string, comma-customer-discount-percent, double-colon-table, comma-bid-customer-table-colon-bore-close-curly-procket.
|
||
|
|
So, now we have a type, customer, that has one algebra constructor, also called customer, that has three fields.
|
||
|
|
Customer name, that is string, customer-discount-percent, that is double, and with customer, that is poor.
|
||
|
|
So, whenever we have a customer, we know that it has these three values available.
|
||
|
|
Then we have a function called total fee.
|
||
|
|
This is a made up example, that calculate what a total fee is based on the cost and the discount percentage.
|
||
|
|
First, we have a special case, total fee, bill-cost-it-open-paren-customer-open-curly-tracket-with-customer-equals-through-close-curly-fracket-close-paren-equals.
|
||
|
|
And then, inside of the definition, bill-times-sylip-on-mime-times-customer-discount-percent-cast.
|
||
|
|
So, there's a lot of coin on here.
|
||
|
|
So, the total fee is taking two parameters.
|
||
|
|
First one, bill-is-a-table.
|
||
|
|
Second one, cost-over, we are matching that to a case where the rib-customer record is true.
|
||
|
|
If that record, if that field is false, this definition won't get called.
|
||
|
|
So, this one will only be called when the rib-customer is set true.
|
||
|
|
And then, we are using that cost at the beginning to catch the value of the customer into the parameter named cost.
|
||
|
|
And then, we can use those values inside of the function to multiply the bill-times-sylip-on-mime-times-customer-discount-percent-cast.
|
||
|
|
Regular case for the function is defined as a total fee, bill-cast equals bill-times-customer-discount-percent-cast.
|
||
|
|
Meaning that if the rib-customer is false, this will get called.
|
||
|
|
And we are going to just multiply the bill by the discount percentage.
|
||
|
|
There's no extra discount that is given to the rib-customer.
|
||
|
|
Okay, then, the special case list, take-and-tip-hatamets-2.
|
||
|
|
I think I showed this.
|
||
|
|
I only used this when we were talking about the records on the way back, but didn't explain that closely.
|
||
|
|
So, we can have a...
|
||
|
|
We were doing the matching the customer for example,
|
||
|
|
a lot of constructors of the Boolean.
|
||
|
|
We can match in the same way list.
|
||
|
|
And the syntax is that if you have a list of at least one item that you want to match,
|
||
|
|
you would just find it as an open-paren-x-colon-xs-close-paren.
|
||
|
|
The names of variables aren't important.
|
||
|
|
What is important is that colon in the definition.
|
||
|
|
So, in this case, x will be the first item of the list and xs will be the rest of the list.
|
||
|
|
There might be a zero item there, then it's an empty list,
|
||
|
|
or there might be a Hunos-how-many item there, but it's a list.
|
||
|
|
If you want to match a list that has at least two items,
|
||
|
|
we could find it as an open-paren-x-colon-y-colon- underscore-close-paren.
|
||
|
|
Now, x is a first item, y is the second item,
|
||
|
|
and that underscore is the rest of the items.
|
||
|
|
But we are not interested in using those.
|
||
|
|
So, we are matching them with the underscore.
|
||
|
|
That's a wild card that matches anything,
|
||
|
|
but it doesn't find it into the name.
|
||
|
|
Now, we have access only the two first items of the list,
|
||
|
|
of the list, and the fifth vegan of the access.
|
||
|
|
And, of course, if you want to match completely empty list,
|
||
|
|
there's an open-pricot-close-pricot that matches the empty list.
|
||
|
|
So, list of exactly one item would be an open-paren-x-colon-open-pricot-close-paren.
|
||
|
|
Because now, we are matching a first item, it matches to a one item,
|
||
|
|
and then it matches to the empty list.
|
||
|
|
So, list where there's a head, and the tail is empty list.
|
||
|
|
And, like I said, the underscore- underscore match has to everything,
|
||
|
|
but it doesn't point the value to a name.
|
||
|
|
And, where items to use this is when I need to match,
|
||
|
|
I mean, where I don't care, value of something,
|
||
|
|
then I'm using underscore.
|
||
|
|
I could, equally, use some made-up name there,
|
||
|
|
like N, or anything,
|
||
|
|
but then, if I'm not using that name in the function,
|
||
|
|
then the compiler will assume you're warning that you have a value
|
||
|
|
that is not being used.
|
||
|
|
And, I think that's a good idea,
|
||
|
|
because that might catch some bugs,
|
||
|
|
or thought errors, or what you call those.
|
||
|
|
So, I don't, either I aim to try to only declare variables,
|
||
|
|
or values that I actually need,
|
||
|
|
and if there's something that I don't need, I use underscore matches.
|
||
|
|
So, for example, we could use that list matching
|
||
|
|
to count how many items there are in the list.
|
||
|
|
And, we would define that as the signature would be count,
|
||
|
|
the bulk colon, open, bracket A, closed bracket, arrow int.
|
||
|
|
This means that our count function takes a list,
|
||
|
|
takes a list of anything,
|
||
|
|
hence the analog SA, and returns an int.
|
||
|
|
If this one can take a list of integers,
|
||
|
|
list of strings, list of moulins, list of anything.
|
||
|
|
And, the definition for this one is count,
|
||
|
|
open, bracket, closed, bracket equals zero.
|
||
|
|
So, when we call our count with an empty list, it returns zero.
|
||
|
|
And, then, the network case, count, open, turn, x colon,
|
||
|
|
x colon, x-s, closed, turn equals one plus count, x-s.
|
||
|
|
So, what this does is that it takes the first element of the,
|
||
|
|
our list, and then takes a count of the rest of the list,
|
||
|
|
and acts one to that.
|
||
|
|
So, it will call count again,
|
||
|
|
and again, and again, until it reaches a point
|
||
|
|
where the empty list is handed to it,
|
||
|
|
and in that case, it returns zero,
|
||
|
|
and then it will add up all those ones together.
|
||
|
|
So, this is one way of counting elements on a list.
|
||
|
|
You can also do a common example is a Fibonacci.
|
||
|
|
That's a sequence of numbers where every number is defined
|
||
|
|
as a sum of two previous numbers.
|
||
|
|
And, the first two ones are zero and one.
|
||
|
|
So, the sequence calls cause something like zero, one, two, three,
|
||
|
|
five, eight, thirteen, and so on.
|
||
|
|
And, we can have it as a different as following.
|
||
|
|
We have a signature Fibonacci.
|
||
|
|
The focal length int are all int.
|
||
|
|
So, you give int, and you get int back.
|
||
|
|
And then the actual definition is Fibonacci zero equals zero.
|
||
|
|
Fibonacci one equals one.
|
||
|
|
These are the two first numbers.
|
||
|
|
So, when you call it zero, you get a zero,
|
||
|
|
and when you call it with one, you get a one.
|
||
|
|
And then there's a general case Fibonacci n equals Fibonacci n minus one
|
||
|
|
plus Fibonacci n minus two.
|
||
|
|
So, when you call it with any other number,
|
||
|
|
that a number that is a zero, one, it goes to this central case
|
||
|
|
and goes itself twice and acts up the numbers together.
|
||
|
|
Here's a slight problem that if you call it
|
||
|
|
with a for example minus one, it will never, never, ever terminate.
|
||
|
|
It will just run until the, until, well, actually,
|
||
|
|
it will run until you exceed the range of the integrals.
|
||
|
|
When you get a big enough number, that it will issue a random error.
|
||
|
|
But here, here there's a problem that if you call it with a negative number,
|
||
|
|
it will keep running.
|
||
|
|
But with positive numbers, this one works.
|
||
|
|
And so far, they have been doing pattern matching on function parameters.
|
||
|
|
There's a case expression that does the same thing, but inside of a function.
|
||
|
|
But the truth for that are exactly, exactly the same.
|
||
|
|
You can do exactly the same pattern matching tricks that were shown above,
|
||
|
|
earlier, but with inside a function.
|
||
|
|
For example, if we wanted to define our Fibonacci
|
||
|
|
using the case, it will be, the other signature will still be the same,
|
||
|
|
but the definition will be Fibonacci n equals case n of 0 arrow 0, 1 arrow 1,
|
||
|
|
n arrow Fibonacci n minus 1 plus Fibonacci n minus 2.
|
||
|
|
And that's it.
|
||
|
|
So the case expression works in a way that there's a case.
|
||
|
|
But what you're matching against off and then the matches underneath.
|
||
|
|
And every after every match, there's an arrow with a value that that case will produce.
|
||
|
|
And even here, if you miss a case, the compiler will helpfully complain to you
|
||
|
|
that this match isn't exhaustive.
|
||
|
|
For example, if we forgot the last case of the Fibonacci example,
|
||
|
|
and match it only to the 0 and 1, the compiler will say that you are missing the.
|
||
|
|
I don't actually remember exact wording, but it would say that you are missing a case
|
||
|
|
where you, that is different than 0 and 1.
|
||
|
|
Which is pretty handy that it even detects that you are not matching to all indexes.
|
||
|
|
So the advantage of this is that you can remove if statements that, well,
|
||
|
|
that's a subject maybe, but I like that there's a, the less there is an if statement,
|
||
|
|
the better in my opinion, because I mean, you still,
|
||
|
|
of course, you need to do the choices, but every time you have an if statement,
|
||
|
|
it has a print and then you have a, for example, if you have a two consecutive if statements
|
||
|
|
in your code, that means that there's a full tax through that code.
|
||
|
|
And if you have three, then you have a eight tax through that code.
|
||
|
|
And you get really complicated to figure out what's going on and how everything plays together.
|
||
|
|
Having those in the pattern matching, custom trim, remove the complexity,
|
||
|
|
but it makes it linear.
|
||
|
|
It's easy to see that with this case, you're doing exactly these things.
|
||
|
|
Another advantage is that compiler will help you to catch the cases where you are,
|
||
|
|
where you missed a match.
|
||
|
|
Like there's a case that you haven't handled.
|
||
|
|
Okay, that's all about this.
|
||
|
|
If you have any questions, comments or feedback, tell me,
|
||
|
|
I'm the best way to catch me if, no, there's either email or in Fediverse,
|
||
|
|
where I'm to do it at most, but that's the social.
|
||
|
|
Thanks a lot.
|
||
|
|
You've been listening to Heckapublic Radio at HeckapublicRadio.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 HBR listener like yourself.
|
||
|
|
If you ever thought of recording a podcast, then click on our contributing to find out how easy it really is.
|
||
|
|
Heckapublic Radio was founded by the digital dog pound and the Infonomicon Computer Club,
|
||
|
|
and is part of the binary revolution at binwreff.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 under Creative Commons,
|
||
|
|
Appropriation, ShareLight, 3.0 license.
|