[英]Why can I not make a (Functor f) => ConcreteType -> f String into an instance Functor ((->) ConcreteType)?
[英]Why can't I make Either instance of Functor using id in Haskell?
在制作我的自定義Either
和Functor
,只是為了理解更清晰的類型和類型類,我發現了以下情況:
Functor
module Functor (Functor, fmap) where
import Prelude hiding(Functor, fmap)
class Functor f where
fmap :: (a -> b) -> f a -> f b
Either
module Either(Either(..)) where
import Prelude hiding(Either(..), Functor, fmap)
data Either a b = Left a | Right b deriving(Show)
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap _ (Left x) = Left x
上面顯示的代碼編譯得很好但是,如果我將其更改為使用id
,則不會編譯:
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap _ = id
為什么?? 我錯過了什么? 以下代碼也不起作用:
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap f all@(Left x) = all
...這在我看來非常奇怪,因為下面的代碼編譯:
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
data Point = Point Float Float deriving (Show)
test :: Shape -> String
test (Circle _ x) = show x
test all@(Rectangle _ x) = show all ++ " - "++ show x
先感謝您
讓我們看一下專門用於Either
仿函數的fmap類型:
fmap :: (a -> b) -> Either e a -> Either e b
正如我們由此可以看出,在fmap f all@(Left _)
類型all
是Either ea
。 這與預期的結果類型不匹配。由fmap
的簽名規定的Either eb
,因此fmap f all@(Left _) = all
不是很好的類型。
對於使用id
的情況也是如此。
你要做的是歸結為:
f :: Either a Bool -> Either a ()
f (Right _) = Right ()
f left = left
有錯誤:
foo.hs:3:7:
Couldn't match type ‘Bool’ with ‘()’
Expected type: Either a ()
Actual type: Either a Bool
In the expression: left
In an equation for ‘f’: f left = left
Failed, modules loaded: none.
left
綁定到函數參數。 所以類型檢查器知道它的類型Either a Bool
。 然后它被用作返回值。 我們從類型f :: Either a Bool -> Either a ()
知道f :: Either a Bool -> Either a ()
得知返回值必須是Either a ()
類型。 如果left
是有效的返回值,則其類型必須與f
的返回類型匹配。 所以Either a ()
必須等於Either a Bool
; 它不是,因此類型檢查器拒絕該程序。
反過來,它基本上是這個問題:
λ let l = Left () :: Either () ()
l :: Either () ()
λ l
Left ()
it :: Either () ()
λ l :: Either () Bool
<interactive>:10:1:
Couldn't match type ‘()’ with ‘Bool’
Expected type: Either () Bool
Actual type: Either () ()
In the expression: l :: Either () Bool
In an equation for ‘it’: it = l :: Either () Bool
我們給了l
一個綁定和一個類型,然后嘗試將它作為一個不同的類型。 這是無效的(通過id
提供它也不會改變它的類型)。 盡管Left ()
也是類型為Either () Bool
的值的有效源代碼文本 ,但這並不意味着已知可以使用源文本Left ()
定義的類型為Either () ()
的特定值Left ()
可以像使用類型Either () Bool
。
如果您有多態值,則可以執行以下操作:
λ let l = Left ()
l :: Either () b
λ l :: Either () ()
Left ()
it :: Either () ()
λ l :: Either () Bool
Left ()
it :: Either () Bool
注意,這里的原始l
值在b
是多態的; 它可以用作任何 b的Either () b
b。
但是你的fmap
案例卻略有不同。 函數 fmap
在b
是多態的,但其參數的值是“在多態的范圍內”; 在你有你的論證的時候,類型b
被fmap的調用者選擇為某種特定類型,所以它是“某種未知的類型,可以通過任何東西”,而不是“我想選擇的任何類型”。 有沒有辦法以某種方式把類型的值, Either ab
成類型的值Either ac
,所以你要提取a
值,然后創建一個Either ac
包含它。
在解釋類型錯誤方面,我沒有任何內容可以添加到前兩個答案中,但是我想提一下, Left x :: Either ta
在內存中的表示方式與Left x :: Either tb
。 這意味着即使類型系統不允許您使用Either ta
代替類型為Either tb
的值,由於已經通過其他答案完全清楚地解釋的原因,您可以通過類型檢查器“強制”它使用unsafeCoerce
:
import Unsafe.Coerce (unsafeCoerce)
instance Functor (Either t) where
fmap f (Right a) = Right (f a)
fmap f l = unsafeCoerce l
即使unsafeCoerce
通常被認為是要避免的東西,如果你知道自己在做什么,並且你有充分的理由這樣做,比如性能, unsafeCoerce
可以讓編譯器知道你確定運行時值會有用匹配預期的結構。
在這種情況下,沒有unsafeCoerce
,並且沒有考慮任何潛在的GHC優化, fmap f (Left x) = Left x
將始終構造一個新的但物理上相同的Left
值,而unsafeCoerce
flavor只返回原始Left
值而沒有額外的內存分配。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.