Search code examples
typeclasspurescript

Ord: No type class instance was found for Data.Eq.Eq (Extended a0). PureScript by Example book, Chapter 6


I am quite new to Haskell/Purescript and currently learning by studying the PureScript by Example book.

In chapter 6 about type classes, exercise 4 has following task:

(Medium) Given any type a with an instance of Ord, we can add a new "infinite" value which is greater than any other value:

data Extended a = Finite a | Infinite

Write an Ord instance for Extended a which reuses the Ord instance for a.

Here is my attempt:
instance ordExtended :: Ord a => Ord (Extended a) where
  compare Infinite Infinite = EQ
  compare Infinite _ = GT
  compare _ Infinite = LT
  compare (Finite f1) (Finite f2) = compare f1 f2

Unfortunately, the code triggers an error:

No type class instance was found for

Data.Eq.Eq (Extended a0)

while checking that expression #dict Eq has type { eq :: Extended a0 -> Extended a0 -> Boolean } in value declaration ordExtended

where a0 is a rigid type variable bound at (line 0, column 0 - line 0, column 0) PureScript(NoInstanceFound)

I cannot quite understand the error message:

  1. What does expression #dict Eq mean? There is no dict in my code.
  2. What is a rigid type variable?
  3. The error seems to use different identifiers like a0 (why? I assume, that is a)

In my book, Eq type class instance should be covered by implementing Ord, as Ord extends Eq.


Solution

  • The key part of the error is at the start:

    No type class instance was found for
    
        Data.Eq.Eq (Extended a0)
    

    Here is the definition of Ord:

    class Eq a <= Ord a where
      compare :: a -> a -> Ordering
    

    This is actually the superclass syntax, saying that you need an Eq instance to have an Ord instance. So, you can fix the error by making an Eq instance:

    instance eqExtended :: Eq a => Eq (Extended a) where
      eq Infinite Infinite = true
      eq (Finite f1) (Finite f2) = eq f1 f2
      eq _ _ = false
    
    instance ordExtended :: Ord a => Ord (Extended a) where
      compare Infinite Infinite = EQ
      compare Infinite _ = GT
      compare _ Infinite = LT
      compare (Finite f1) (Finite f2) = compare f1 f2
    

    As to why a0 is used, it seems the purescript compiler just likes adding numbers after type variables, possibly to reduce vagueness or to allow for scoped type variables. You can read about rigid type variables here (they're basically variables that can't be changed to fit constraints).