簡體   English   中英

難道是 (Alternative f, Foldable f) => Monad f?

[英]Could it be that (Alternative f, Foldable f) => Monad f?

以下類型檢查:

instance (Applicative f, Alternative f, Foldable f) => Monad f where 
  (>>=) = flip $ \f -> foldr (<|>) empty . fmap f
  -- Or equivalently
  a >>= b = getAlt . foldMap Alt . fmap b $ a

這實際上是一個有效的Monad實例嗎? 如果是,為什么不使用它? 如果不是,它是否違反了任何法律或其他法律? 我沒有證明這些定律成立,但我也找不到反例。

這應該是正確身份 monad 法的反例。

下面,我們利用來自GHC.Generics的函子產品Maybe :*: Maybe ,但如果願意,它可以被內聯。 這也是一個適用的、替代的、可折疊的和 monad。 我相信這些情況下的圖書館是守法的。

然后,我們將提議的instance Monad (問題中的instance Monad )與標准庫instance Monad進行比較。 我們發現建議的實例不滿足正確的恆等律,而它似乎在庫實例中成立(至少在我非常有限的測試中)。

{-# LANGUAGE FlexibleInstances, GeneralizedNewtypeDeriving, TypeOperators #-}
{-# OPTIONS -Wall #-}

module NotAMonad where

import Control.Applicative
import GHC.Generics ((:*:)(..))

-- A basic wrapper to avoid overlapping instances, and to be able to
-- define a custom monad instance.
newtype Wrap m a = Wrap { unWrap :: m a }
    deriving (Functor, Applicative, Alternative, Foldable, Show)

-- The proposed instance
instance (Applicative f, Alternative f, Foldable f) => Monad (Wrap f) where 
  (>>=) = flip $ \f -> foldr (<|>) empty . fmap f

-- This is Applicative, Alternative, and Foldable
type T = Maybe :*: Maybe

-- A basic test
test :: Wrap T Int
test = Wrap (Just 3 :*: Just 4) >>= return
-- result:
-- Wrap {unWrap = Just 3 :*: Just 3}

4現在被3取代。 不過,我沒有試圖解釋原因。 我猜它是由Just 3 <|> Just 4 = Just 3

相反,使用庫 monad 實例,一切看起來都很好:

> (Just 3 :*: Just 4) >>= return
Just 3 :*: Just 4

Alternative有點像駭人聽聞的野獸。 它本質上是幺半群構造函數的類:類型構造函數T使得對於任何包含的類型XTX是一個幺半群。 這與函子……單子並沒有太大關系,而且在數學上的深度要低得多。 (因此,僅出於數學上的優雅,將Monad設置在Alternative之下會有點糟糕。)

為了清楚起見,讓我們根據Monoid編寫該實例(這實際上不會編譯):

instance (Foldable f, (∀ x . Monoid (f x))) => Monad f where
  (>>=) = flip $ \f -> foldr mappend empty . fmap f
        ≡ flip $ \f -> fold . fmap f
        ≡ flip foldMap

或者確實

  (=<<) = foldMap

所以,這絕對不是什么未知數。

要檢查定律,我們最好看看 Kleisli 公式:

  (f <=< g) x = f =<< g x
              ≡ foldMap f $ g x

  f <=< g = foldMap f . g

那么單子定律是

  • 左身份

    f <=< pure ≡ foldMap f . pure =! f
  • 正確的身份

    pure <=< f ≡ foldMap pure . f =! f
  • 關聯性

    (f <=< g) <=< h ≡ foldMap (foldMap f . g) . h =! foldMap f . foldMap g . h ≡ foldMap f . (foldMap g . h) ≡ f <=< (g <=< h)

所以簡而言之,我們需要

  • foldMap f . pure =! f =! foldMap pure . f foldMap f . pure =! f =! foldMap pure . ff
  • foldMap (foldMap f . g) =! foldMap f . foldMap g foldMap (foldMap f . g) =! foldMap f . foldMap gf , g

這當然看起來並非不合理,但我不認為您可以從哪里對任意Foldable + Alternative實例嚴格得出結論。

真的,我在這個實例中看到的一個大問題是它不夠通用。 大多數 monad 既不是Foldable也不是Alternative 如果像您提議的那樣有一個涵蓋所有的定義,則需要OverlappingInstances來定義您自己的任何實例,而這些通常被認為是您不應該在沒有充分理由的情況下使用的東西。

但是,我確實想知道 bind 方法的以下默認定義是否有任何問題:

{-# LANGUAGE DefaultSignatures #-}
class Applicative f => Monad f where
  return :: a -> m a
  return = pure
  (>>=) :: m a -> (a -> m b) -> m b
  default (>>=) :: (Foldable m, Monoid m b)
          => m a -> (a -> m b) -> m b
  (>>=) = flip foldMap

這至少允許將例如列表實例簡單地定義為

instance Monad []

完全不需要寫出方法,因為foldMap ≡ concatMap ≡ (=<<)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM