2011-09-14 29 views
5

Xin lỗi nếu câu hỏi có vẻ hơi tầm thường ... nó không phải dành cho tôi. Tôi đã hạnh phúc bao gồm các đơn nguyên sau:Tóm tắt thành phần đơn lẻ làm biến áp

type SB i a = ReaderT (AlgRO i) (State (AlgState i)) a 

đó là, tốt, một đơn nguyên cư xử rất tốt. ReaderT là một biến thể đơn nguyên và Trạng thái là trạng thái đơn nguyên, và AlgRO và AlgState là các kiểu dữ liệu parametrized trong i cho trạng thái có thể thay đổi và chỉ đọc, tương ứng. Bây giờ, nếu tôi muốn tạo một biến thể đơn giản gọn gàng với newtype, một cái gì đó như thế này:

newtype SbT m i a = SbT { 
    runSbT:: m (SB i a) 
} 

Tôi nên tiến hành như thế nào? Tôi thậm chí không thể quản lý để kết hợp phương pháp liên kết (của Monad typeclass), ít hơn "nâng" (của MonadTrans) ... Tôi đoán rằng derivation tự động có thể giúp, nhưng tôi muốn hiểu nó hoạt động như thế nào trong trường hợp này.

Xin cảm ơn trước.

Trả lời

10

Tôi không nghĩ rằng định nghĩa cho SbT là những gì bạn muốn. Điều đó xác định thành phần functor và giả sử tham số mFunctor hoặc Applicative, điều này sẽ giữ nguyên các thuộc tính đó. Nhưng thành phần như vậy không, nói chung, tạo ra một đơn nguyên mới ra khỏi hai người khác. Xem this question để biết thêm về chủ đề đó.

Vì vậy, cách làm bạn tạo biến áp đơn lẻ bạn muốn, sau đó? Trong khi các monads không soạn trực tiếp, các trình biến đổi đơn lẻ có thể được sáng tác. Vì vậy, để xây dựng một biến mới ra khỏi những cái hiện có, về cơ bản bạn chỉ muốn đặt tên cho thành phần đó. Điều này khác với newtype bạn có bởi vì bạn đang áp dụng trực tiếp m thay vì chuyển nó vào ngăn máy biến áp. Một điều cần lưu ý về việc xác định biến thế đơn nguyên là chúng nhất thiết phải làm việc "ngược" theo những cách nhất định - khi bạn áp dụng máy biến áp tổng hợp thành một đơn nguyên, biến áp "trong cùng" sẽ là vết nứt đầu tiên ở đó, và đơn nguyên được biến đổi mà nó tạo ra là biến thế tiếp theo được làm việc với, & c. Lưu ý rằng điều này không khác với thứ tự bạn nhận được khi áp dụng một hàm được soạn thảo cho một đối số, ví dụ: (f . g . h) x đặt đối số cho h trước tiên, mặc dù f là hàm "đầu tiên" trong bố cục.

Được rồi, vì vậy biến áp tổng hợp của bạn cần phải lấy đơn nguyên nó được áp dụng đến và vượt qua nó để máy biến áp trong cùng, đó là, uhm .... oops, chỉ ra rằng SBđã áp dụng cho một đơn nguyên. Không có gì lạ khi nó không hoạt động. Chúng tôi sẽ cần phải loại bỏ điều đó, trước tiên. Nó đâu rồi? Không phải State - chúng tôi có thể xóa điều đó, nhưng chúng tôi không muốn, bởi vì đó là một phần của những gì bạn muốn. Hmm, nhưng chờ đợi - State được định nghĩa là gì? Oh yeah:

type State s = StateT s Identity 

Aha, chúng tôi đi. Hãy lấy số Identity ra khỏi đó.Chúng tôi đi từ định nghĩa hiện tại của bạn:

type SB i a = ReaderT (AlgRO i) (State (AlgState i)) a 

Đối với hình thức tương đương:

type SB i a = ReaderT (AlgRO i) (StateT (AlgState i) Identity) a 

Sau đó, chúng tôi đá bum lười biếng ra:

type SB' i m a = ReaderT (AlgRO i) (StateT (AlgState i) m) a 
type SB i a = SB' i Identity a 

Nhưng bây giờ SB' vẻ nghi ngờ như một biến đơn nguyên định nghĩa, và với lý do chính đáng, bởi vì nó là. Vì vậy, chúng tôi tái tạo newtype wrapper, và quăng một vài trường hợp ngoài kia:

newtype SbT i m a = SbT { getSB :: ReaderT (AlgRO i) (StateT (AlgState i) m) a } 

instance (Functor m) => Functor (SbT i m) where 
    fmap f (SbT sb) = SbT (fmap f sb) 

instance (Monad m) => Monad (SbT i m) where 
    return x = SbT (return x) 
    SbT m >>= k = SbT (m >>= (getSB . k)) 

instance MonadTrans (SbT i) where 
    lift = SbT . lift . lift 

runSbT :: SbT i m a -> AlgRO i -> AlgState i -> m (a, AlgState t) 
runSbT (SbT m) e s = runStateT (runReaderT m e) s 

Một vài điều cần lưu ý: Các runSbT chức năng ở đây không phải là accessor lĩnh vực, mà là một sáng tác "chạy" chức năng cho mỗi biến áp trong ngăn xếp mà chúng ta biết. Tương tự, chức năng lift phải nâng một lần cho hai máy biến áp bên trong, sau đó thêm lớp phủ newtype cuối cùng. Cả hai đều làm cho nó hoạt động như một biến áp đơn lẻ, che giấu thực tế rằng nó thực sự là một hỗn hợp.

Nếu bạn muốn, bạn nên viết đơn giản cho các trường hợp MonadReaderMonadState bằng cách nâng các phiên bản cho máy biến áp đã soạn.

+0

Điều đó sẽ làm điều đó. Cảm ơn! – dsign

2

Bạn có định quấn thêm m những thứ khác trong kiểu mới của mình không? Tôi sẽ đề xuất những điều sau đây:

newtype Sb i a = Sb { runSb :: SB i a } 

... điều này sẽ giúp bạn dễ viết hơn instance Monad (Sb i). Nếu bạn đang thực sự cố gắng để viết một biến áp monad, sau đó bạn nên sử dụng máy biến áp tất cả các con đường xuống; ví dụ,

type SBT m i a = ReaderT (AlgRO i) (StateT (AlgState i) m) a 
newtype SbT m i a = SbT { runSbT :: SBT m i a } 

Là một điểm thứ hai về lợi ích, nó thường thích hợp hơn để η-giảm type từ đồng nghĩa (vì chúng phải luôn luôn là "hoàn toàn ứng dụng"); làm điều này với SBSBT sẽ trông giống như sau:

type SB i = ReaderT (AlgRO i) (State (AlgState i)) 
type SBT m i = ReaderT (AlgRO i) (StateT (AlgState i) m) 
+0

Nhân tiện, bạn không thể tạo ra một biến áp đơn lẻ trong 'SbT' theo cách đó. Transformers có loại '(* -> *) -> * -> *', tức là, lấy một đơn nguyên và một kiểu làm đối số. Tham số 'i' phải là đầu tiên, vì vậy bạn có thể áp dụng một phần nó. –

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