Justification of two separate map functions in ClassyPrelude

ClassyPrelude has two display functions, namely:

  • map
  • omap

map works with any Functor . However, things like Text are not functors, since they are not universal containers. That is, they cannot contain any type, unlike a list. As seen from the following code:

 module Main where import Prelude () import ClassyPrelude import qualified Data.Text as T import Data.Char as C main = do let l = [1,2,3] :: [Int] let t = (T.pack "Hello") let m = Just 5 print $ map (*2) l print $ map (*2) m print $ omap C.toUpper t return () 

Note that you must use omap to work with Text . Since map requires the type to be Functor , map f text fails. The thing is, I found it trivially easy to override map to work for both calls. Here is the code:

 {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverlappingInstances #-} module Main where import Prelude hiding (map) import qualified Data.Text as T import Data.Char as C import Control.Monad (Functor) import qualified Data.Vector.Unboxed as U class CanMap ab where type Element a :: * type Container ab :: * map :: (Element a -> b) -> a -> Container ab instance (Functor f) => CanMap (fa) b where type Element (fa) = a type Container (fa) b = fb map = fmap instance CanMap T.Text Char where type Element T.Text = Char type Container T.Text Char = T.Text map = T.map instance (U.Unbox a, U.Unbox b) => CanMap (U.Vector a) b where type Element (U.Vector a) = a type Container (U.Vector a) b = U.Vector b map = U.map main = do let l = [1,2,3] :: [Int] let m = Just 5 let t = (T.pack "Hello") let u = U.generate 3 id print $ map (*2) l print $ map (*2) m print $ map C.toUpper t print $ map (*2) u return () 

All that is required is to add instances to CanMap for any monomorphic containers. ClassyPrelude already does this anyway using the "omap" in the Data.MonoTraversable module. I suspect, however, that there is a good reason why I cannot understand why there should be two separate map functions for these alternative situations, but I wonder what it is.

+5
source share
1 answer

I think the problem is with types like unboxed Vector s, where the type looks almost as if it should have a Functor instance, but you really need a restriction on the element types to display:

 instance U.Unbox a => MonoFunctor (U.Vector a) 

When you try to include such types in your system, since the GHC looks only at the heads, not at the instance contexts when viewing them, you will certainly encounter problems of overlapping instances.

It is also useful in the case of map to know that you can actually convert any desired element type. When using only one multiparameter type, you cannot express that an instance is sometimes independent of an element.

+8
source

Source: https://habr.com/ru/post/1213201/


All Articles