Tôi đang viết một chương trình chạy dưới dạng daemon. Để tạo daemon, người sử dụng cung cấp một tập hợp các triển khai cho mỗi lớp học cần thiết (một trong số đó là một cơ sở dữ liệu) Tất cả những lớp học có chức năng có loại chữ ký của các hình thức StateT s IO a
, nhưng s
là khác nhau cho mỗi lớp.Kết hợp nhiều tiểu bang trong StateT
Giả sử mỗi người trong số các lớp học sau mô hình này:
import Control.Monad (liftM)
import Control.Monad.State (StateT(..), get)
class Hammer h where
driveNail :: StateT h IO()
data ClawHammer = MkClawHammer Int -- the real implementation is more complex
instance Hammer ClawHammer where
driveNail = return() -- the real implementation is more complex
-- Plus additional classes for wrenches, screwdrivers, etc.
Bây giờ tôi có thể xác định một kỷ lục mà đại diện cho việc thực hiện lựa chọn bởi người dùng cho mỗi "khe".
data MultiTool h = MultiTool {
hammer :: h
-- Plus additional fields for wrenches, screwdrivers, etc.
}
Và daemon làm hầu hết công việc của mình trong đơn nguyên StateT (MultiTool h ...) IO()
.
Bây giờ, vì multitool chứa một cái búa, tôi có thể sử dụng nó trong mọi tình huống nơi cần búa. Nói cách khác, các MultiTool
loại có thể thực hiện bất kỳ của các lớp mà nó chứa, nếu tôi viết mã như thế này:
stateMap :: Monad m => (s -> t) -> (t -> s) -> StateT s m a -> StateT t m a
stateMap f g (StateT h) = StateT $ liftM (fmap f) . h . g
withHammer :: StateT h IO() -> StateT (MultiTool h) IO()
withHammer runProgram = do
t <- get
stateMap (\h -> t {hammer=h}) hammer runProgram
instance Hammer h => Hammer (MultiTool h) where
driveNail = withHammer driveNail
Nhưng việc triển khai của withHammer
, withWrench
, withScrewdriver
vv về cơ bản giống hệt nhau. Sẽ thật tuyệt nếu có thể viết một cái gì đó như thế này ...
--withMember accessor runProgram = do
-- u <- get
-- stateMap (\h -> u {accessor=h}) accessor runProgram
-- instance Hammer h => Hammer (MultiTool h) where
-- driveNail = withMember hammer driveNail
Nhưng tất nhiên điều đó sẽ không biên dịch.
Tôi nghi ngờ giải pháp của mình quá hướng đối tượng. Có cách nào tốt hơn không? Biến thế Monad, có thể? Cảm ơn bạn trước vì bất kỳ đề xuất nào.
Ngẫu nhiên, tôi đã chỉnh sửa nhanh mã của bạn vì đơn giản hóa của bạn bỏ qua việc thực hiện 'ClawHammer' bạn đã tạo ra thứ gì đó có lẽ không phải là ý của bạn. –