2015-02-12 15 views
5

ClassyPrelude có hai chức năng bản đồ, cụ thể là:Lý do cho hai chức năng bản đồ riêng biệt trong ClassyPrelude

  1. map
  2. omap

map công trình trên bất kỳ Functor. Tuy nhiên, những thứ như Text không phải là functors vì chúng không phải là các container chung. Nghĩa là, chúng không thể chứa bất kỳ loại nào, không giống như một danh sách. Như có thể thấy trong đoạn mã sau:

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() 

Thông báo người ta phải sử dụng omap để đối phó với Text. Vì map yêu cầu loại là Functor, map f text không thành công. Vấn đề là, tôi thấy dễ dàng để xác định lại map để hoạt động cho cả hai cuộc gọi. Đây là mã:

{-# 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() 

Tất cả những gì cần thiết là thêm trường hợp vào CanMap cho bất kỳ thùng chứa đơn lẻ nào. ClassyPrelude đã làm điều này anyway với "omap" trong mô-đun Data.MonoTraversable. Tôi nghi ngờ tuy nhiên có một lý do chính đáng tôi bỏ lỡ về lý do tại sao nên có hai chức năng bản đồ riêng biệt để đối phó với những tình huống thay thế này, nhưng tôi tự hỏi đó là những gì.

Trả lời

8

Tôi nghĩ rằng vấn đề là với các loại như không có hộp bọc Vector s, nơi mà các loại trông gần như nó nên có một trường hợp Functor nhưng bạn thực sự cần một hạn chế về các loại yếu tố để làm các bản đồ:

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

Khi bạn cố gắng bao gồm các loại như vậy trong hệ thống của mình, với cách GHC chỉ nhìn vào đầu chứ không phải ngữ cảnh của các cá thể khi tìm kiếm chúng, bạn nhất thiết sẽ kết thúc với các sự cố chồng chéo.

Cũng có thể hữu ích, trong trường hợp map, để biết rằng bạn thực sự có thể chuyển đổi thành bất kỳ loại phần tử nào bạn muốn. Chỉ với một typeclass đa thông số bạn không thể diễn tả rằng instance đôi khi không phụ thuộc vào phần tử.

Các vấn đề liên quan