2011-01-24 39 views
9

Tôi đang tìm cách viết một mô-đun chung cho phép các chương trình Haskell tương tác với Cassandra. Mô-đun sẽ cần phải duy trì trạng thái riêng của nó. Ví dụ, nó sẽ có một hồ bơi kết nối và một danh sách các cuộc gọi lại được gọi khi một bản ghi mới được lưu. Làm thế nào tôi nên cấu trúc mã để module này có thể duy trì trạng thái của nó? Dưới đây là một số phương pháp tôi đã cân nhắc. Có phải tôi đang trên đường ray bên phải không? (Tôi mới đến Haskell và vẫn đang học hỏi những cách tốt nhất để suy nghĩ về mặt chức năng.)Làm thế nào để bạn cấu trúc một mô-đun stateful trong Haskell?

Lựa chọn 1:

Module chạy trong một (StateT s IO) đơn nguyên, trong đó s là trạng thái toàn cầu cho toàn bộ chương trình sử dụng mô-đun Cassandra. Tất nhiên, vì mô-đun Cassandra có thể được sử dụng bởi nhiều chương trình, chi tiết về những gì trong s nên được ẩn với mô-đun Cassandra. Các mô-đun sẽ phải xuất khẩu một loại lớp cho phép nó để trích xuất CassandraState từ s và đẩy một CassandraState mới trở lại vào s. Sau đó, bất kỳ chương trình nào sử dụng mô-đun sẽ phải làm cho trạng thái chính của nó trở thành thành viên của lớp loại này.

Tùy chọn 2:

Mô-đun chạy trong đơn vị (StateT CassandraState IO). Mỗi khi ai đó gọi một hành động trong mô-đun, họ sẽ phải giải nén CassandraState từ bất cứ nơi nào mà nó có nó stashed, gọi hành động với runState, và lấy trạng thái kết quả và stash nó lại (bất cứ nơi nào).

Tùy chọn 3:

Không đặt các chức năng của mô đun Cassandra trong trạng thái StateT. Thay vào đó, có người gọi một cách rõ ràng vượt qua trong CassandraState khi cần thiết. Vấn đề với tùy chọn 2 là không phải tất cả các chức năng trong mô-đun sẽ thay đổi trạng thái. Ví dụ, có được một kết nối sẽ sửa đổi trạng thái và sẽ yêu cầu người gọi để stash off trạng thái kết quả. Tuy nhiên, việc lưu một bản ghi mới cần phải đọc trạng thái (để nhận các cuộc gọi lại), nhưng nó không cần phải thay đổi trạng thái. Tùy chọn 2 không cung cấp cho người gọi bất kỳ gợi ý nào kết nối thay đổi trạng thái trong khi tạo không.

Tuy nhiên, nếu tôi chuyển từ sử dụng đơn vị StateT và chỉ có các hàm tham gia trạng thái dưới dạng tham số và trả về giá trị đơn giản hoặc các giá trị đơn giản và trạng thái mới, thì thực sự rõ ràng đối với người gọi khi tiểu bang cần để được cứu. (Theo bao gồm trong mô-đun của tôi, tôi muốn đưa các tiểu bang đến và xây dựng chúng thành một (StateT CassandraState IO) monad, nhưng các chi tiết của điều này sẽ được ẩn từ người gọi.Vì vậy, để người gọi, giao diện là rất rõ ràng , nhưng dưới bìa, nó chỉ là Lựa chọn 2.)

Tùy chọn 4:

Cái gì khác?

Sự cố này phải xuất hiện khá thường xuyên khi xây dựng mô-đun có thể tái sử dụng. Có cách nào tiêu chuẩn để giải quyết nó không?

(Bằng cách này, nếu ai đó biết một cách tốt hơn để tương tác với Cassandra từ Haskell so với sử dụng tiết kiệm, xin vui lòng cho tôi biết! Có lẽ tôi không cần phải viết những dòng này ở tất cả. :-)

+2

FYI - trong mô-đun vòng kết nối Haskell 'là đơn vị biên dịch, nghĩa là - một tệp nguồn duy nhất. Mặc dù tôi không thực sự có một danh từ tốt hơn cho những gì bạn đang mô tả, nói về một 'mô-đun' có nhà nước ném tôi một chút. –

