Là mỗi Monad thay thế có thể lọc?


8

Các loại của bộ là cả monoidal cartoidian và cocartesian monoidal. Các loại của đẳng cấu chính tắc chứng kiến ​​hai cấu trúc đơn hình này được liệt kê dưới đây:

type x + y = Either x y
type x × y = (x, y)

data Iso a b = Iso { fwd :: a -> b, bwd :: b -> a }

eassoc :: Iso ((x + y) + z) (x + (y + z))
elunit :: Iso (Void + x) x
erunit :: Iso (x + Void) x

tassoc :: Iso ((x × y) × z) (x × (y × z))
tlunit :: Iso (() × x) x
trunit :: Iso (x × ()) x

Đối với các mục đích của câu hỏi này, tôi xác định Alternativelà một functor đơn hình lỏng lẻo từ Hask dưới Eithertenor đến Hask theo tenor (,)(và không còn nữa):

class Functor f => Alt f
  where
  union :: f a × f b -> f (a + b)

class Alt f => Alternative f
  where
  nil :: () -> f Void

Các luật chỉ là những luật cho một functor lỏng lẻo.

Kết hợp:

fwd tassoc >>> bimap id union >>> union
=
bimap union id >>> union >>> fmap (fwd eassoc)

Đơn vị còn lại:

fwd tlunit
=
bimap nil id >>> union >>> fmap (fwd elunit)

Đúng đơn vị:

fwd trunit
=
bimap id nil >>> union >>> fmap (fwd erunit)

Dưới đây là cách phục hồi các thao tác quen thuộc hơn cho kiểu chữ Alternativetheo các bản đồ kết hợp của mã hóa functor đơn hình lỏng lẻo:

(<|>) :: Alt f => f a -> f a -> f a
x <|> y = either id id <$> union (Left <$> x, Right <$> y)

empty :: Alternative f => f a
empty = absurd <$> nil ()

Tôi xác định Filterablefunctors là oplax functors monoidal từ Hask dưới Eithertensor để Hask dưới (,)tensor:

class Functor f => Filter f
  where
  partition :: f (a + b) -> f a × f b

class Filter f => Filterable f
  where
  trivial :: f Void -> ()
  trivial = const ()

Có luật của nó chỉ ngược lại luật functor lỏng lẻo:

Kết hợp:

bwd tassoc <<< bimap id partition <<< partition
=
bimap partition id <<< partition <<< fmap (bwd eassoc)

Đơn vị còn lại:

bwd tlunit
=
bimap trivial id <<< partition <<< fmap (bwd elunit)

Đúng đơn vị:

bwd trunit
=
bimap id trivial <<< partition <<< fmap (bwd erunit)

Xác định các hàm bộ lọc-y tiêu chuẩn như mapMaybefiltervề mặt mã hóa functor đơn hình oplax còn lại như một bài tập cho người đọc quan tâm:

mapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
mapMaybe = _

filter :: Filterable f => (a -> Bool) -> f a -> f a
filter = _

Câu hỏi là đây: có phải Alternative Monadcũng Filterablevậy không?

Chúng ta có thể gõ tetris theo cách của chúng ta để thực hiện:

instance (Alternative f, Monad f) => Filter f
  where
  partition fab = (fab >>= either return (const empty), fab >>= either (const empty) return)

Nhưng việc thực hiện này luôn luôn hợp pháp? Có đôi khi hợp pháp (đối với một số định nghĩa chính thức của "đôi khi")? Bằng chứng, phản mẫu và / hoặc các đối số không chính thức đều rất hữu ích. Cảm ơn.


Cá nhân, tôi sẽ khá ngạc nhiên nếu nó luôn hợp lệ. Nó chắc chắn đôi khi hợp lệ theo nghĩa là có tồn tại functor mà nó hợp lệ, nhưng tôi có xu hướng nghi ngờ đó là một lớp đặc biệt thú vị.
dfeuer

@dfeuer Bạn có bất kỳ phản biện nào trong đó rõ ràng là không hợp lệ không? Có lẽ đó sẽ là hướng dẫn.
Asad Saeeduddin

