ClassyPrelude中两个独立地图函数的基本原理

时间:2015-02-12 04:29:20

标签: haskell typeclass

ClassyPrelude有两个地图功能,即:

  1. map
  2. omap
  3. map适用于任何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 ()
    

    请注意,必须使用omap来处理Text。由于map要求类型为Functormap 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 ()
    

    所需要的只是为CanMap的任何单形容器添加实例。无论如何,ClassyPrelude已经使用" omap"在Data.MonoTraversable模块中。但我怀疑我错过了为什么应该有两个单独的地图函数来处理这些替代情况的原因,但我想知道那是什么。

1 个答案:

答案 0 :(得分:8)

我认为问题出现在诸如未装箱的Vector之类的类型中,其中类型看起来几乎,就像它应该有一个Functor实例,但实际上你需要一个限制要执行映射的元素类型:

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

当您尝试在系统中包含此类类型时,GHC仅查看头部而不是实例的上下文时,您最终会遇到重叠的实例问题。

map的情况下,知道您确实可以转换为您想要的任何“元素”类型也很有用。只有一个多参数类型类,你无法表达实例有时不依赖于元素。