In a prior I wrote about how type class instance selection worked. To help get a sense of good type class , I want to walk through a type class pattern and a related type class anti-pattern. post design To/From Type Classes The first pattern is the and type classes. This pattern shows up in many packages: , , , among others. To From aeson [cassava](https://hackage.haskell.org/package/cassava-0.4.5.1/docs/Data-Csv.html) [postgresql-simple](https://hackage.haskell.org/package/postgresql-simple) In general the class looks like: To class ToBlah a wheretoBlah :: a -> Blah Although it can also have the form: class ToBlah a wheretoBlah :: a -> BlahBuilder Here is a more efficiently composable intermediate version of (this is the case when converting to s via s). BlahBuilder Blah ByteString Builder The type classes tend to look like: From class FromBlah a whereparseBlah :: Parser a The type typically exposes a interface with some type of state and a way to short-circuit parsing to produce an error message. There tends to be some API function that uses the parsers (this is the function in and the function in ), and they are only used directly to compose other parsers. Parser Monad [decode](https://hackage.haskell.org/package/aeson-1.2.0.0/docs/Data-Aeson.html#v:decode) aeson [query](https://hackage.haskell.org/package/postgresql-simple-0.5.3.0/docs/Database-PostgreSQL-Simple.html#g:15) postgresql-simple Sometimes, both and classes are provided; other times, as in the case of the , only one direction is needed. To From [Yesod.Core.Content.ToContent](https://hackage.haskell.org/package/yesod-core-1.4.33/docs/Yesod-Core-Content.html#t:ToContent) In general, this pattern is used for converting between user-defined types and a particular type which an API is built around. Why the To Pattern is Good Let’s look at an example with ’s , which is used to convert a user’s type to a or JSON expression type. aeson ToJSON Value If I have a type, data BigRecord = BigRecord{ fiestaPolicies :: [Policy], brunchMenu :: Menu, dinnerMenu :: Menu, backgroundMusic :: Maybe (Either ScreamingMan [Song])} the instance for types like this tend to be boring like*: ToJSON instance ToJSON BigRecord wheretoJSON BigRecord {..} = object[ ("fiesta-policy" , toJSON fiestaPolicies ), ("brunch-menu" , toJSON brunchMenu ), ("dinner-menu" , toJSON dinnerMenu ), ("background-music", toJSON backgroundMusic)] Boring is good. Boring means predictable. Boring means I don’t have to think hard when writing or reading it. Additionally, type classes allow us to write a single function for polymorphic types like , and . The instances for the ’s and ’s will automatically get used correctly. Maybe a Either a b a b Type classes provide canonicity, there is only one instance for a type. For conversions that only have a single valid conversion per type, classes are a good fit. The type class pattern takes advantage of identifier overloading and canonicity to reduce the degrees of freedom when writing instances. To To Freedom isn’t Free If I don’t use the type class pattern, I need to make more choices. Every time I make a choice, I increase the chance I will make the wrong choice. To Here is one way we can write conversion code like the example: ToJSON bigRecordToJSON :: BigRecord -> ValuebigRecordToJSON BigRecord {..} = object[ ("fiesta-policy" . , listToJSON policyToJSON fiestaPolicies), ("brunch-menu" , brunchMenuToJSON brunchMenu), ("dinner-menu" , dinnerMenuToJSON dinnerMenu), ("background-music", maybeToJSON (eitherJSON screamingManToJSONplaylistToJSON)backgroundMusic)] Even though the conversions are completely determined by the types, I have to choose the correct functions for the conversion. Also, I have to pass an additional function to every polymorphic conversion function — one function per type variable, in fact. This code is the best-case scenario. I have decomposed my conversion functions predictably and used a naming convention. I have not always found myself in such a lucky situation. Since there is less incentive to decompose the code into small functions, it could also look like this: bigRecordToJSON :: BigRecord -> ValuebigRecordToJSON BigRecord {..} = object[ ("fiesta-policy" , Array$ Vector.fromList$ map policyToJSON fiestaPolicies), ("brunch-menu" , Array$ Vector.fromList$ map brunchMenuItem brunchMenu), ("dinner-menu" , Array$ Vector.fromList$ map dinnerMenuItem dinnerMenu), ("background-music", maybe (object [])(eitherJSON screamingManToJSONplaylistToJSON)backgroundMusic)] For both of these versions, I added potential issues. If you haven’t already found the problem areas, see if you can. If one does not use a type class, they give themselves more freedom than they potentially need. This opens the door for errors, inconsistency and confusion. Roundtrip Anti-Pattern Sometimes a single type class is used that has both a and method. to from This is the case with the type class from the package. There are no functions in the API that require having both directions specified, but I have to implement both directions anyway. [Binary](https://hackage.haskell.org/package/binary-0.8.5.1/docs/Data-Binary.html#g:1) binary binary If I am writing a client that sends data to a server, it is likely I will only serialize datatypes and never deserialize them. I will still need an implementation for deserializing, so I’ll create a dummy one, or throw an error. A meaningless method implementation is a maintenance hazard. A programmer in the might assume the instance was completely implemented and become confused when deserialization fails. future Put a Law on It The argument for forcing the implementation of to have both and might be it allows one to require the law , which documents as a requirement. Binary to from decode . encode = id Binary A law is useful as a specification. It can also be useful for equational reasoning, which means I can replace with . This is a very useful optimization … if one was ever to come across an actual use of .** decode . encode id decode . encode Except I don’t think this will happen in practice. Encoding and decoding are part of totally different code paths. Why it is Bad In practice, what happens is you have a type class that sometimes has meaningless implementations for methods, with an API that doesn’t take advantage of both directions, with a law that is not particularly useful for equational reasoning. From the this vantage point, I believe roundtrip type classes are many times an anti-pattern. Instead of providing programs with more reasoning power, they provide less because they are abused. They are trying to encourage a best-practice that is not always applicable. They don’t fit the problem they are used to solve. If our type should roundtrip from the and directions, we should do the same thing we do with our laws: document it and test it.*** to from There are cases where it does make sense to have both a and in a single type class, but lacking a compelling reason (like a shared associated type, or an API function that needs both directions for a single type), including them together is a mistake. to from Lawless Good For cases where there is single mapping between a type and a conversion function, and type classes can be used to compose easy-to-reason-about functions. Avoiding type classes results in conversion functions that accept an additional function for elements (think of the example earlier), or some bespoke process that can be inconsistent. and instances implicitly use unique and correct implementations. This process automatically prevents against a class of incorrect implementations and encourages correctness purely through composition. To From eitherToJSON To From In the short, the and type class are simple and powerful type class patterns you should not be afraid to use. To From * For the smarties, I’m avoiding the method because most classes don’t have something analogous, and this makes the general pattern more obvious. aeson .= To ** Not only can equational reasoning be used for rewriting expressions to simplify them, it can also be used to expand expressions and derive functions. I have not witnessed Haskellers other than Conal doing this however. ** Cale Gibbard, in addition to helping me refine my ideas, pointed out the and instances for do not roundtrip, when encoding and then decoding a . I think the same is true with ’s instance for . Ideally, this should be more clearly documented, since Haskeller’s tend to assume these instances do roundtrip. ToJSON FromJSON Maybe Just Nothing postgresql-simple ToField Maybe is how hackers start their afternoons. We’re a part of the family. We are now and happy to opportunities. Hacker Noon @AMI accepting submissions discuss advertising & sponsorship To learn more, , , or simply, read our about page like/message us on Facebook tweet/DM @HackerNoon. If you enjoyed this story, we recommend reading our and . Until next time, don’t take the realities of the world for granted! latest tech stories trending tech stories