2010-08-10 28 views
13

Tôi mới ở haskell, tôi phải viết một chương trình ngữ cảnh-nhận thức, vì vậy tôi nghĩ rằng tôi có thể sử dụng Reader Monad để giữ ngữ cảnh đọc từ một tập tin, tôi biết làm thế nào để đọc các tập tin đặt nội dung trong danh sách không giống như [[Char], [Char])], nhưng tôi không biết cách triển khai Reader Monad để làm cho môi trường có sẵn cho tất cả các thành phần của chương trình mà không sử dụng kiểu bắt buộc. không biết cách thiết lập và sử dụng môi trường, theo như tôi hiểu tôi nên cho nó như tham số cho tất cả các hàm cần môi trường với hàm enR runReader, nhưng tôi rất bối rối, ai đó có thể cho tôi một số chỉ dẫn hoặc hướng dẫn? cảm ơn trướchelp with reader monad

+0

Bạn có chắc chắn bạn cần 'Reader' ngay từ đầu không? "Làm cho môi trường có sẵn cho tất cả các thành phần" thường không phải là cách tốt nhất để viết mã trong Haskell. Bạn có thể mô tả công việc bạn đang làm việc chi tiết hơn không? –

+3

@Travis Brown: Có thể có ý nghĩa nếu bạn có một lượng lớn dữ liệu tĩnh cơ bản, cần thiết ở nhiều nơi trong suốt chương trình, chỉ có sẵn khi chạy, ví dụ: bằng cách tải tệp dữ liệu. Hãy tưởng tượng một chương trình mà tất cả văn bản được bản địa hóa và được nạp từ một tệp tài nguyên khi chương trình bắt đầu, chẳng hạn. –

+0

