Haskell ClassyPrelude中两个单独映射函数的基本原理

Haskell ClassyPrelude中两个单独映射函数的基本原理,haskell,typeclass,Haskell,Typeclass,具有两个映射功能,即: map omap 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] :

具有两个映射功能,即:

  • map
  • omap
  • 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
    来处理
    文本。由于
    映射
    要求类型为
    函子
    映射文本
    失败。问题是,我发现重新定义
    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添加实例。classyprellude已经通过模块中的“omap”完成了这项工作。然而,我怀疑我没有找到一个很好的理由来解释为什么应该有两个单独的映射函数来处理这些交替的情况,但我想知道这是什么。

    我认为问题在于类型,例如unbox
    Vector
    s,类型看起来几乎应该有一个
    Functor
    实例,但实际上需要对元素类型进行限制才能进行映射:

    instance U.Unbox a => MonoFunctor (U.Vector a)
    
    当您试图在系统中包含此类类型时,GHC在查找它们时只查看头部而不查看实例的上下文,您最终必然会遇到重叠实例问题

    map
    的情况下,了解您确实可以转换为所需的任何“元素”类型也很有用。只有一个多参数typeclass,您无法表示实例有时不依赖于元素