2012-06-12 34 views
5

Tôi có một mô-đun nơi môi trường toàn cầu (xác định các ràng buộc nhất định như địa chỉ IP lân cận, v.v.) được tạo và khởi tạo bằng cách gọi hàm khởi tạo. Một số hàm tiếp theo nên sử dụng các ràng buộc này khi chúng được gọi.Biến toàn cầu và đơn vị đọc

Trong khi về nguyên tắc, tôi hiểu những gì mà người đọc độc giả làm tôi không hoàn toàn chắc chắn làm thế nào tôi có thể áp dụng điều này cho vấn đề của tôi, đặc biệt.

  • Cách sử dụng để khởi tạo môi trường do người dùng xác định và chuyển thành dữ liệu/đối số cho hàm khởi tạo. Ý tôi là, người đọc độc giả phải có được những giá trị thực sự tạo nên môi trường bất biến toàn cầu từ đâu đó. Tôi muốn rằng các giá trị được đọc từ một cuộc gọi chức năng Khởi tạo như myinitial :: arg1 -> arg1 -> IOString nơi sau đó arg1arg2 trở thành toàn cầu dữ liệu bất biến thể truy cập đến các chức năng sau này thông qua đơn nguyên đầu đọc (?)

  • Làm thế nào tôi có thể sử dụng các giá trị môi trường như các đối số chức năng ví dụ recvFrom s arg1 trong đó arg1 là dữ liệu bất biến toàn cầu từ môi trường của tôi. Hoặc if arg2 > arg1 then ... else ...

Tôi có thể tạo tệp cấu hình, nhưng tôi cảm thấy rằng tệp cấu hình sẽ mất nhiều tính linh hoạt.

[Chỉnh sửa] Tôi hiểu về yêu cầu, nhưng không nên có thêm các cách "không giống như điểm" sao cho toàn cầu/môi trường không thay đổi có thể bị bỏ qua nếu chữ ký hàm đã được xác định đúng không? Làm cách nào để tôi có thể tái cấu trúc if-then-else của mình để áp dụng this.

Trả lời

4

Đây là một ví dụ có thể xóa mọi thứ.Trước tiên, bạn cần phải nhập khẩu các mô-đun Reader:

import Control.Monad.Reader 

Bây giờ chúng ta hãy xác định một số cấu trúc dữ liệu (mà chúng ta sẽ sử dụng để tổ chức một tên và tuổi)

data Config = Config { name :: String, age :: Int } 

Bây giờ xác định một hàm hoạt động trong trình đơn Reader (loại của nó là Reader Config (String, Int) nhưng chúng tôi không cần chỉ định điều đó - nó có thể được suy ra). Tất cả các chức năng này làm là yêu cầu môi trường (loại Config) và sau đó chiết xuất các lĩnh vực và làm một cái gì đó với họ.

example = do 
    c <- ask 
    return ("Hello " ++ name c, 2 * age c) 

Bây giờ chúng tôi kết hợp tất cả lại với nhau thành một chương trình. Bốn dòng đầu tiên sau khối lệnh cho phép người dùng nhập tên và tuổi của họ. Sau đó, chúng tôi tạo cấu trúc Config bằng cách sử dụng đầu vào của người dùng (chúng tôi phải sử dụng read để chuyển đổi biến _age, thành Int để chúng tôi có thể cấp nó cho nhà xây dựng Config) và thực thi example với môi trường này. runReader chức năng. Cuối cùng, chúng tôi sử dụng kết quả tính toán này để tạo ra một số đầu ra.

main = do 
    putStrLn "Enter your name:" 
    _name <- getLine 
    putStrLn "Enter your age:" 
    _age <- getLine 
    let config = Config _name (read _age) 
    let result = runReader example config 
    putStrLn $ fst result 
    putStrLn $ "Twice your age is: " ++ show (snd result) 
+0

Hoạt động một phần, như đã đề cập trong câu hỏi tôi có ** mô-đun ** có một số hàm; trong số đó là một hàm khởi tạo tạo môi trường. Các hàm từ mô đun được gọi sau này nên sử dụng môi trường. Điều đó có thể xảy ra với người đọc không? –

+0

Nhưng cách này hoạt động như thế nào? Trong ví dụ của bạn/hàm khởi tạo sẽ nói một cái gì đó như 'let config = Config blabla' và một hàm tiếp theo sẽ cần phải làm' let result = runReader example config'; - tuy nhiên chức năng tiếp theo không biết 'config'. –

+0

Kiểm tra [gist này] (https://gist.github.com/2924160) trong đó có một ví dụ đơn giản. Trong mô-đun 1, chúng tôi định nghĩa một hàm khởi tạo môi trường toàn cục và một số hàm phụ thuộc vào môi trường toàn cục. Module 2 chỉ chứa một hàm chính khởi tạo môi trường và gọi các hàm phụ thuộc vào nó bằng cách sử dụng 'runReader' (** Edit: ** xin lỗi, tôi đã xóa chú thích gốc mà bạn đã trả lời và thay thế nó bằng cái này.) –

5

Hầu hết các câu hỏi của bạn có thể được trả lời bằng cách kiểm tra các loại và tài liệu cho các chức năng askrunReader.

Thứ nhất, ask:

ask :: Reader m r => m r 

này trả về chỉ đọc dữ liệu cơ bản được bọc trong các đơn nguyên. Mát mẻ, vì vậy đó là cách bạn sẽ nhận được với nhà nước khi bạn muốn sử dụng nó với các chức năng khác, trong ví dụ của bạn ở trên:

do x <- ask 
    recvFrom s x 

(tùy thuộc vào loại recvFrom, tất nhiên)

Tiếp theo là runReader, đây là cách bạn cung cấp cho nó dữ liệu ban đầu mà bạn đang nói đến. Về cơ bản nó chỉ chạy Reader tính toán bằng cách sử dụng dữ liệu nó đưa ra:

runReader :: Reader r a -> r -> a 

có nghĩa là: chạy việc tính toán (đối số đầu tiên) với các dữ liệu chỉ đọc của loại r (đối số thứ hai). Cuối cùng, nó sẽ trả về kiểu kết quả của đối số thứ nhất, a. Trong trường hợp của bạn, điều này có thể trông giống như:

result = runReader computationUsingArg1Arg2 (arg1, arg2) 

Sau đó bên computationUsingArg1Arg2 bạn có thể đọc arg1arg2 qua ask.

+0

Cảm ơn, tôi vẫn không hiểu cách áp dụng 'runReader' vào ví dụ của tôi một trong những câu hỏi của tôi. –

+0

Tôi đã mở rộng câu trả lời một chút, hãy cho tôi biết nếu vẫn còn điều gì đó không rõ ràng. – ScottWest

+0

Tôi biết đó là một năm sau đó nhưng với tôi điều này không trả lời câu hỏi. Vấn đề không chỉ là có compuationUsingArg1Arg2, nhưng tính toánUsingArg1Arg2 có thể muốn có khả năng gọi các hàm KHÁC cũng cần truy cập vào thông tin "toàn cầu". Nếu không, bạn chỉ có thể vượt qua các tham số trực tiếp và không cần người đọc. Vậy tất cả các hàm khác được định nghĩa và được gọi ra như thế nào? – David