Search code examples
haskellprogramming-languagestypeclass

Typeclass which is superset of another and default implementations in Haskell


I've came to an interesting situation when I tried to decompose one typeclass I needed for restoring tree from a list representation. The idea was to represent property of elements to be relative each to other in original hierarchy.

Consider next traits of data types

class HierarchyOrd a where
    -- | Compares two objects for being on the same branch of hierarchy
    -- LT/GT lower/higher in hierarchy, EQ on the same node
    hierarchyCompare :: a -> a -> Maybe Ordering

class HierarchyOrd a => Hierarchy a where
    -- | Get information for common joint of branches for two objects
    -- Either one of them already on joint node (parent)
    -- or we need another object that represent that joint
    hierarchyJoint :: a -> a -> Either Ordering a
    -- hierarchyCompare x y = either Just (const Nothing) (hierarchyJoint x y)

-- Sample for FilePath
instance Hierarchy FilePath where
    hierarchyJoint x y = case (length x', length y', length z') of
            (a, b, c) | a == c && b == c -> Left EQ
            (a, _, c) | a == c -> Left GT
            (_, b, c) | b == c -> Left LT
            _ -> Right (joinPath z')
        where
            [x', y'] = map splitDirectories [x, y]
            skel = takeWhile id (zipWith (==) x' y')
            z' = zipWith const x' skel -- common prefix

instance HierarchyOrd FilePath where
    hierarchyCompare x y = either Just (const Nothing) (hierarchyJoint x y)

As you can see HierarchyOrdering is a subset of Hierarchy for which we only need to implement ordering without requiring of building new node. In this particular case (FilePath) having two non-overlapping functionality isn't feasible and even may result in extra-work (splitting directories twice for hierarchyCompare and hierarchyJoint). That's why it was decided to cover functionality of hierarchyCompare by hierarchyJoint since it makes no sense to call it if we got Just _.

Question is: how to be with default implementation of hierarchyCompare when object is defined on Hierarchy. Maybe there is some extension that allows to expose such kind of relation between typeclasses in a more descriptive way (wich allows default implementation)?


Solution

  • I think you're looking for the GHC DefaultSignatures extension? That let's you do:

    class HierarchyOrd a where
        -- | Compares two objects for being on the same branch of hierarchy
        -- LT/GT lower/higher in hierarchy, EQ on the same node
        hierarchyCompare :: a -> a -> Maybe Ordering
        default hierarchyCompare :: Hierarchy a=> a -> a -> Maybe Ordering
        hierarchyCompare x y = either Just (const Nothing) (hierarchyJoint x y)
    

    and then you can simply provide an empty instance declaration:

    instance HierarchyOrd FilePath