2013-09-07 29 views
14

Tôi hiện đang làm việc theo số Data.FreshControl.Monad.Trans.Fresh. xác định một giao diện để tạo ra các biến mới, và một biến áp đơn lẻ thực hiện giao diện này.Có thể thực hiện `(Áp dụng m) => Áp dụng (StateT s m)`?

Tôi bước đầu nghĩ rằng có thể triển khai cá thể Applicative cho số FreshT v m của mình với yêu cầu duy nhất là Applicative m tồn tại. Tuy nhiên, tôi gặp khó khăn và có vẻ như tôi cần phải yêu cầu Monad m. Không tin tưởng tôi Haskell-fu, tôi sau đó quay sang gói máy biến áp, và rất ngạc nhiên bởi những gì tôi tìm thấy trong Control.Monad.Trans.State.Lazy.Strict:

instance (Functor m, Monad m) => Applicative (StateT s m) where 
    pure = return 
    (<*>) = ap 

Vì vậy, đây là câu hỏi của tôi: là nó có thể tạo ra một thể hiện với ngữ nghĩa tương đương với người đứng đầu dụ sau đây?

instance (Applicative m) => Applicative (StateT s m) where 
+0

Điều thú vị là, có máy biến áp khác (ví dụ, 'WriterT',' ExceptT'), mà ' Ví dụ ứng dụng yêu cầu hàm tạo kiểu cơ bản là 'Monad'. – kirelagin

Trả lời

12

Hãy xem xét rằng bạn có hai chức năng:

f :: s -> m (s, a -> b) 
g :: s -> m (s, a) 

Và bạn muốn tạo một hàm h = StateT f <*> StateF g

h :: s -> m (s, b) 

Từ trên bạn có một s bạn có thể vượt qua để f vì vậy bạn có:

f' :: m (s, a -> b) 
g :: s -> m (s, a) 

Tuy nhiên, để có được s trong số f' bạn cần Monad (bất kỳ điều gì bạn muốn làm với đơn đăng ký, nó vẫn sẽ ở dạng m s để bạn không thể áp dụng giá trị cho g).

Bạn có thể chơi với định nghĩa và sử dụng free monad nhưng đối với sự sụp đổ của tiểu bang bạn cần join.

+0

Tôi đã nghĩ như vậy, vì tôi luôn phải đối mặt với một số 'm (m a)'. –

+1

@Rhymoid: Nếu bạn bỏ qua phần 'a -> b' bạn sẽ nhận được' f ':: m s' và 'g :: s -> m b'. Do đó, việc triển khai '<*>' cho 'StateT' sẽ cho phép thực thi' >> = 'cho' m' giả sử 's' có thể tùy ý. –

+0

Chắc chắn điều này là sai? StateT là một Monad, do đó, theo định nghĩa nó là một Applicative, và đó là lý do tại sao trong các thư viện tiêu chuẩn họ chỉ đơn giản là nhà nước '(<*>) = ap'. Nhưng điều đó có nghĩa là bạn PHẢI có thể thực hiện trực tiếp '(<*>) ', chỉ sử dụng' (<*>) 'hoặc' fmap' từ Áp dụng bên trong. Rắc rối là tôi không thể thấy làm thế nào để làm điều đó. –

5

Mặc dù, như đã nêu trong các câu trả lời trước, trường hợp này có thể không được định nghĩa nói chung, nó là đáng chú ý là, khi fApplicatives là một Monoid, StateT s f cũng là Applicative, vì nó có thể được coi là một thành phần của functors applicative: biến thể

StateT s f = Reader s `Compose` f `Compose` Writer s 
+0

Làm thế nào chính xác là functor ứng dụng này bạn định nghĩa liên quan đến 'StateT'? – kirelagin

4

Yếu của một biến Applicative

Mặc dù nó không phải là có thể xác định một biến applicative cho StateT, có thể xác định một biến thể yếu hơn hoạt động. Thay vì có s -> m (a, s), trong đó trạng thái quyết định hiệu ứng tiếp theo (do đó m phải là một đơn nguyên), chúng tôi có thể sử dụng m (s -> (a, s)) hoặc tương đương m (State s a).

import Control.Applicative 
import Control.Monad 
import Control.Monad.State 
import Control.Monad.Trans 

newtype StateTA s m a = StateTA (m (State s a)) 

Điều này hoàn toàn yếu hơn StateT.Mỗi StateTA thể được làm thành StateT (nhưng không phải ngược lại):

toStateTA :: Applicative m => StateTA s m a -> StateT s m a 
toStateTA (StateTA k) = StateT $ \s -> flip runState s <$> k 

Xác định FunctorApplicative chỉ là vấn đề thời nâng hoạt động của State vào cơ bản m:

instance (Functor m) => Functor (StateTA s m) where 
    fmap f (StateTA k) = StateTA $ liftM f <$> k 
instance (Applicative m) => Applicative (StateTA s m) where 
    pure = StateTA . pure . return 
    (StateTA f) <*> (StateTA k) = StateTA $ ap <$> f <*> k  

Và chúng ta có thể xác định một biến thể ứng dụng của lift:

lift :: (Applicative m) => m a -> StateTA s m a 
lift = StateTA . fmap return 

Cập nhật: Thực tế ở trên không cần thiết vì thành phần của hai ứng viên luôn là một hàm functor (không giống như monads). StateTA của chúng tôi là đẳng cấu với Compose m (State s), tự động là Applicative:

instance (Applicative f, Applicative g) => Applicative (Compose f g) where 
    pure x = Compose (pure (pure x)) 
    Compose f <*> Compose x = Compose ((<*>) <$> f <*> x) 

Vì vậy chúng tôi có thể viết chỉ

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 
import Control.Applicative 
import Control.Monad.State 
import Data.Functor.Compose 

newtype StateTA s m a = StateTA (Compose m (State s) a) 
    deriving (Functor, Applicative) 
+0

Rất thú vị. Trong ứng dụng của tôi, bản thân trạng thái (một nguồn cung cấp các giá trị mới) không nhất thiết phải quyết định hiệu ứng tiếp theo, nó thường được sao chép và trích xuất như thế nào. Một trong những việc triển khai của tôi có thể gặp vấn đề này, vấn đề còn lại chưa được thực hiện; có lẽ câu trả lời này có thể được sử dụng. Cảm ơn! –

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