Tôi khá chắc chắn rằng đây luôn là một ví dụ hợp pháp, chỉ từ việc mở ra các định nghĩa và một số cách viết lại tầm thường (do đó cho thấy rằng các Filterableluật này khá yếu). @AsadSaeeduddin Cân nhắc chọn một số kỹ năng chứng minh định lý tương tác để bạn có thể mở rộng tâm lý "sử dụng chứ không phải bộ não của bạn" để chứng minh!
Li-yao Xia

Câu trả lời:


3

Đây là một lập luận ủng hộ rộng rãi cho ý tưởng đẹp của bạn.

Phần một: mapMaybe

Kế hoạch của tôi ở đây là khôi phục vấn đề về mặt mapMaybe, hy vọng rằng làm như vậy sẽ đưa chúng ta đến một nền tảng quen thuộc hơn. Để làm như vậy, tôi sẽ sử dụng một vài Eitherchức năng tiện ích:

maybeToRight :: a -> Maybe b -> Either a b
rightToMaybe :: Either a b -> Maybe b
leftToMaybe :: Either a b -> Maybe a
flipEither :: Either a b -> Either b a

(Tôi mất ba tên đầu tiên từ relude , và thứ tư từ lỗi . Bằng cách này, lỗi Mời maybeToRightrightToMaybenhư notehushtương ứng trong Control.Error.Util.)

Như bạn đã lưu ý, mapMaybecó thể được định nghĩa theo partition:

mapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
mapMaybe f = snd . partition . fmap (maybeToRight () . f)

Điều quan trọng, chúng ta cũng có thể đi theo con đường khác:

partition :: Filterable f => f (Either a b) -> (f a, f b)
partition = mapMaybe leftToMaybe &&& mapMaybe rightToMaybe

Điều này cho thấy nó có ý nghĩa để làm lại luật của bạn về mặt mapMaybe. Với luật định danh, làm như vậy cho chúng ta một lý do tuyệt vời để quên hoàn toàn về trivial:

-- Left and right unit
mapMaybe rightToMaybe . fmap (bwd elunit) = id  -- [I]
mapMaybe leftToMaybe . fmap (bwd erunit) = id   -- [II]

Đối với tính kết hợp, chúng ta có thể sử dụng rightToMaybeleftToMaybephân chia luật theo ba phương trình, một cho mỗi thành phần chúng ta nhận được từ các phân vùng kế tiếp nhau:

-- Associativity
mapMaybe rightToMaybe . fmap (bwd eassoc)
    = mapMaybe rightToMaybe . mapMaybe rightToMaybe  -- [III]
mapMaybe rightToMaybe . mapMaybe leftToMaybe . fmap (bwd eassoc)
    = mapMaybe leftToMaybe . mapMaybe rightToMaybe   -- [IV]
mapMaybe leftToMaybe . fmap (bwd eassoc)
    = mapMaybe leftToMaybe . mapMaybe leftToMaybe    -- [V]

Phương tiện tham số mapMaybelà bất khả tri đối với các Eithergiá trị chúng ta đang giải quyết ở đây. Điều đó là như vậy, chúng ta có thể sử dụng kho vũ khí Eitherđồng phân nhỏ của mình để xáo trộn mọi thứ xung quanh và cho thấy [I] tương đương với [II] và [III] tương đương với [V]. Bây giờ chúng tôi xuống đến ba phương trình:

mapMaybe rightToMaybe . fmap (bwd elunit) = id       -- [I]
mapMaybe rightToMaybe . fmap (bwd eassoc)
    = mapMaybe rightToMaybe . mapMaybe rightToMaybe  -- [III]
mapMaybe rightToMaybe . mapMaybe leftToMaybe . fmap (bwd eassoc)
    = mapMaybe leftToMaybe . mapMaybe rightToMaybe   -- [IV]

Tham số cho phép chúng ta nuốt fmapvào [I]:

mapMaybe (rightToMaybe . bwd elunit) = id

Tuy nhiên, điều đó chỉ đơn giản là ...

mapMaybe Just = id

... đó là tương đương với định luật bảo toàn / danh từ witherable 'sFilterable :

