[英]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
使得對於任何包含的類型X
, TX
是一個幺半群。 這與函子……單子並沒有太大關系,而且在數學上的深度要低得多。 (因此,僅出於數學上的優雅,將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 . f
∀ f
foldMap (foldMap f . g) =! foldMap f . foldMap g
foldMap (foldMap f . g) =! foldMap f . foldMap g
∀ f
, 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.