20

All the typeclasses in Typeclassopedia have associated laws, such as associativity or commutativity for certain operators. The definition of a "law" seems to be a constraint that cannot be expressed in the type system. I certainly understand why you want to have, say, monad laws, but is there a fundamental reason why a typeclass that can be expressed fully within the type system is pointless?

Gurgeh
  • 2,130
  • 15
  • 28
  • 2
    You might find section 2 of [this](http://www.haskell.org/haskellwiki/Monad_Laws) to be interesting. Also [this](http://stackoverflow.com/questions/6399648/what-happens-to-you-if-you-break-the-monad-laws) discussion on breaking the monad laws. I think the short answer is that these laws define the expected behaviour of the typeclass, so why would you call something a duck if it isn't a duck. – sabauma Feb 21 '13 at 14:10
  • 3
    Not all typeclasses have laws. Typeclassopedia lists only a small part of all existing typeclasses, namely those with important algebraic/categorical properties (laws *are* these properties). Things like Read and Show and ... have none. – n. m. could be an AI Feb 21 '13 at 14:10
  • @sabauma No offense, but that was not very interesting. I wrote "I certainly understand why you want to have, say, monad laws" to avoid that type of answer. – Gurgeh Feb 21 '13 at 14:18
  • @Gurgeh I misunderstood your last sentence. You are referring to typeclasses without associated laws, rather than instances that do not follow those laws. – sabauma Feb 21 '13 at 14:34

3 Answers3

21

You will notice that almost always the laws are algebraic laws. They could be expressed by the type system by using some extensions, but the proofs would be cumbersome to express. So you have unchecked laws and potentially implementations might break them. Why is this good?

The reason is that the design patterns used in Haskell are motivated (and in most cases mirrored) by mathematical structures, usually from abstract algebra. While most other languages have an intuitive notion of certain features like safety, performance and semantics, we Haskell programmers prefer to establish a formal notion. The advantage of doing this is: Once your types and functions obey the safety laws, they are safe in the sense of the underlying algebraic structure. They are provably safe.

Take functors as an example. A Haskell functor has the following two laws:

fmap f . fmap g = fmap (f . g)
fmap id         = id

Firstly this is very important: Functions in Haskell are opaque. You cannot examine, compare or whatever them. While this sounds like a bad thing in Haskell it is actually a very good thing. The fmap function cannot examine the function you've passed it. Particularly it can't check that you've passed the identity function or that you've passed a composition. In short: it can't cheat! The only way for it to obey these two laws is actually not to introduce any effects of its own. That means, in a proper functor fmap will never do anything unexpected. In fact it cannot do anything else than to map the given function. This is a very simple example and I haven't explained all the subtleties why fmap can't cheat, but it demonstrates the point.

Now extend this all over the language, the base libraries and most sensible third party libraries. This gives you a language that is as predictable as a language can get. When you write code, you know what it's going to do. That's one of the main reasons why Haskell code often works out of the box. I often write pages of Haskell code before compiling. Once my type errors are fixed, my program usually works.

The other reason why this is desirable is that it allows a more compositional style of programming. This is particularly useful when working as a team. First you map your application to algebraic structures and establish the necessary laws. For example: You express what it means for something to be a Valid Web Server. In particular you establish a formal notion of web server composition. If you compose two Valid Web Servers, the result is a Valid Web Server. Do you see where this is going? After establishing these laws the teammates go to work, and they work in isolation. Little to no communication is necessary to get their job done. When they meet again, everybody presents their Valid Web Servers and they just compose them to make the final product, a web site. Since the individual components were all Valid Web Servers, the final result must be a Valid Web Server. Provably.

ertes
  • 4,430
  • 1
  • 18
  • 23
  • 2
    This was an interesting and well-written comment, but seems to be the answer to an entirely different question than "is there a fundamental reason why a typeclass that can be expressed fully within the type system is pointless?" :) – Gurgeh Feb 21 '13 at 16:59
  • 1
    @Gurgeh: Well, a type class without laws does well make sense, why wouldn't it? I covered that in the first paragraph, didn't I? If not, I may have misunderstood your question entirely. =) – ertes Feb 22 '13 at 15:18
  • Well, I thought that perhaps it might be an anti-pattern if you found yourself defining a type-class that has no laws. Perhaps it should be.. something else (for example just passing along instance-functions together with your data type or putting them in a record or something)? – Gurgeh Feb 22 '13 at 17:00
7

Yes and no. For instance the Show class does not have any laws associated with it, and it is certainly useful.

However, typeclasses express interfaces. An interface needs to satisfy more than being just a bunch of functions - you want these functions to fulfill a specification. The specification is normally more complicated than what can be expressed in Haskell's type system. For example, take the Eq class. It only needs to provide us with a function, the type of which has to be a -> a -> Bool. That's the most that Haskell's type system will allow us to require from an instance of an Eq type. However, we would normally expect more from this function - you would probably want it to be an equivalence relation (reflexive, symmetric and transitive). So then you state these requirements as separate "laws".

oggy
  • 3,483
  • 1
  • 20
  • 24
  • 2
    `Show` kind of does have laws as well – `show x` should be valid Haskell code equivalent to `x` (provided it's interpreted as the same type). This isn't obeyed nearly as universally as the other standard-typeclass laws. – leftaroundabout Feb 21 '13 at 14:35
  • 2
    @leftaroundabout It doesn't tend to *break* code if `read (show x)` is not the same as `x` though, because people don't tend to serialize data by converting it to a `String` with `show`. Whereas breaking the monad, functor or applicative laws has a very strong probability of breaking code, because reasonable-looking refactorings no longer work. – Chris Taylor Feb 21 '13 at 14:55
  • 1
    @ChrisTaylor, most of the time when monad laws are broken they are still satisfied up to an equivalence relation; I've experienced this most commonly with nondeterminism monads, where applying associativity changes the order of the results. But I don't usually care very much about the order of the results in such a monad, just that I get the one I want eventually. Anyway I would say breaking the monad etc. laws has a very strong probability of *changing behavior*, not necessarily breaking code. – luqui Feb 21 '13 at 17:11
  • 1
    @luqui Yes, I suppose that's because in practice people only break e.g. the monad laws *just a little bit* rather than writing instances that are clearly ridiculous. – Chris Taylor Feb 21 '13 at 17:25
  • 1
    @ChrisTaylor: I'd rather say that the equivalence relation scenario entails people wrote something that is in fact a monad if it was written for a *different* type than the one in their implementation—one with less structure than the implementation type. In particular, luqui's example sounds like the good old `Set` monad so many of us wish we could have. – Luis Casillas Feb 21 '13 at 18:35
1

A typeclass doesn't need to have laws, but it often will be more useful if it has them. Many typeclasses are expected to function in a certain way, the laws codify user expectations. The laws let users make assumptions about the way that an instance of a typeclass will work. If you break the typeclass laws, you don't get arrested by the Haskell police, you just end up with confused users.

Dirk Holsopple
  • 8,731
  • 1
  • 24
  • 37