mapMaybe (Just . f) = fmap f

Điều đó Filterablecũng có luật thành phần:

-- The (<=<) is from the Maybe monad.
mapMaybe g . mapMaybe f = mapMaybe (g <=< f)

Chúng ta cũng có thể rút ra điều này từ luật của chúng ta? Hãy bắt đầu từ [III] và, một lần nữa, có tham số thực hiện công việc của nó. Cái này khó hơn, vì vậy tôi sẽ viết nó đầy đủ:

mapMaybe rightToMaybe . fmap (bwd eassoc)
    = mapMaybe rightToMaybe . mapMaybe rightToMaybe  -- [III]

-- f :: a -> Maybe b; g :: b -> Maybe c
-- Precomposing fmap (right (maybeToRight () . g) . maybeToRight () . f)
-- on both sides:
mapMaybe rightToMaybe . fmap (bwd eassoc)
  . fmap (right (maybeToRight () . g) . maybeToRight () . f)
    = mapMaybe rightToMaybe . mapMaybe rightToMaybe 
      . fmap (right (maybeToRight () . g) . maybeToRight () . f)

mapMaybe rightToMaybe . mapMaybe rightToMaybe 
  . fmap (right (maybeToRight () . g) . maybeToRight () . f)  -- RHS
mapMaybe rightToMaybe . fmap (maybeToRight () . g)
  . mapMaybe rightToMaybe . fmap (maybeToRight () . f)
mapMaybe (rightToMaybe . maybeToRight () . g)
 . mapMaybe (rightToMaybe . maybeToRight () . f)
mapMaybe g . mapMaybe f

mapMaybe rightToMaybe . fmap (bwd eassoc)
  . fmap (right (maybeToRight () . g) . maybeToRight () . f)  -- LHS
mapMaybe (rightToMaybe . bwd eassoc 
    . right (maybeToRight () . g) . maybeToRight () . f)
mapMaybe (rightToMaybe . bwd eassoc 
    . right (maybeToRight ()) . maybeToRight () . fmap @Maybe g . f)
-- join @Maybe
--     = rightToMaybe . bwd eassoc . right (maybeToRight ()) . maybeToRight ()
mapMaybe (join @Maybe . fmap @Maybe g . f)
mapMaybe (g <=< f)  -- mapMaybe (g <=< f) = mapMaybe g . mapMaybe f

Theo hướng khác:

mapMaybe (g <=< f) = mapMaybe g . mapMaybe f
-- f = rightToMaybe; g = rightToMaybe
mapMaybe (rightToMaybe <=< rightToMaybe)
    = mapMaybe rightToMaybe . mapMaybe rightToMaybe
mapMaybe (rightToMaybe <=< rightToMaybe)  -- LHS
mapMaybe (join @Maybe . fmap @Maybe rightToMaybe . rightToMaybe)
-- join @Maybe
--     = rightToMaybe . bwd eassoc . right (maybeToRight ()) . maybeToRight ()
mapMaybe (rightToMaybe . bwd eassoc 
    . right (maybeToRight ()) . maybeToRight ()
      . fmap @Maybe rightToMaybe . rightToMaybe)
mapMaybe (rightToMaybe . bwd eassoc 
    . right (maybeToRight () . rightToMaybe) 
      . maybeToRight () . rightToMaybe)
mapMaybe (rightToMaybe . bwd eassoc)  -- See note below.
mapMaybe rightToMaybe . fmap (bwd eassoc)
-- mapMaybe rightToMaybe . fmap (bwd eassoc)
--     = mapMaybe rightToMaybe . mapMaybe rightToMaybe

(Lưu ý: Mặc dù maybeToRight () . rightToMaybe :: Either a b -> Either () bkhông id, trong đạo hàm trên, các giá trị bên trái sẽ bị loại bỏ, vì vậy thật công bằng khi loại bỏ nó như thể nó là id.)

Vì vậy [III] tương đương với luật thành phần của witherable 's Filterable.

Tại thời điểm này, chúng ta có thể sử dụng luật thành phần để đối phó với [IV]:

