2013-10-31 15 views
10

Những gì tôi đang cố gắng làm là tầm thường để xác định bằng tay, về cơ bảnLàm thế nào để sử dụng các giá trị monoid và kết hợp với thao tác tùy chỉnh, dễ dàng?

maybeCombine :: (a->a->a) -> Maybe a -> Maybe a -> Maybe a 
maybeCombine _ Nothing Nothing = Nothing 
maybeCombine _ (Just a) Nothing = Just a 
maybeCombine _ Nothing (Just a) = Just a 
maybeCombine f (Just a) (Just a') = Just $ f a a' 

Nó không phải là một vấn đề lớn để define this locally when needed, nhưng vẫn cumbersone và đang được rất cơ bản và tổng quát có vẻ như cần có một thực hiện tiêu chuẩn, nhưng tôi dường như không thể tìm thấy một.

Có lẽ tôi chỉ đang xem cái gì đó. Những gì tôi muốn có vẻ khá không liên quan đến hành vi của các đơn nguyên có lẽ, vì vậy tôi nghĩ rằng tôi sẽ không tìm thấy bất cứ điều gì trong ngăn kéo Monad/Arrow; nhưng nó chắc chắn tương tự như Monoid dụ

Prelude Data.Monoid> Chỉ cần "a" <> Không có gì
Chỉ cần "a"
Prelude Data.Monoid> Chỉ cần "a" <> Chỉ cần "b"
chỉ cần "ab"
...

... mà tuy nhiên đòi hỏi a là một monoid chính nó, nghĩa là nó về cơ bản có a->a->a "được xây dựng trong". Các ví dụ MonadPlus cũng cư xử giống như tôi muốn, nhưng nó chỉ đơn giản là ném đi một trong những giá trị chứ không phải là cho phép tôi để cung cấp một chức năng kết hợp

Prelude Data.Monoid Control.Monad> Chỉ cần 4 'mplus` Không có gì
chỉ cần 4
Prelude Data.Monoid Control.Monad> Không có gì 'mplus` chỉ 4
chỉ 4
Prelude Data.Monoid Control.Monad> chỉ cần 4' mplus` chỉ 5
chỉ 4

Giải pháp kinh điển là gì? Kết hợp mẫu địa phương? Một cái gì đó với combinators từ ví dụ: Data.Maybe? Xác định một monoid tùy chỉnh để làm việc kết hợp?

Trả lời

11

Bạn có quyền nhận tiền khi bạn nhận thấy rằng f giống như hoạt động Monoid trên loại a cơ bản. Cụ thể hơn những gì đang xảy ra ở đây là bạn đang nâng một Semigroup vào một Monoid bằng cách nối một số không (mempty), Nothing.

Đây chính xác là những gì bạn thấy trong Haddocks cho MaybeMonoid thực sự.

Lift một -nửa nhóm vào Có lẽ tạo thành một monoid theo http://en.wikipedia.org/wiki/Monoid: "Bất cứ -nửa nhóm S có thể được biến thành một monoid đơn giản bằng cách tiếp giáp một e yếu tố không S và xác định e e = e và e s = s = s * e cho tất cả s ∈ S. " Vì không có "Semigroup" typeclass cung cấp chỉ là mappend, thay vào đó chúng ta sử dụng Monoid.

Hoặc, nếu bạn thích các gói semigroups, sau đó có Option trong đó có chính xác hành vi này, phù hợp khái quát hóa để sử dụng một nền tảng Semigroup để thay thế.


Vì vậy, cho thấy cách rõ ràng nhất là xác định hoặc là một Monoid hoặc Semigroup dụ vào loại cơ bản a. Đó là một cách sạch sẽ để kết hợp một số combiner f với loại đó.

Điều gì xảy ra nếu bạn không kiểm soát loại đó, không muốn các phiên bản trẻ mồ côi và nghĩ rằng trình bao bọc newtype là xấu? Thông thường bạn sẽ không may mắn, nhưng đây là một nơi mà sử dụng ma thuật đen tổng cộng, có hiệu quả là gói GHC chỉ reflection có ích. Giải thích thấu đáo tồn tại in the paper itself nhưng Ausin Seipp's FP Complete Tutorial bao gồm một số mã ví dụ để cho phép bạn "tiêm" các sản phẩm semigroup tùy ý vào các loại không có nhiễu định nghĩa loại (nhiều) ... với chi phí của một chữ ký đáng sợ hơn rất nhiều.  

Đó có thể là chi phí cao hơn đáng kể so với giá trị của nó.

+1

Tôi không gặp may, trong vấn đề này tôi đang làm việc ngay bây giờ tôi có thể thực sự sử dụng semigroup 'Max' và nhận được một giải pháp thực sự tốt đẹp! – leftaroundabout

+3

Tuyệt vời! Tôi thường kết thúc việc xác định 'Option (Max a)' như tiếp giáp với "infinity âm" trên một loại, vì vậy tuyệt đối. Tôi nghĩ đó là một "Monoid" thực sự tao nhã. –

11

Bạn luôn có thể sử dụng

f <$> m <*> n <|> m <|> n 

Nhưng điều này, thật đáng buồn, không có một thực hiện kinh điển bất cứ nơi nào.

Bạn có thể sử dụng reflection để nhận được rằng (a -> a -> a) "nướng trong" như Semigroup để sử dụng với Option, được cung cấp bởi semigroups như một phiên bản cải tiến của Maybe có trường hợp 'đúng' cho Monoid về Semigroup. Đây là bàn tay khá nặng nề cho vấn đề này. =)

Có lẽ điều này chỉ nên được thêm làm bộ tổ hợp thành Data.Maybe.

+0

Phải, tôi quên mất 'Thay thế'! Điều này là khá tốt, nhưng tôi nghĩ rằng tôi thích sự sang trọng của 'Semigroup'. – leftaroundabout

2
import Data.Monoid 
maybeCombine :: (a->a->a) -> Maybe a -> Maybe a -> Maybe a 
maybeCombine f mx my = let combine = mx >>= (\x -> my >>= (\y -> Just (f x y))) 
         in getFirst $ First combine `mappend` First mx `mappend` First` my 

trong GHCi, điều này mang lại cho tôi

*Main> maybeCombine (+) Nothing Nothing 
Nothing 
*Main> maybeCombine (+) (Just 3) Nothing 
Just 3 
*Main> maybeCombine (+) (Just 3) (Just 5) 
Just 8 

Cũng làm việc với getLast nếu bạn đặt Last combine vào cuối của mappend chuỗi

+0

Tôi thừa nhận đây không phải là một giải pháp chung như J. Abrahamson;) – itsbruce

+0

+1 để trả lời một khía cạnh chưa được nhấn mạnh trước đây (nhưng có lẽ là đơn giản nhất) của câu hỏi này ... nhưng thành thật, việc triển khai này không ngắn hơn hoặc dễ đọc hơn so với mô hình phù hợp với mô hình ngu ngốc ban đầu của tôi. Tôi cũng không thể thấy nó có thể thích nghi tốt hơn với các biến thể trong yêu cầu như thế nào. – leftaroundabout

+1

Bạn có thể chơi golf phương pháp này xuống một cái gì đó gần như là pithy như giải pháp của Ed-'getFirst. mconcat. map $ đầu tiên [liftA2 f mx my, mx, my] ', hoặc thậm chí thay thế' getFirst. mconcat. map First' với 'msum' mặc dù nó ít rõ ràng hơn nếu không có' First'. –

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