簡體   English   中英

為什么我不能在Haskell中使用id創建Functor實例?

[英]Why can't I make Either instance of Functor using id in Haskell?

在制作我的自定義EitherFunctor ,只是為了理解更清晰的類型和類型類,我發現了以下情況:

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 _)類型allEither 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案例卻略有不同。 函數 fmapb是多態的,但其參數的值是“在多態的范圍內”; 在你有你的論證的時候,類型bfmap的調用者選擇為某種特定類型,所以它是“某種未知的類型,可以通過任何東西”,而不是“我想選擇的任何類型”。 有沒有辦法以某種方式把類型的值, 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.

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