mapMaybe rightToMaybe . mapMaybe leftToMaybe . fmap (bwd eassoc)
    = mapMaybe leftToMaybe . mapMaybe rightToMaybe   -- [IV]
mapMaybe (rightToMaybe <=< leftToMaybe) . fmap (bwd eassoc)
    = mapMaybe (letfToMaybe <=< rightToMaybe)
mapMaybe (rightToMaybe <=< leftToMaybe . bwd eassoc)
    = mapMaybe (letfToMaybe <=< rightToMaybe)
-- Sufficient condition:
rightToMaybe <=< leftToMaybe . bwd eassoc = letfToMaybe <=< rightToMaybe
-- The condition holds, as can be directly verified by substiuting the definitions.

Điều này đủ cho thấy số lượng lớp học của bạn với một công thức được thiết lập tốt Filterable, đó là một kết quả rất tốt đẹp. Đây là một bản tóm tắt của các luật:

mapMaybe Just = id                            -- Identity
mapMaybe g . mapMaybe f = mapMaybe (g <=< f)  -- Composition

Như các tài liệu có thể khắc phục được , đây là các luật functor cho một functor từ Kleisli Có thể đến Hask .

Phần hai: Thay thế và Monad

Bây giờ chúng tôi có thể giải quyết câu hỏi thực tế của bạn, đó là về các đơn nguyên thay thế. Đề xuất của bạn partitionlà:

partitionAM :: (Alternative f, Monad f) => f (Either a b) -> (f a, f b)
partitionAM
    = (either return (const empty) =<<) &&& (either (const empty) return =<<)

Theo kế hoạch rộng hơn của tôi, tôi sẽ chuyển sang mapMaybethuyết trình:

mapMaybe f
snd . partition . fmap (maybeToRight () . f)
snd . (either return (const empty) =<<) &&& (either (const empty) return =<<)
    . fmap (maybeToRight () . f)
(either (const empty) return =<<) . fmap (maybeToRight () . f)
(either (const empty) return . maybeToRight . f =<<)
(maybe empty return . f =<<)

Và vì vậy chúng ta có thể định nghĩa:

mapMaybeAM :: (Alternative f, Monad f) => (a -> Maybe b) -> f a -> f b
mapMaybeAM f u = maybe empty return . f =<< u

Hoặc, trong một lỗi chính tả:

mapMaybeAM = (=<<) . (maybe empty return .)

Một vài đoạn trên, tôi lưu ý rằng các Filterableluật nói rằng đó mapMaybelà ánh xạ hình thái của một functor từ Kleisli Có thể đến Hask . Kể từ khi thành phần của functors là một functor, và (=<<)là ánh xạ cấu xạ của một functor từ Kleisli f để Hask , (maybe empty return .)là lập bản đồ cấu xạ của một functor từ Kleisli lẽ để Kleisli f đủ cho mapMaybeAMlà hợp pháp. Các luật functor có liên quan là:

maybe empty return . Just = return  -- Identity
maybe empty return . g <=< maybe empty return . f
    = maybe empty return . (g <=< f)  -- Composition

Luật định danh này có hiệu lực, vì vậy hãy tập trung vào thành phần một:

maybe empty return . g <=< maybe empty return . f
    = maybe empty return . (g <=< f)
maybe empty return . g =<< maybe empty return (f a)
    = maybe empty return (g =<< f a)
-- Case 1: f a = Nothing
maybe empty return . g =<< maybe empty return Nothing
    = maybe empty return (g =<< Nothing)
maybe empty return . g =<< empty = maybe empty return Nothing
maybe empty return . g =<< empty = empty  -- To be continued.
-- Case 2: f a = Just b
maybe empty return . g =<< maybe empty return (Just b)
    = maybe empty return (g =<< Just b)
maybe empty return . g =<< return b = maybe empty return (g b)
maybe empty return (g b) = maybe empty return (g b)  -- OK.

Vì vậy, mapMaybeAMlà iff hợp pháp maybe empty return . g =<< empty = emptycho bất kỳ g. Bây giờ, nếu emptyđược định nghĩa là absurd <$> nil (), như bạn đã làm ở đây, chúng tôi có thể chứng minh điều đó f =<< empty = emptycho bất kỳ f:

