If this post got you interested in learning more about Haskell and you want to see type classes in some real code, a great starting point for Haskell is IHP :)
IHP is a new Haskell framework with a focus on actual building applications. Imagine the productiveness of rails combined with the typesafety of Haskell.
It's now already the biggest Haskell web framework, we just hit 3200 GitHub stars. I belive Haskell can reach a lot more than it's currently doing if we get things easy to use.
I'm not sure what this adds over LYaH. Possibly it is useful because you have to survive until LYaH Chapter 8 for "Typeclasses 102"? [1]
The basic post was fine I didn't see any lightbulb moments. It works through an example showing how to add your own data type to Eq and Ord.
The later post, "What's That Typeclass: Monoid" could have been interesting. To me it went to mathematically-abstract. Like every other Haskell post.
Example quote: "...because a semigroup is a monoid without an identity element..." To be fair, they have defind SemiGroup, but really -- who is the audience for this?
This is good for a laugh though:
> Why do you need monoids?
> In general, monoids are not very advanced, interesting, or cool.
To compare with Learn You a Haskell, I wanted to provide a different option / way of grasping the concept with, hopefully, examples that are relatable to some people more than LYAH. Having a few different explanations of the same thing is not a bad thing in my eyes since different things click with different people.
Regarding Monoid article -- it is sad to hear that since my goal was to provide something that would be unlike the "mathematically-abstract" posts. It has plenty of examples and a rather long exposition, and I am not sure how much more it is possible to improve it without losing any meaning for the word "monoid".
Semigroups and monoids is a rather awkward moment there, I didn't manage to find a perfect way to cover that. But, to be honest, I did mention what a monoid is plenty of times, and I did mention what an identity element is several times as well. So a careful reader should be able to imagine what a semigroup is if necessary.
Anyway, thank you for reading the articles and pointing my attention towards that. I will for sure look at that part again. :)
I liked the text about typeclasses.
It is a standalone piece, "Typeclasses 102" in LYaH is a part of a chapter of a bigger piece.
I think you achieved your goal - your Pokemon example is simple to grasp and shows how to derive type classes and implement functions by hand:)
Monoids are pretty cool in theory, and at the same time, the tool I use least of when working with Haskell.
This page [0] really shows how elegant Monoid comprehension would be when implementing SQL-like operations within pure Haskell code. (Goes over desugaring, implementation details and comparison to list/monad comprehensions, just jump down to examples)
Funny enough, monoids/semigroups are one of my favorite parts of Haskell and I really miss having an explicit abstraction for them in other languages.
A lot of the code I write is looping over all of the values of a data structure to either look for a certain condition being true (any/all boolean monoid), look for an extremal value (min/max semigroup), or combine all of the values in the data structure (sum/product/average monoid). The product of a monoid is also a monoid, so if you want to do two or more of those operations at once, the code remains simple and orthogonal while only traversing the structure once.
In particular, all of the those operations are nicely expressed as one-lines using foldMap (https://hackage.haskell.org/package/base-4.9.1.0/docs/Data-F...). If you have a data structure called `xs` and each element in data structure has an integer field called `size` you can do the following by using foldMap with different Monoid instances.
Sum the sizes:
let (Sum total_size) = foldMap (\x -> Sum (size x)) xs
Check if any size is greater than 5:
let (Any over_threshold) = foldMap (\x -> Any ((size x) > 5)) xs
Do both in one pass:
let (Sum total_size, Any over_threshold) = foldMap (\x -> (Sum (size x), Any ((size x) > 5))) xs
Get the first entry with a size greater than 5:
let (First over_threshold) = foldMap (\x -> First (if (size x) > 5 then Just x else Nothing)) xs
Get all entries with a size greater than 5:
let over_threshold = foldMap (\x -> if (size x) > 5 then [x] else []) xs
Since all of these operations are associative, we can completely change the data structure or run the operations in parallel or concurrently and the code will still behave exactly the same. These nice properties mean that when I'm thinking about these sorts of tasks in any language, I think about what monoid or semigroup captures a given operation, rather than any of the mechanics of writing the loop. I find clarifies my thinking a lot.
I may be way off on this, but I like to think of them as a bridge between ad-hoc and parametric polymorphism. Or maybe an ad hoc wolf in parametric sheep's clothing.
Functions and types that refer to typeclasses are parametrically polymorphic insofar as they don't refer to a specific implementation of that type. This is quite different from run-of-the-mill function overloading, where you must supply separate implementations for every set of types you want to support.
But, unlike how parametric polymorphism works in OOP, late binding is not an option. At the use site, the typeclass implementation you want to use must be statically resolvable. In that respect, it does behave like function overloading.
Wouldn’t it be helpful to use the word “interface” in the article (and other docs) since that’s basically what they are known as in other languages (or “trait” in Rust and Scala). I’m sure we can have a really long thread on the difference between these concepts in different languages but wouldn’t it help beginners to be told that those are more or less the same thing?
Very well written, but an introductory tutorial shouldn't ignore fundamental difficulties like multiple conflicting instances of the same typeclass for the same type.
Your example would be a good fit, since Pokémon can be "naturally" ordered by number, by name, by which one has more types, and in infinite other ways.
IHP is a new Haskell framework with a focus on actual building applications. Imagine the productiveness of rails combined with the typesafety of Haskell.
It's now already the biggest Haskell web framework, we just hit 3200 GitHub stars. I belive Haskell can reach a lot more than it's currently doing if we get things easy to use.
If you want to build a web app with Haskell and play with type classes check it out: https://ihp.digitallyinduced.com/ GitHub: https://github.com/digitallyinduced/ihp