haskellpolymorphismflexibility

How to code polymorphic functions under Haskell 98


As a training exercise, I have written a polymorphic function to determine whether a given number is prime to either a single number or all of a list of numbers:

{-# LANGUAGE FlexibleInstances #-}
class PrimeTo a where
    ispt :: Integer -> a -> Bool
instance PrimeTo (Integer) where
    ispt n d = 0 /= (rem n d)
instance PrimeTo ([Integer]) where
    ispt n [] = True
    ispt n (x:xs) = (ispt n x) && (ispt n xs)

To get this to work, I had to use FlexibleInstances, and I'm happy with that, but curious.

Under strict Haskell 98, as I understand it, I would need to add a type descriptor, T, to the instance definitions:

class PrimeTo a where
    ispt :: Integer -> a -> Bool
instance PrimeTo (T Integer) where
    ispt n d = 0 /= (rem n d)
instance PrimeTo (T [Integer]) where
    ispt n [] = True
    ispt n (x:xs) = (ispt n x) && (ispt n xs)

but I haven't a clue what goes in place of "T", and I don't even know whether this is possible under Haskell 98.

So:

  1. Is this even possible under Haskell 98?
  2. If so, what would be used for T?

Solution

  • T can be Integer or [], as folllows:

    class PrimeTo a where
        ispt :: Integer -> a -> Bool
    instance PrimeTo Integer where
        ispt n d = 0 /= (rem n d)
    instance PrimeToList a => PrimeTo [a] where
        ispt = isptList  -- see below
    

    Since the last one can only be about [a], we need a helper class PrimeToList. Here's the additional helper class and instance:

    class PrimeToList a where
        isptList :: Integer -> [a] -> Bool
    
    instance PrimeToList Integer where
        isptList n []     = True
        isptList n (x:xs) = ispt n x && isptList n xs
    

    By the way, I'd rewrite the last definition using all:

        isptList n = all (ispt n)
    

    The above shows the general technique. In your specific case, you can probably avoid the helper class and use

    class PrimeTo a where
        ispt :: Integer -> a -> Bool
    instance PrimeTo Integer where
        ispt n d = 0 /= (rem n d)
    instance PrimeTo a => PrimeTo [a] where
        ispt n = all (ispt n)
    

    This will also define PrimeTo [[Integer]], PrimeTo [[[Integer]]] and so on, so it is not a perfect replacement like the previous one.