f =<< empty = empty
f =<< empty  -- LHS
f =<< absurd <$> nil ()
f . absurd =<< nil ()
-- By parametricity, f . absurd = absurd, for any f.
absurd =<< nil ()
return . absurd =<< nil ()
absurd <$> nil ()
empty  -- LHS = RHS

Theo trực giác, nếu emptythực sự trống rỗng (vì nó phải như vậy, theo định nghĩa chúng ta đang sử dụng ở đây), sẽ không có giá trị nào fđược áp dụng, và do đó f =<< emptykhông thể dẫn đến bất cứ điều gì ngoài empty.

Một cách tiếp cận khác nhau ở đây sẽ là xem xét sự tương tác của các lớp AlternativeMonad. Khi nó xảy ra, có một lớp cho các đơn nguyên thay thế : MonadPlus. Theo đó, một restyled mapMaybecó thể trông như thế này:

-- Lawful iff, for any f, mzero >>= maybe empty mzero . f = mzero
mmapMaybe :: MonadPlus m => (a -> Maybe b) -> m a -> m b
mmapMaybe f m = m >>= maybe mzero return . f

Mặc dù có nhiều ý kiến ​​khác nhau về bộ luật nào là phù hợp nhất MonadPlus, một trong những luật dường như không ai phản đối là ...

mzero >>= f = mzero  -- Left zero

... đó chính xác là tài sản của emptychúng tôi đã thảo luận về một vài đoạn trên. Tính hợp pháp của việc mmapMaybetuân theo ngay lập tức từ luật không bên trái.

(Ngẫu nhiên, Control.Monadcung cấpmfilter :: MonadPlus m => (a -> Bool) -> m a -> m a , phù hợp với những gì filterchúng ta có thể xác định bằng cách sử dụng mmapMaybe.)

Tóm tắt:

Nhưng việc thực hiện này luôn luôn hợp pháp? Có đôi khi hợp pháp (đối với một số định nghĩa chính thức của "đôi khi")?

Có, việc thực hiện là hợp pháp. Kết luận này xoay quanh việc emptythực sự trống rỗng, vì nó nên, hoặc trên đơn nguyên thay thế có liên quan theo MonadPlusluật không bên trái , điều này làm cho mọi thứ trở nên tương tự.

Điều đáng nhấn mạnh Filterablelà không bị thu hẹp bởi MonadPlus, như chúng ta có thể minh họa bằng các ví dụ sau:

  • ZipList: có thể lọc, nhưng không phải là một đơn nguyên. Các Filterableví dụ là giống như một cho danh sách, mặc dù Alternativemột là khác nhau.

  • Map: có thể lọc, nhưng không phải là đơn nguyên hay ứng dụng. Trong thực tế, Mapthậm chí không thể được áp dụng vì không có triển khai hợp lý pure. Nó, tuy nhiên, có riêng của nó empty.

  • MaybeT f: trong khi nó Monadvà các Alternativethể hiện yêu cầu fphải là một đơn nguyên, và một emptyđịnh nghĩa riêng biệt sẽ cần ít nhất Applicative, Filterablecá thể chỉ yêu cầu Functor f(mọi thứ đều có thể lọc được nếu bạn trượt một Maybelớp vào nó).

Phần ba: trống

Tại thời điểm này, người ta vẫn có thể tự hỏi vai trò của nó lớn như thế nào empty, hoặc nil, thực sự đóng vai trò gì Filterable. Nó không phải là một phương thức lớp, và hầu hết các trường hợp dường như có một phiên bản hợp lý của nó nằm xung quanh.

Một điều chúng ta có thể chắc chắn là, nếu loại có thể lọc có bất kỳ cư dân nào, thì ít nhất một trong số chúng sẽ là một cấu trúc trống, bởi vì chúng ta luôn có thể lấy bất kỳ cư dân nào và lọc mọi thứ ra:

chop :: Filterable f => f a -> f Void
chop = mapMaybe (const Nothing)

