2013-08-05 19 views
5

Tôi biết nó có thể thay đổi kiểu bọc, do đó bạn có thể cóCó thể thay đổi loại đơn nguyên theo một trình tự đơn sắc không?

f :: (a -> m b) 
g :: (b -> m c) 
f >>= g :: (a -> m c) 

nhưng là nó có thể thay đổi m? Nếu mMonadError và được triển khai cả bởi Either ErrorAEither ErrorB, tôi có thể xích chúng bằng cách nào đó không? Rõ ràng là tôi không thể xích chúng trực tiếp, bởi vì đây sẽ là loại Left? Tuy nhiên, tôi đang ở trong một tình huống mà tôi kết thúc gọi show trong cả hai trường hợp, nhưng tôi đã không tìm thấy một giải pháp tốt hơn so với

case mightFail1 of 
    Left e -> show e 
    Right v -> either show doStuff mightFail2 

mà thất bại trong việc sử dụng đúng các hành vi monadic dừng lại ở lỗi đầu tiên mà không tôi phải kiểm tra một cách rõ ràng.

+1

Như câu trả lời của J. Abrahamson cho biết, đây là một sự chuyển đổi tự nhiên giữa hai monads, còn được gọi là ** monph morphism **. Có một vài gói để hỗ trợ khái niệm này và các khái niệm tương tự; Tekmo có [một bài đăng blog trên gói 'mmorph' của anh ấy] (http://www.haskellforall.com/2013/03/mmorph-100-monad-morphisms.html) mà bạn có thể thử đọc. Nói chung, tuy nhiên, tôi tin rằng một hình thái giữa hai monads có thể cần phải có một số truy cập vào "ruột" của mỗi monad để dịch giữa chúng. –

Trả lời

6

Không thể thực hiện điều này trong một chuỗi monome.

Lưu ý rằng điều này không thực sự quan tâm với Monad ở tất cả: bạn không có bất kỳ cách nào ràng buộc hành động đơn lẻ lồng nhau trong đối số Left hoặc một cái gì đó tương tự, nhưng bạn chỉ đang chuyển đổi đối số. Đó là về cơ bản hoạt động functor fmap, nhưng trên trái thay vì phần bên phải:

fmap  :: (r->ρ) -> Either l r -> Either l ρ 
fmapLeft :: (l->λ) -> Either l r -> Either λ r 

Một chức năng với chữ ký đặc biệt, đáng ngạc nhiên, doesn't seem to exist. Tuy nhiên, ý tưởng này của một functor với hai đối số covariant rõ ràng là tổng quát hơn chỉ là Either, và thực sự là there's a dedicated class. Nó có (đặt tên IMO ấn tượng không hay, đụng độ với Arrow)

Data.Bifunctor.first :: (a -> b) -> p a c -> p b c 

chuyên trong thực tế để

first :: (a -> b) -> Either a c -> Either b c 

Vì vậy, bạn có thể sử dụng

f :: (Show a) => (Either a b) -> (Either String b) 
f = first show 
+0

bifunctors! hấp dẫn. Tôi đã kết thúc việc thực hiện 'mapLeft' trước câu trả lời của bạn, mà bây giờ chỉ là' đầu tiên'. – BruceBerry

1

StackOverflow là một con vịt cao su tuyệt vời. Tôi tìm thấy một giải pháp, nhưng tôi vẫn tò mò nếu có cách tiếp cận khác. Vì tôi đã viết xong câu hỏi, tôi sẽ đăng nó.

Thay vì nghĩ về "thay đổi loại đơn nguyên trong chuỗi", chỉ đơn giản là chuyển đổi tất cả các giá trị trước khi chúng được xiềng xích, bằng cách làm cho họ trở Either String a:

f :: (Show a) => (Either a b) -> (Either String b) 
f = either (Left . show) (Right) 

này kết thúc tốt đẹp toàn bộ cuộc gọi đến hoặc (f mightFail1) , có thể có một biến thể composable (f . mightFail1)

Chế độ điên: quấn vào một kiểu mới, làm cho nó trở thành một thể hiện của hàm ánh xạ bên trái thay vì bên phải và chỉ gọi fmap show mightFail1 (đừng quên để bọc và unwrap kiểu mới của bạn). Điều đó có ý nghĩa? : D

10

Toàn bộ khái niệm "thay đổi container "được gọi là" biến đổi tự nhiên ". Cụ thể, chúng tôi muốn một hàm chuyển đổi vùng chứa mà không ảnh hưởng đến nội dung bên trong. Chúng tôi có thể đảm bảo đây là trường hợp trong hệ thống kiểu bằng cách sử dụng forall.

-- natural transformation 
type (m :~> n) = forall a. m a -> n a 

Sau đó, chúng có thể được áp dụng bất cứ khi nào chúng tôi muốn. Ví dụ, nếu bạn có thể chuyển ErrorA -> ErrorB sau đó có một hoạt động chung của bạn

mapE :: (e -> e') -> (Either e :~> Either e') 
mapE f (Left e) = Left (f e) 
mapE _ (Right a) = a 

Bạn thậm chí có thể nhận được thực sự lạ mắt với những hoạt động kiểu và các loại tiền.

-- a generic sum type 
infixr 9 :+: 
newtype (a :+: b) = Inl a | Inr b 

liftE :: (Either e :~> Either (e' :+: e)) 
liftE = mapE Inr 

Bifunctors đạt được hiệu quả tương tự nhưng chúng là cách hoàn toàn khác để xem sự cố. Thay vì thường thay đổi vùng chứa, chúng ảnh hưởng đến tham số covariant khác (hoặc chỉ mục) trong chính vùng chứa. Vì vậy, các hành động Bifunctor luôn luôn có thể được xem là biến đổi tự nhiên, nhưng NTs là tổng quát hơn.

3

Đối với trường hợp này cụ thể mà bạn có thể sử dụng fmapL từ errors thư viện của tôi:

fmapL :: (a -> b) -> Either a r -> Either b r 

Vì bạn nói rằng bạn đang đi để show cả trong số họ cuối cùng, bạn có thể sử dụng fmapL show để thống nhất cả trong số họ đồng ý về các Either String đơn nguyên và trình tự trực tiếp:

do v <- fmapL show mightFail1 
    fmapL show $ mightFail2 v 

Bây giờ bạn có thể trình tự cả trong số họ sử dụng làm ký hiệu và họ đã chia sẻ những lỗi ha cùng ndling cơ chế.

Lưu ý rằng show không phải là cách duy nhất để hợp nhất các giá trị bên trái. Bạn cũng có thể hợp nhất các giá trị không thể hiển thị bằng ... Either!

example :: Either (Either Error1 Error2)() 
example = do 
    v <- fmapL Left mightFail1 
    fmapL Right $ mightFail2 v 
Các vấn đề liên quan