[英]Flip functor instance Haskell
我需要為Flip
數據類型編寫 Functor 實例:
data K a b = K a
newtype Flip f a b = Flip (f b a) deriving (Eq, Show)
instance Functor (Flip K a) where
fmap=undefined
class給我的解決方案是:
instance Functor (Flip K a) where
fmap f (Flip (K b)) = Flip (K (f b))
我真的不明白這里發生了什么,我開始懷疑我對數據類型和仿函數的整體理解。 我的理解是這樣的(如果有任何錯誤請糾正我):
K
是一種將 2 個參數轉換為結構K a
(僅保留第一個參數)的數據類型Flip
是一種數據類型,它將 3 arguments 轉換為一個結構fmap:: (a-> b) -> fa -> fb
中, fa
有種類*
,要編寫Flip
的 Functor 實例,我們將其寫在Flip
的最后一個類型上。 aka f
和a
在某種程度上是“常量”,我們為類型b
編寫仿函數。 我會寫這樣的東西:instance Functor (Flip f a) where
fmap f (Flip x y z) = fmap Flip x y (f z)
我知道那是完全錯誤的,但我不確定為什么。
另外,為什么我們要將K
帶入 Flip 的 Functor 實例中? 有人可以徹底解釋提出這個解決方案的過程以及為什么它是正確的嗎?
K
是一種將 2 個參數轉換為結構K a
(僅保留第一個參數)的數據類型
這不太對。 K ab
是一種使用兩個參數形成的數據類型,但說它“將它們變成”任何東西並不正確。 相反,它只是向世界聲明現在存在一種新類型: K ab
。 “所以呢?” 你可能會問。 那么,數據類型的后半部分定義了如何生成這種類型的新值。 那部分說,“你可以用這個 function 創建一個類型為K ab
的新值,我將調用K
類型a -> K ab
。” 認識到類型K
和構造函數K
之間的區別非常重要。
因此,並不是K
“只保留第一個參數”——而是構造函數K
(它是一個函數)恰好沒有采用任何類型為b
的 arguments。
Flip
是一種數據類型,它將 3 arguments 轉換為一個結構
正如上面所說,這不太對。 Flip
聲明聲明可以有Flip fab
類型的值,唯一的方法是使用具有fba -> Flip fab
類型的構造函數Flip
。
如果您想知道我是如何得出構造函數K
和Flip
的類型簽名的,它實際上並不神秘,您可以通過在 GHCi 中鍵入:t K
或:t Flip
來仔細檢查。 這些類型完全根據數據類型聲明的右側分配。 另外,請注意類型名稱和構造函數不必相同。 例如,考慮這種數據類型:
data Foo a = Bar Int a | Foo String | Baz a a
這聲明了一個具有三個構造函數的類型Foo a
:
Bar :: Int -> a -> Foo a
Foo :: String -> Foo a
Baz :: a -> a -> Foo a
基本上,構造函數名稱后面的每個類型都是 arguments,按順序排列到構造函數。
- 因為在
fmap:: (a-> b) -> fa -> fb
中,fa
有種類*
,要編寫Flip
的 Functor 實例,我們將其寫在Flip
的最后一個類型上。 akaf
和a
在某種程度上是“常量”,我們為類型b
編寫仿函數。
這基本上是對的! 你也可以說f
有種類* -> *
。 因為Flip
有種類(* -> *) -> * -> * -> *
,你需要給它提供兩個類型 arguments (第一個種類* -> *
和第二個種類*
)讓它正確種類。 前兩個 arguments 在實例中變為固定值(在某種程度上是“常量”)。
我會這樣寫:......我知道那是完全錯誤的,但我不確定為什么。
您的實例完全錯誤的原因是您將類型與構造函數混淆了。 將(Flip xyz)
放在模式 position 中是沒有意義的,因為構造函數Flip
只接受一個參數——記住,它的類型是Flip:: fba -> Flip fab
:所以你想寫點東西像:
instance Functor (Flip f a) where
fmap f (Flip fxa) = ...
現在,您要為...
填寫什么? 你有一個值fxa:: fxa
,你有一個 function f:: x -> y
,你需要生成一個fya
類型的值。 老實說,我不知道該怎么做。 畢竟,typ fxa
的值是多少? 我們不知道f
是什么?!
另外,為什么我們要將
K
帶入Flip
的 Functor 實例中? 有人可以徹底解釋提出這個解決方案的過程以及為什么它是正確的嗎?
我們在上面看到我們不能為任意的f
編寫Functor
實例,但我們可以做的是為特定的f
編寫它。 事實證明, K
就是這樣一個有效的特定f
。 讓我們嘗試讓它工作:
instance Functor (Flip K a) where
fmap f (Flip kxa) = ...
當f
是任意的時,我們被困在這里,但現在我們知道kxa:: K xa
。 請記住,生成類型K xa
的值的唯一方法是使用構造函數K
。 因此,這個值kxa
必須是使用該構造函數創建的,因此我們可以將其拆分為: kxa ⩳ K x'
where x':: x
。 讓我們先輸入 go 並將其放入我們的模式中:
fmap f (Flip (K x')) = ...
現在我們可以取得進展了! 我們需要生成一個Flip K ay
類型的值。 唔。 生成Flip
類型值的唯一方法是使用Flip
構造函數,所以讓我們從這里開始:
fmap f (Flip (K x')) = Flip ...
Flip K ay
類型的Flip
構造函數采用K ya
類型的值。 產生其中之一的唯一方法是使用K
構造函數,所以讓我們添加:
fmap f (Flip (K x')) = Flip (K ...)
K ya
類型的K
構造函數采用類型y
的值,因此我們需要在此處提供類型y
的值。 我們有一個值x':: x
和一個 function f:: x -> y
。 將第一個插入第二個可以得到我們需要的值:
fmap f (Flip (K x')) = Flip (K (f x'))
只需將x'
重命名為b
,您就擁有了老師提供的代碼。
DDub在他們的回答中寫道:
你有一個值
fxa:: fxa
,你有一個 functionf:: x -> y
,你需要生成一個fya
類型的值。 老實說,我不知道該怎么做。 畢竟,什么是fxa
類型的值? 我們不知道f
是什么?!
我同意,但我想補充一點。 你的老師關於如何處理這個問題的想法很酷(當你試圖寫下一些反例時,像K
這樣的東西會派上用場,比如這里),但是,我認為我們可以使這段代碼更廣泛。 我使用Data.Bifunctor
。
那么,什么是Bifunctor
? 它們正如它們的名字所說: * -> * -> *
類型(我們有時也稱其為雙函子,但它們不是一回事),它允許映射到它的兩個 arguments(來自源代碼的片段):
class Bifunctor p where
-- | Map over both arguments at the same time.
--
-- @'bimap' f g ≡ 'first' f '.' 'second' g@
bimap :: (a -> b) -> (c -> d) -> p a c -> p b d
bimap f g = first f . second g
{-# INLINE bimap #-}
-- | Map covariantly over the first argument.
--
-- @'first' f ≡ 'bimap' f 'id'@
first :: (a -> b) -> p a c -> p b c
first f = bimap f id
{-# INLINE first #-}
-- | Map covariantly over the second argument.
--
-- @'second' ≡ 'bimap' 'id'@
second :: (b -> c) -> p a b -> p a c
second = bimap id
{-# INLINE second #-}
所以,這就是我 go 對此的看法:
instance Bifunctor f => Functor (Flip f a) where
fmap x2y (Flip fxa) = Flip (first x2y fxa)
說到你老師的代碼,這是一個非常好的想法,但由於K
是一個Bifunctor
,所以范圍更窄:
instance Bifunctor K where
bimap f _g (K a) = K (f a)
合法的:
bimap id id (K a) = K (id a) = id (K a)
正如上面鏈接中所說,只寫下bimap
,這是我們唯一需要擔心的法則。
我們只需要使用理智和有用的命名,突然間一切都變得簡單明了(而不是折磨和扭曲):
data K b a = MkK b -- the type (K b a) "is" just (b)
newtype Flip f a b = MkFlip (f b a) -- the type (Flip f a b) "is" (f b a)
deriving (Eq, Show)
instance Functor (Flip K a) where
-- fmap :: (b -> c) -> Flip K a b -> Flip K a c
fmap g (MkFlip (MkK b)) = MkFlip (MkK (g b))
-- MkK b :: K b a
-- MkFlip (_ :: K b a) :: Flip K a b
現在看着這個,我們的腦海里甚至沒有一個問題出現,沒有一個我們無法立即解決的疑問。
在教學時對類型和數據構造函數使用相同的名稱,以及對“f”函數和“f”函數使用f
,這純粹是對學生的濫用。
只有當您厭倦了所有的Mk
並且覺得它們對您沒有任何幫助時,您才可以像專家通常做的那樣安全、輕松地扔掉它們。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.