Rules of thumb for type constraints.

  • Prefer manipulating data through interfaces rather than directly.
  • If one typeclass can implement a second typeclass, the first typeclass is a subtype of the second.

At one point I needed to implement a simple stack structure in Haskell:

Nice and simple. Afterwards I needed to implement a stack somewhere else using a different data structure. In the interests of code reuse and simplicity, I decided to implement an interface:

Which is only slightly longer than the original, with only one extra line of code. It’s usually going to be better to implement an interface and use that instead of using a data structure directly. We get the following advantages:

  • Code reuse across all members of the typeclass.
  • Fewer ways to write an incorrect program if we can only interact with a data structure through it’s interface.
  • Less namespace pollution.
  • Only marginally longer.

We can now implement a second instance (or more).

The implementation is unimportant, safe to say that it behaves like a stack.

I also wanted to fold across stack structures. For that we need to implement Foldable for all of the members of Stack. The minimum complete definition for Foldable is foldr. An example implementation of foldr could be as follows:

Which works for any type of Stack. My initial thought was to automatically implement foldr for every member of Stack automatically:

Which doesn’t work. If it did work we could do the following:

If we has something that instanced both Stack and OtherClass, the compiler would have no way of knowing which version of Foldable to use.

We know that every instance of Stack can also implement Foldable because we can write a default implementation for it. Therefore we should modify our definition of the Stack typeclass:

The only disadvantage now is that we have to manually implement Foldable for every member of Stack. With our default implementation, this is usually pretty easy to do.

Summary

  • Try not to write functions for data structures directly, instead, try to access them through an interface. That way we can reuse functions across multiple instances.
  • If you can write a default implementation for one typeclass using the functions from another typeclass, make it a type constraint. (e.g, we can make a default implementation for Foldableusing the functions from Stack, so we make Foldable a type restriction).

Originally published at https://andymac-2.github.io/notepad/

The stories I write are a part of a learning journey through life, logic and programming. Share this journey with me.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store