Tôi đang cố gắng trừu tượng hóa mô hình áp dụng một ngữ nghĩa nhất định cho một đơn nguyên miễn phí trên một số hàm. Ví dụ đang chạy mà tôi đang sử dụng để thúc đẩy việc này là áp dụng các bản cập nhật cho một thực thể trong trò chơi. Vì vậy, tôi nhập một vài thư viện và xác định một vài loại ví dụ và một lớp thực thể cho các mục đích của ví dụ này (Tôi đang sử dụng việc thực hiện đơn nguyên miễn phí trong kiểm soát đơn nguyên-miễn phí):Áp dụng ngữ nghĩa cho các Monads miễn phí
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Monad.Free
import Control.Monad.Identity
import Control.Monad.Writer
-- Things which can happen to an entity
data Order = Order deriving Show
data Damage = Damage deriving Show
class Entity a where
evolve :: Double -> a -> a
order :: Order -> a -> a
damage :: Damage -> a -> a
-- Make a trivial entity for testing purposes
data Example = Example deriving Show
instance Entity Example where
evolve _ a = a
order _ a = a
damage _ a = a
-- A type to hold all the possible update types
data EntityUpdate =
UpdateTime Double
| UpdateOrder Order
| UpdateDamage Damage
deriving (Show)
-- Wrap UpdateMessage to create a Functor for constructing the free monad
data UpdateFunctor cont =
UpdateFunctor {updateMessage :: EntityUpdate, continue :: cont} deriving (Show, Functor)
-- Type synonym for the free monad
type Update = Free UpdateEntity
bây giờ tôi nhấc một số cơ bản cập nhật vào đơn nguyên:
liftF = wrap . fmap Pure
updateTime :: Double -> Update()
updateTime t = liftUpdate $ UpdateTime t
updateOrder :: Order -> Update()
updateOrder o = liftUpdate $ UpdateOrder o
updateDamage :: Damage -> Update()
updateDamage d = liftUpdate $ UpdateDamage d
test :: Update()
test = do
updateTime 8.0
updateOrder Order
updateDamage Damage
updateTime 4.0
updateDamage Damage
updateTime 6.0
updateOrder Order
updateTime 8.0
Bây giờ chúng ta có đơn nguyên miễn phí, chúng ta cần phải cung cấp khả năng triển khai khác nhau, hoặc giải thích ngữ nghĩa, thẩm đơn nguyên như test
trên. Các mô hình tốt nhất mà tôi có thể đưa ra cho việc này được đưa ra bởi các chức năng sau:
interpret :: (Monad m, Functor f, fm ~ Free f c) => (f fm -> fm) -> (f fm -> a -> m a) -> fm -> a -> m a
interpret _ _ (Pure _ ) entity = return entity
interpret c f (Impure u) entity = f u entity >>= interpret c f (c u)
Sau đó, với một số chức năng ngữ nghĩa cơ bản chúng ta có thể cung cấp cho các giải thể hai sau, người ta như một đánh giá cơ bản và một như một đơn nguyên nhà văn preforming logging:
update (UpdateTime t) = evolve t
update (UpdateOrder o) = order o
update (UpdateDamage d) = damage d
eval :: Entity a => Update() -> a -> a
eval updates entity = runIdentity $ interpret continue update' updates entity where
update' u entity = return $ update (updateMessage u) entity
logMessage (UpdateTime t) = "Simulating time for " ++ show t ++ " seconds.\n"
logMessage (UpdateOrder o) = "Giving an order.\n"
logMessage (UpdateDamage d) = "Applying damage.\n"
evalLog :: Entity a => Update() -> a -> Writer String a
evalLog = interpret continue $ \u entity -> do
let m = updateMessage u
tell $ logMessage m
return $ update m entity
kiểm tra này trong GHCI:
> eval test Example
Example
> putStr . execWriter $ evalLog test Example
Simulating time for 8.0 seconds.
Giving an order.
Applying damage.
Simulating time for 4.0 seconds.
Applying damage.
Simulating time for 6.0 seconds.
Giving an order.
Simulating time for 8.0 seconds.
này tất cả hoạt động tốt, nhưng nó mang lại cho tôi một cảm giác hơi khó chịu mà nó có thể là mo nói chung, hoặc có thể được tổ chức tốt hơn. Có để cung cấp một chức năng để cung cấp sự tiếp tục không rõ ràng lúc đầu và tôi không chắc chắn đó là cách tiếp cận tốt nhất. Tôi đã thực hiện một số nỗ lực để xác định lại interpret
về các chức năng trong mô-đun Control.Monad.Free, chẳng hạn như foldFree
và induce
. Nhưng tất cả chúng dường như không hoàn toàn hoạt động.
Tôi có đúng đường với điều này hoặc đang đánh giá sai không? Hầu hết các bài viết về monads miễn phí tôi đã tìm thấy tập trung vào hiệu quả của họ hoặc cách khác nhau để thực hiện chúng, chứ không phải trên các mẫu để thực sự sử dụng chúng như thế này. Nó cũng có vẻ mong muốn đóng gói này trong một số loại Semantic
lớp, vì vậy tôi chỉ đơn giản có thể làm cho các trường hợp đơn nguyên khác nhau từ đơn nguyên miễn phí của tôi bằng cách gói functor trong một newtype và làm cho nó một thể hiện của lớp này. Tôi không thể hoàn toàn làm việc ra làm thế nào để làm điều này tuy nhiên.
CẬP NHẬT -
Tôi ước tôi có thể chấp nhận cả hai câu trả lời vì cả hai đều cực kỳ thông tin và được viết một cách chu đáo. Trong phần kết thúc, mặc dù chỉnh sửa để câu trả lời được chấp nhận bao gồm các chức năng sau khi tôi đã:
interpret :: (Functor m, Monad m) => (forall x. f x -> m x) -> Free f a -> m a
interpret evalF = retract . hoistFree evalF
(retract
và hoistFree
là trong gói miễn phí Edward Kemmet trong Control.Monad.Free).
Tất cả ba trong số pipes
, operational
và sacundim's free-operational package rất phù hợp và trông giống như chúng sẽ rất hữu ích cho tôi trong tương lai. Cảm ơn tất cả.
Điều này thực sự hấp dẫn! Thời gian để tôi có một cái nhìn rất khó khăn về những phiên bản khác nhau của 'giải thích' ... –