+0

Rất tiếc. Tôi nên nói 'gói' hoặc 'thư viện'. –

Trả lời

9

Giống như mô hình HDBC sẽ có loại dữ liệu CassandraConnection rõ ràng. Nó có một MVar bên trong với một số trạng thái có thể thay đổi. Vì tất cả các hành động của bạn đều trong IO, dù sao tôi cũng tưởng tượng, họ chỉ có thể lấy CassandraConnection làm đối số cho những hành động này. Người dùng sau đó có thể đóng gói kết nối đó vào một trạng thái hoặc trình đọc đơn vị, hoặc chuỗi nó một cách rõ ràng, hoặc làm bất cứ điều gì họ muốn.

Nội bộ, bạn có thể sử dụng một đơn vị hay không - đó thực sự là cuộc gọi của bạn. Tuy nhiên, tôi ưu tiên các API khi có thể không buộc người dùng vào bất kỳ đơn vị cụ thể nào trừ khi thật sự cần thiết.

Vì vậy, đây là phiên bản tùy chọn 3. Nhưng người dùng không thực sự quan tâm đến việc họ có thay đổi trạng thái kết nối hay không - ở cấp độ đó, bạn có thể ẩn chi tiết khỏi chúng.

+0

Tôi chỉ tìm thấy câu hỏi khác này nói về việc kết nối các kết nối HDBC bằng cách sử dụng MVar. Tôi nghĩ nó minh họa những gì bạn đang gợi ý. http://stackoverflow.com/questions/1141677/concurrent-db-connection-pool-in-haskell –

+0

Nếu, ngoài ra, bạn cấu trúc tất cả các loại chức năng của bạn để chúng kết thúc bằng '... -> CassandraConnection -> IO a', người dùng của bạn cũng có thể cực kỳ dễ dàng có được sự trừu tượng tương tự như trong tùy chọn 2 thông qua biến áp đơn nguyên 'ReaderT'. Chúng có thể chỉ đơn giản là bọc các hàm thư viện trong hàm tạo 'ReaderT'. E.G., 'foo :: A -> B -> CassandraConnection -> IO C' được bao bọc dưới dạng:' myFoo a b = ReaderT (foo a b) '. – mokus

+0

@Clint có vẻ tương tự. Về cơ bản, bạn muốn một xử lý trừu tượng cho một tài nguyên, với các hoạt động rõ ràng để kết nối, ngắt kết nối, v.v. – sclv

3

tôi 'd go with Option 2. Người dùng của mô-đun của bạn không được sử dụng trực tiếp runState; thay vào đó, bạn nên cung cấp loại Cassandra mờ đục với ví dụ về kiểu chữ Monad và một số hoạt động runCassandra :: Cassandra a -> IO a để "thoát" Cassandra. Tất cả hoạt động do mô-đun của bạn xuất tất cả sẽ chạy trong đơn lẻ Cassandra (ví dụ: doSomethingInterestingInCassandra :: Int -> Bool -> Cassandra Char) và định nghĩa của chúng có thể truy cập vào gói CassandraState.

Nếu người dùng của bạn cần một số trạng thái bổ sung cho ứng dụng của họ, họ luôn có thể quấn một máy biến áp đơn lẻ xung quanh Cassandra, ví dụ: StateT MyState Cassandra.

+2

Cảm ơn câu trả lời. Giả sử một ứng dụng không chỉ sử dụng mô-đun Cassandra của tôi, mà còn sử dụng một số mô-đun trạng thái khác. Giả sử rằng ứng dụng cần xác định trạng thái riêng của nó (MyState) và chạy trong đơn nguyên IO. Bạn có kết thúc với một bộ khó chịu của StateT nhúng bên trong của StateT, và trình tự khó chịu của nâng để có được bất cứ điều gì ra khỏi nó? Điều gì xảy ra trên đường khi ứng dụng bắt đầu sử dụng mô-đun trạng thái mới hoặc ngừng sử dụng mô-đun trạng thái? Điều đó có làm hỏng tất cả các StateT lồng nhau và nâng không? –

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