lanskey
lanskey

Reputation: 457

Haskell multiple monomorphic containers in one library

I have a library project, with quite few specialized monomorphic containers (mostly sets and also some map-like). They all share the basic Set functioanality (empty, singleton, insert, union etc), but also have additional value-specific functions. Some of them are just wrappers around Data.Set, but others need more efficient implementations (like finite bitset). I have 2 questions:

  1. What are the pros and cons of suffixing every such function (emptyT, singletonU)? Would it make a good general practice to suffix all monomorphic container functions to easier differentiate between them, and the polymorphic ones?

  2. Here is some sample code.

    import qualified Data.Set as S
    
    data T = ...
    
    newtype TSet = TSet { unpack :: S.Set T }
    
    emptyT = TSet S.empty
    
    singletonT = TSet . S.singleton
    

Is this the right way to wrap around polymorphic containers? Is there any more clever way to "inherit" functionality? I thought about creating a custom Set typeclass, but after reading some posts here I see it would only overcomplicate things..

Upvotes: 1

Views: 86

Answers (1)

chi
chi

Reputation: 116139

I'd recommend you use a module for each monomorphic specialization. Consider modules names Data.Set.Specialized.Int, Data.Set.Specialized.Char etc. Reuse, as much as possible, the same names used in Data.Set, without any suffix. Make your modules be used with import qualified, as Data.Set.

Advantages:

  • users are already familiar with the names
  • if one wants to change their Data.Set code to the specialized variant, this be dome simply by changing the types, from e.g.

    import qualified Data.Set as S
    foo :: S.Set Int -> String
    

    to

    import qualified Data.Set.Specialized.Int as SI
    foo :: SI.Set -> String
    

    without touching the code.

A typeclass is also a possibility, but one needs to be careful to choose the common operations among all variants. Also, if I understand correctly, you still want to allow some sets to have special operations which are not available for other sets. These much be outside the hypothetical type class, of course.

For question 2: if the wrapping is a newtype around Data.Set, you should consider safe coercions.

module Data.Set.Specialized.Int where

import qualified Data.Set as S
import Data.Coerce

newtype Set = Set { unSet :: S.Set Int }

union :: Set -> Set -> Set
union = coerce S.union

Upvotes: 1

Related Questions