2015-03-23 19 views
5

Trong hàm test, tôi duyệt qua danh sách, tạo thấu kính từ các thành viên của nó rồi in một số dữ liệu. Điều này làm việc khi tôi sử dụng một kiểu gọi quan trọng. Nó không đánh máy khi tôi làm cho nó điểm-miễn phí.Tạo ống kính không điểm không nhập kiểm tra

Tại sao lại xảy ra trường hợp này và làm cách nào tôi có thể giải quyết vấn đề này? Có vẻ như với tôi rằng GHC không giữ lại thông tin rằng xếp hạng cao hơn f (trong ống kính) là Functor khi sử dụng kiểu không có điểm, nhưng tôi không quá chắc chắn.

Tôi đang sử dụng GHC 7.8.3

{-# LANGUAGE RankNTypes #-} 
{-# LANGUAGE TemplateHaskell #-} 

import Control.Lens 
import Control.Monad 
import Data.List 
import Data.Maybe 

type PlayerHandle = String 

data Player = Player { _playerHandle :: PlayerHandle } 
makeLenses ''Player 

data GameState = GameState { _gamePlayers :: [Player] } 
makeLenses ''GameState 

type PlayerLens = Lens' GameState Player 

getPlayerLens :: PlayerHandle -> PlayerLens 
getPlayerLens handle f st = fmap put' get' 
    where 
     players = st^.gamePlayers 
     put' player = let 
      g p = case p^.playerHandle == handle of 
       True -> player 
       False -> p 
      in set gamePlayers (map g players) st 
     get' = f $ fromJust $ find (\p -> p^.playerHandle == handle) players 


printHandle :: GameState -> PlayerLens -> IO() 
printHandle st playerLens = do 
    let player = st^.playerLens 
    print $ player^.playerHandle 


test :: GameState -> IO() 
test st = do 
    let handles = toListOf (gamePlayers.traversed.playerHandle) st 
    -- 
    -- Works: Pointful 
    --forM_ handles $ \handle -> printHandle st $ getPlayerLens handle 
    -- 
    -- Does not work: Point-free 
    forM_ handles $ printHandle st . getPlayerLens 


main :: IO() 
main = test $ GameState [Player "Bob", Player "Joe"] 

Test.hs:45:38: 
    Couldn't match type `(Player -> f0 Player) 
         -> GameState -> f0 GameState' 
        with `forall (f :: * -> *). 
         Functor f => 
         (Player -> f Player) -> GameState -> f GameState' 
    Expected type: PlayerHandle -> PlayerLens 
     Actual type: PlayerHandle 
        -> (Player -> f0 Player) -> GameState -> f0 GameState 
    In the second argument of `(.)', namely `getPlayerLens' 
    In the second argument of `($)', namely 
     `printHandle st . getPlayerLens' 
Failed, modules loaded: none. 
+1

Tôi không biết chi tiết, nhưng tôi chắc chắn nó có liên quan đến các loại xếp hạng cao hơn gây ra một số vấn đề về nhập. Việc truyền các đối số 'Lens' có thể gây ra vấn đề đó. Nó có thể sẽ hoạt động nếu bạn sử dụng 'ALens' thay vì' Lens', vì nó được thiết kế để truyền xung quanh. –

+1

@DavidYoung Bạn * có thể * làm điều đó với 'ALens', nhưng sau đó hàm được gọi phải rõ ràng' cloneLens' nó trở lại một ống kính bình thường trước khi sử dụng nó. Đó là bình thường chỉ thích hợp cho một chức năng mà thực sự cần các tính năng ống kính đầy đủ của đối số của nó. –

+0

@DavidYoung: Tôi sẽ phải xem xét nó. Một google nhanh chóng không đưa ra bất kỳ hướng dẫn/ví dụ cho 'ALens' trên' Lens', nhưng tôi sẽ có thể tìm ra nó. Thời gian để gọi ngày nghỉ ngay bây giờ. –

Trả lời

8

Lens' là một loại xếp hạng cao hơn, và suy luận kiểu rất giòn với những người, và về cơ bản chỉ có tác dụng khi tất cả các chức năng mà mất các đối số cấp cao hơn có chữ ký rõ ràng để làm như vậy. Điều này hoạt động rất kém với mã điểm miễn phí sử dụng . và các loại tương tự, không có chữ ký như vậy. (Chỉ $ có một hack đặc biệt để đôi khi làm việc với điều này.)

Các lens thư viện riêng của mình được khoảng này bằng cách đảm bảo rằng tất cả các chức năng mà sử dụng một đối số ống kính không có một loại ống kính hoàn toàn chung của nó, nhưng chỉ một loại cho biết tính năng ống kính chính xác, chúng sử dụng.

Trong trường hợp của bạn, đó là chức năng printHandle là thủ phạm cho việc này. mã của bạn sẽ biên dịch nếu bạn thay đổi chữ ký của mình cho chính xác hơn

printHandle :: s -> Getting Player s Player -> IO() 

tôi thấy chữ ký này bằng cách xóa một bản gốc và sử dụng :t printHandle.

EDIT (và EDIT lần nữa để thêm ALens'): Nếu bạn nghĩ rằng "chữa bệnh còn tồi tệ hơn so với bệnh tật", sau đó tùy thuộc vào nhu cầu của bạn một lựa chọn, mà không yêu cầu bạn phải thay đổi chữ ký chức năng của bạn, nhưng mà yêu cầu bạn thực hiện một số chuyển đổi rõ ràng, thay vào đó, hãy sử dụng loại ALens'. Sau đó bạn cần phải thay đổi hai dòng:

type PlayerLens = ALens' GameState Player 
... 
printHandle st playerLens = do 
    let player = st^.cloneLens playerLens 
... 

ALens' là một-cao không loại cấp bậc đã được khéo léo xây dựng để nó chứa tất cả các thông tin cần thiết để trích xuất một ống kính nói chung từ nó với cloneLens. Nhưng vẫn còn một loại phụ đặc biệt của ống kính (Functor vừa được chọn một cách khéo léo) vì vậy bạn chỉ cần chuyển đổi rõ ràng từALens' thành Lens', không phải theo cách khác.

Một lựa chọn thứ ba, trong đó có thể không phải là tốt nhất cho ống kính, nhưng mà thường làm việc với nhiều loại hạng cao hơn trong chung, là để biến bạn PlayerLens thành một newtype:

newtype PlayerLens = PL (Lens' GameState Player) 

Tất nhiên điều này bây giờ cần cả hai gói và unwrapping ở một số nơi trong mã của bạn.getPlayerLens đặc biệt bị gián đoạn:

getPlayerLens :: PlayerHandle -> PlayerLens 
getPlayerLens handle = PL playerLens 
    where 
     playerLens f st = fmap put' get' 
      where 
       players = st^.gamePlayers 
       put' player = let 
        g p = case p^.playerHandle == handle of 
         True -> player 
         False -> p 
        in set gamePlayers (map g players) st 
       get' = f $ fromJust $ find (\p -> p^.playerHandle == handle) players 
+0

Một trường hợp chữa bệnh có thể tồi tệ hơn bệnh. Oh well, nhưng cảm ơn vì thông tin! –

+0

@ThomasEding Một giải pháp thay thế khác mà bạn có thể hoặc không thích ... –

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