Sự tồn tại của chop, mặc dù không có nghĩa là sẽ có một giá trị trống duy nhất nil hoặc chopsẽ luôn đưa ra kết quả tương tự. Xem xét, ví dụ, MaybeT IO, mà Filterableví dụ có thể được dùng như một cách để kiểm duyệt kết quả IOtính toán. Trường hợp này là hoàn toàn hợp pháp, mặc dù chopcó thể tạo ra MaybeT IO Voidcác giá trị riêng biệt mang IOhiệu ứng tùy ý .

Ở một lưu ý cuối cùng, bạn đã ám chỉ đến khả năng làm việc với các functor đơn hình mạnh, do đó AlternativeFilterableđược liên kết bằng cách tạo union/ partitionnil/ trivialđẳng cấu. Có unionpartitionnhư các nghịch đảo lẫn nhau là có thể hiểu được nhưng khá hạn chế, cho rằng union . partitionloại bỏ một số thông tin về sự sắp xếp của các yếu tố cho một phần lớn các trường hợp. Đối với sự đồng hình khác, trivial . nillà tầm thường, nhưng nil . trivialthú vị ở chỗ nó ngụ ý chỉ có một f Voidgiá trị duy nhất , một cái gì đó giữ cho một phần lớn các Filterabletrường hợp. Nó xảy ra rằng có một MonadPlusphiên bản của điều kiện này. Nếu chúng ta yêu cầu điều đó, cho bất kỳ u...

absurd <$> chop u = mzero

... và sau đó thay thế mmapMaybetừ phần hai, chúng tôi nhận được:

absurd <$> chop u = mzero
absurd <$> mmapMaybe (const Nothing) u = mzero
mmapMaybe (fmap absurd . const Nothing) u = mzero
mmapMaybe (const Nothing) u = mzero
u >>= maybe mzero return . const Nothing = mzero
u >>= const mzero = mzero
u >> mzero = mzero

Khách sạn này được gọi là luật không quyền của MonadPlus, mặc dù có những lý do chính đáng để tranh chấp vị thế của nó như là một luật của hạng cụ thể đó.


Cảm ơn đã dành thời gian để viết này lên! Tôi chưa có thời gian để trải qua tất cả những điều này, nhưng có công bằng không khi nói rằng tóm tắt là: "không phải không có luật bổ sung liên quan đến các trường hợp MonadAlternativetrường hợp"?
Asad Saeeduddin

@AsadSaeeduddin Yup. MonadPlus(có thể được nhìn thấy thông qua đặc tính hóa gần như bán kết nối) thiết lập một kết nối giữa AlternativeMonadđặc điểm "functor đơn hình" chưa được tạo từ Hask-with- (,)đến Hask-with- Either"không cung cấp. Trong mọi trường hợp, tôi vẫn tự hỏi liệu có điều gì sâu sắc hơn về cách thức empty, trông xa lạ với Filterableđịnh nghĩa trần trụi và vẫn cảm thấy khá phù hợp, hiển thị ở đây.
song lập

emptylà một phần của định nghĩa Alternative. Các AlternativeFilterablecó thể liên quan chặt chẽ hơn bằng cách đòi hỏi unionlà nghịch đảo của partition(và ngược lại), và đó triviallà nghịch đảo của nil(và ngược lại). Đây là những gì được gọi là "functor mạnh đơn hình".
Asad Saeeduddin

@AsadSaeeduddin Đối với một số Filterabletrường hợp phổ biến , tài sản đó sẽ quá mạnh, partitioncó thể bị mất. Ví dụ, (union . partition) [L 7, R 2, L 1, R 6][L 7, L 1, R 2, R 6]. Phần trivial/ nilcuối cùng sẽ sôi sục khi chỉ có một f Voidgiá trị, có vẻ dễ sửa đổi hơn. Về MonadPlusmặt, nó tương ứng với luật không quyền được tranh cãi m >> mzero = mzero, ví dụ, giữ cho các danh sách nhưng không dành cho các trình phân tích cú pháp.
song mã

1
@AsadSaeeduddin Tôi đã thêm một phần chắt lọc suy nghĩ của tôi empty. Đây có thể là bản cập nhật cuối cùng :)
duplode
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.