[英]Is the composition of an arbitrary monad with a traversable always a monad?
[英]Writing Monad and Traversable instances for NonEmpty
我對實例定義有疑問。 我不能使定義符合單子和遍歷實例中的身份定律。 這里的類型:
data NotEmpty a = LastValue a | MidValue a (NotEmpty a) deriving Show
instance Functor NotEmpty where
fmap f (LastValue a) = LastValue (f a)
fmap f (MidValue a b) = MidValue (f a) (fmap f b)
instance Applicative NotEmpty where
pure = LastValue
(LastValue f) <*> LastValue a = LastValue (f a)
(LastValue f) <*> MidValue a b = pure (f a)
(MidValue f x) <*> MidValue a b = MidValue (f a) (x <> b)
(MidValue f x) <*> LastValue a = LastValue (f a)
instance Monad NotEmpty where
(LastValue a) >>= f = f a
(MidValue a b) >>= f = f a
instance Foldable NotEmpty where
foldMap f (LastValue a) = f a
foldMap f (MidValue a b) = (f a) <> (foldMap f b)
instance Traversable NotEmpty where
traverse f (LastValue a) = fmap LastValue (f a)
traverse f (MidValue a b) = traverse f b
我試過了:
(MidValue a b) >>= f = MidValue a (b >>= f)
得到錯誤:
* Couldn't match type b' with a'
b' is a rigid type variable bound by
the type signature for:
(>>=) :: forall a b. NotEmpty a -> (a -> NotEmpty b) -> NotEmpty b
at LabWorks4.hs:87:19-21
a' is a rigid type variable bound by
the type signature for:
(>>=) :: forall a b. NotEmpty a -> (a -> NotEmpty b) -> NotEmpty b
at LabWorks4.hs:87:19-21
Expected type: NotEmpty a
Actual type: NotEmpty b
* In the second argument of MidValue', namely (b >>= f)'
In the expression: MidValue a (b >>= f)
In an equation for `>>=':
(MidValue a b) >>= f = MidValue a (b >>= f)
* Relevant bindings include
f :: a -> NotEmpty b (bound at LabWorks4.hs:88:24)
b :: NotEmpty a (bound at LabWorks4.hs:88:17)
a :: a (bound at LabWorks4.hs:88:15)
(>>=) :: NotEmpty a -> (a -> NotEmpty b) -> NotEmpty b
(bound at LabWorks4.hs:87:19)
|
88 | (MidValue a b) >>= f = MidValue a (b >>= f)
| ^^^^^^^
在遍歷中我嘗試過:
traverse f (MidValue a b) = fmap (MidValue a) (traverse f b)
我得到錯誤:
* Couldn't match type b' with a'
b' is a rigid type variable bound by
the type signature for:
traverse :: forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> NotEmpty a -> f (NotEmpty b)
at LabWorks4.hs:134:5-12
a' is a rigid type variable bound by
the type signature for:
traverse :: forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> NotEmpty a -> f (NotEmpty b)
at LabWorks4.hs:134:5-12
Expected type: f (NotEmpty a)
Actual type: f (NotEmpty b)
* In the second argument of fmap', namely (traverse f b)'
In the expression: fmap (MidValue a) (traverse f b)
In an equation for `traverse':
traverse f (MidValue a b) = fmap (MidValue a) (traverse f b)
* Relevant bindings include
b :: NotEmpty a (bound at LabWorks4.hs:135:28)
a :: a (bound at LabWorks4.hs:135:26)
f :: a -> f b (bound at LabWorks4.hs:135:14)
traverse :: (a -> f b) -> NotEmpty a -> f (NotEmpty b)
(bound at LabWorks4.hs:134:5)
|
135 | traverse f (MidValue a b) = fmap (MidValue a) (traverse f b)
您對 monad 定義的嘗試是不正確的。
(MidValue a b) >>= f = MidValue a (b >>= f)
假設您的MidValue ab
的類型為NotEmpty Int
。 這意味着a
是Int
類型,而b
是NotEmpty Int
類型。 現在假設f
是Int -> NotEmpty String
類型的 function 。 根據您的定義, b >>= f
將是NonEmpty String
類型。 這意味着在MidValue a (b >>= f)
中, a
類型為Int
,而(b >>= f)
的類型為NonEmpty String
。 因此不可能以這種方式構造一個有效的類型。
相反,您應該定義一個串聯 function。 這可以通過為NotEmpty
定義Semigroup
的實例來完成,如下所示:
instance Semigroup (NotEmpty a) where
LastValue a <> b = MidValue a b
MidValue a as <> b = MidValue a (as <> b)
接下來,您可以在Monad
實例中使用(<>)
:
(MidValue a b) >>= f = f a <> (b >>= f)
您在此實例上的嘗試以與 monad 實例類似的方式失敗。
traverse f (MidValue a b) = fmap (MidValue a) (traverse f b)
在這里, MidValue a
的類型為NotEmpty a -> NotEmpty a
。 traverse fb
的類型為Applicative f => f (NotEmpty b)
。 這兩種類型不可能與fmap
的類型統一: Functor f => (a -> b) -> fa -> fb
。
因此,對於traverse f (MidValue ab)
,您已經發現需要遞歸調用traverse f
for b
。 您可以為a
做一些非常相似的事情,就像您在LastValue
的情況下所做的那樣:
let a' = fmap pure (f a)
b' = traverse f b
您現在有兩個具有相同類型的值: Applicative f => f (NotEmpty b)
。 最后一步是再次連接這兩個值。 最簡單的方法是使用liftA2 ,為此您需要將import Control.Applicative
添加到文件頂部。 liftA2:: Applicative f => (a -> b -> c) -> fa -> fb -> f c
可以通過提供 function a -> b -> c
來組合兩個值,這兩個值都包裝在某個 Applicative 類型中a -> b -> c
。 在這種情況下,您只需將之前定義的串聯 function 提供給liftA2
。 該案的最終結果如下:
traverse f (MidValue a b) = let a' = fmap pure (f a)
b' = traverse f b
in liftA2 (<>) a' b'
您的代碼僅將f
應用於列表的尾部。 正確的實現必須將f
應用於頭部和尾部。
instance Monad NotEmpty where
LastValue a >>= f = f a
MidValue a b >>= f = f a <> (b >>= f) -- assuming that NonEmpty is a Semigroup
instance Traversable NotEmpty where
traverse f (LastValue a) = LastValue <$> f a
traverse f (MidValue a b) = MidValue <$> f a <*> traverse f b
請注意, Functor
、 Foldable
和Traversable
可以自動派生,因此您不需要自己編寫它們:
{-# LANGUAGE DeriveTraversable #-}
data NotEmpty a = LastValue a | MidValue a (NotEmpty a)
deriving (Show, Functor, Foldable, Traversable)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.