ClassyPrelude有两个map函数,即:
mapomapmap适用于任何Functor.但是,类似Text的东西不是仿函数,因为它们不是通用容器.也就是说,与列表不同,它们不能包含任何类型.从以下代码中可以看出:
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 ()
Run Code Online (Sandbox Code Playgroud)
注意一个人必须用来omap处理Text.由于map要求类型为a Functor,map f text失败.问题是,我发现重新定义map这两个电话非常容易.这是代码:
{-# 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 a b where
type Element a :: *
type Container a b :: *
map :: (Element a -> b) -> a -> Container a b
instance (Functor f) => CanMap (f a) b where
type Element (f a) = a
type Container (f a) b = f b
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 ()
Run Code Online (Sandbox Code Playgroud)
所需要的只是为CanMap的任何单形容器添加实例.无论如何,ClassyPrelude已经在Data.MonoTraversable模块中使用"omap"执行此操作.但我怀疑有一个很好的理由我错过了为什么应该有两个单独的地图函数来处理这些替代情况,但我想知道那是什么.
我认为问题在于类型如unboxed Vectors,其中类型看起来几乎应该有一个Functor实例,但实际上你需要对元素类型进行限制来进行映射:
instance U.Unbox a => MonoFunctor (U.Vector a)
Run Code Online (Sandbox Code Playgroud)
当您尝试在系统中包含此类型时,GHC仅查看头部而不是查找实例的上下文时,您最终会遇到重叠的实例问题.
在这种情况下map,知道你真的可以转换为你想要的任何"元素"类型也是有用的.如果只有一个多参数类型类,则无法表示实例有时不依赖于元素.