Trong thực tế, nếu bất cứ điều gì âm thanh đáng ngờ với tôi đó là loại '[([Char], [Char])]'. Biết rằng đó là một môi trường, nó có vẻ nghi ngờ giống như một từ điển chuỗi, mà nên ở * ít nhất * là một chuỗi 'Data.Map.Map String' thay vào đó, nếu không phải thứ gì đó quyến rũ hơn như một [trêu chọc] đáng yêu (http: //hackage.haskell.org/package/bytestring-trie). –

Trả lời

5

Tôi nghĩ dễ nhất là nếu bạn xem cách bạn giải quyết vấn đề này mà không sử dụng Reader, sau đó so sánh phiên bản đã dịch. Dưới đây là một ví dụ được cắt xén từ một chương trình mà tôi đang làm việc trên môi trường là một tập các chức năng gọi lại để cập nhật màn hình. Nó phức tạp hơn một chút vì nó sử dụng ReaderT thay vì Reader, nhưng mọi thứ hoạt động theo cách cơ bản giống nhau.

runProcess :: Env -> State -> Action -> IO State 
runProcess env state action = do 
    newstate <- processAction state action 
    let ufunc = mainUFunc env    -- get the callback to update the display 
    ufunc newstate       -- update the display 
    return newstate 

Bây giờ tôi sẽ thay đổi nó để sử dụng trình đơn Reader để truyền dọc theo môi trường. Kể từ khi mã đã được trong IO, nó là cần thiết để sử dụng phiên bản biến áp monad, ReaderT.

runProcessR :: State -> Action -> ReaderT Env IO State 
runProcessR state action = do 
    newstate <- lift $ processAction state action 
    env <- ask        -- get the environment from the reader 
    liftIO $ (mainUFunc env) newstate  -- updating is in IO; it needs to be lifted 
    return newstate 

Tại thời điểm này, vòng lặp chính của chương trình về cơ bản sẽ là:

loop :: State -> ReaderT Env IO() 
loop = do 
    action <- liftIO getAction 
    if action == EndLoop 
    then return() 
    else do 
     st' <- processActionR st action 
     loop st' 

mainLoop :: IO() 
mainLoop = do 
    env <- setUpCallbacks 
    let st = initState 
    runReaderT $ loop st 

Vì vậy, đó là cách bạn có thể sử dụng Reader. Mỗi hàm sử dụng để lấy tham số môi trường không còn cần đến. Các hàm không lấy môi trường có thể được sử dụng trực tiếp hoặc được dỡ bỏ nếu chúng đơn thuần.

8

Đề án cơ bản để sử dụng bất kỳ đơn vị "bình thường" nào [0] là khá giống nhau trên bảng. Về cơ bản:

  • Viết chức năng mà trả về một giá trị kiểu monadic, sử dụng do ký hiệu nếu bạn thích, giống như bạn muốn viết một chức năng IO như main.
  • Sử dụng bất kỳ chức năng cụ thể nào cho đơn lẻ mà bạn đang làm việc.
  • Gọi các chức năng này với nhau, bằng cách sử dụng quy tắc tiêu chuẩn:
    • Bind một giá trị từ cùng đơn nguyên sử dụng một <- để có được giá trị "bên trong", khiến giá trị khác là "chạy" .
    • Liên kết bất kỳ giá trị nào khác bằng cách sử dụng let, để nó độc lập với cấu trúc đơn sắc.
  • Sử dụng chức năng "chạy" chuyên biệt của một đơn vị cụ thể để đánh giá tính toán đơn lẻ và nhận kết quả cuối cùng.

Làm điều đó và tất cả các chi tiết lộn xộn về chức năng bổ sung được mô tả bởi đơn nguyên (trong trường hợp này, truyền tham số môi trường xung quanh) được xử lý tự động.

Bây giờ, các hoạt động đọc thông thường là asklocal:

  • ask là một giá trị monadic giữ môi trường; trong một khối do bạn sử dụng nó giống như cách bạn sử dụng một cái gì đó như getLine trong đơn vị IO.
  • local có chức năng cung cấp môi trường mới và tính toán trong trình đơn Reader, chạy sau trong môi trường được sửa đổi trước đó, sau đó lấy kết quả và đặt nó vào hàm hiện tại. Nói cách khác, nó chạy một tính toán phụ với một môi trường biến đổi cục bộ.

Chức năng "chạy" được đặt tên một cách sáng tạo runReader, chỉ cần tính toán trong đơn vị Reader và giá trị môi trường, chạy giá trị cũ và trả về kết quả cuối cùng bên ngoài đơn nguyên.

Như một ví dụ, đây là một số chức năng làm một số tính toán vô nghĩa trong một đơn nguyên Reader, nơi mà môi trường là một "giá trị tối đa" mà nói khi nào phải ngừng:

import Control.Monad.Reader 

computeUpToMax :: (Int -> Int) -> Int -> Reader Int [Maybe Int] 
computeUpToMax f x = do 
    maxVal <- ask 
    let y = f x 
    if y > maxVal 
     then return [] 
     else do zs <- local (subtract y) (computeUpToMax f y) 
       z <- frob y 
       return (z:zs) 

frob :: Int -> Reader Int (Maybe Int) 
frob y = do 
    maxVal <- ask 
    let z = maxVal - y 
    if z == y 
     then return Nothing 
     else return $ Just z 

Để chạy nó, bạn muốn sử dụng một cái gì đó như thế này:

> runReader (computeUpToMax (+ 1) 0) 9 
[Just 8, Just 6, Nothing] 

... trong đó 9 là môi trường ban đầu. Bạn có thể sử dụng chính xác cấu trúc tương tự với các monads khác, chẳng hạn như State, Maybe hoặc [], mặc dù trong hai trường hợp sau, bạn thường chỉ sử dụng giá trị kết quả monadic cuối cùng thay vì sử dụng hàm "chạy" .

[0]: Trường hợp bình thường có nghĩa là không liên quan đến ma thuật trình biên dịch, đơn vị "bất thường" rõ ràng nhất đương nhiên là IO.

+0

Trường hợp nào ràng buộc '>> =' có hiệu lực? – CMCDragonkai

+1

@CMCDragonkai: Được sử dụng để dịch các khối 'do'. Một dòng giống như 'x <- foo' trở thành một liên kết' foo >> = \ x -> ', trong đó phần thân của lambda là (phiên bản đã dịch của) phần còn lại của khối' do'. –