Re: [Haskell-cafe] Different advice for type classes for end-user app and libraries?

Dear Ruben,
Currently I am working a end user application. Through the years I had incorporated the advice from r/haskell that classes without laws were not desirable. Their lack meant that we didn't have a contract boundary that should be respected by the instances. They secretly could `throw exc` on their implementation and you wouldn't notice.
Indeed you are right. This is especially pressing for the type classes that convey relations between multiple types. Luckily, diligent students of Haskell source code will find common patterns for the laws that occur -- see below.
But on the current app I am working on, I follow the advice from this blogpost [1] that I should use classes for domain modelling. This manifest on classes like
class UserQueryCapability f where getUser :: Id -> f User
Before we get to the laws, I would recommend that you use technique of "linguistic analysis" to find a better name for the class. If not, you may want to remove words that are implicit in the object. Class name of "UserQuery" may be better, because "capability" is implicit in the fact that it is a type class. See example here: https://olegchursin.medium.com/a-brief-introduction-to-domain-modeling-862a3...
for interacting with a DB. Functions can be written against the interface and I can mock up with QuickCheck some pretty broad cases. The classes usually don't have a superclass constrain on `Monad` as in `MonadReader` because I *cannot* establish a law relating the monadic behavior and `getUser`. The name has Capability in it and not Monad because I won't lie that way.
First method is to recognize a common pattern: you have a function that reads a part of the state. That means it is similar to `lookup userId <$> ask`. `ask` follows certain laws, and you may apply these laws here: getUser u >> getUser u == ask -- idempotence getUser u >> return () == return () -- no effect of reading To get inspiration for other laws you may look at how this type class interacts with other type classes that deal with `User` objects here. There are several other techniques that apply in special cases. For example if you have multiple self-contained implementations of a type class, you may use `quickspec` to get equational laws for different implementations, and look for common laws between different implementations. https://hackage.haskell.org/package/quickspec These two ways seem to be most applicable from the example you have provided so far.
Making sure that these effect classes are orthogonal is something I have to check by hand. But in the back of my head there is the concern about the lack of laws could bite me even on a end user-app. Is my intuition well founded that this case is OK?
Lack of laws may mean one of the following: * type class is not an abstraction and thus does not allow you to tell anything abstract about it * you may have undocumented tacit knowledge about the type class that gets lost in the implementation * your users will find it harder to use type class as interface (as it is intended to be used). Advert: If the above description is insufficient, and the task is too challenging to fully solve within haskell-cafe mailing list, you may contact https://www.migamake.com for consulting on type class design and training. We do provide business analysis for Haskell projects too. -- All the best MichaĆ
participants (1)
-
Michal J Gajda