为什么我不能在Haskell中使用id创建Functor实例?

Fth*_*der 6 haskell functional-programming currying typeclass

在制作我的自定义时Either,Functor为了理解更清晰的类型和类型类,我发现了以下情况:

Functor

module Functor (Functor, fmap) where

import Prelude hiding(Functor, fmap)

class Functor f where
  fmap :: (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

上面显示的代码编译得很好但是,如果我将其更改为使用id,则无法编译:

instance Functor (Either a) where
  fmap f (Right x) = Right (f x)
  fmap _ = id
Run Code Online (Sandbox Code Playgroud)

为什么??我错过了什么?以下代码也不起作用:

instance Functor (Either a) where
  fmap f (Right x) = Right (f x)
  fmap f all@(Left x) = all
Run Code Online (Sandbox Code Playgroud)

...这在我看来非常奇怪,因为下面的代码编译:

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
Run Code Online (Sandbox Code Playgroud)

先感谢您

Cac*_*tus 7

让我们看一下专门用于仿Either函数的fmap类型:

fmap :: (a -> b) -> Either e a -> Either e b
Run Code Online (Sandbox Code Playgroud)

正如我们由此可以看出,在fmap f all@(Left _),类型allEither e a.这Either e b与签名规定的预期结果类型不匹配fmap,因此fmap f all@(Left _) = all不是很好的类型.

同样适用于使用的情况id.

  • 不,你错了.如果`x :: e`,那么`左x :: E ec'可以选择`c`.如果你设置`c~b`,你会得到`Left x :: Either eb`. (2认同)
  • @FtheBuilder`id`的类型为`a - > a`,返回类型与**完全匹配**参数的类型.因此,它将"要么ab"映射到另一个"要么ab".但是`fmap`的返回类型确实**不完全匹配第二个参数,即使它的一个参数没有改变.因此使用简单的`= id`不会工作.同样使用`all`也不会出于同样的原因:参数的类型错误.Haskell不能简单地概括参数的类型以允许你想要的东西. (2认同)

Ben*_*Ben 7

你要做的是归结为:

f :: Either a Bool -> Either a ()
f (Right _) = Right ()
f left = left
Run Code Online (Sandbox Code Playgroud)

有错误:

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.
Run Code Online (Sandbox Code Playgroud)

left绑定到函数参数.所以类型检查器知道它的类型Either a Bool.然后它被用作返回值.我们从类型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
Run Code Online (Sandbox Code Playgroud)

我们提供l了绑定和类型,然后尝试将其用作不同类型.那是无效的(通过它id也不会改变它的类型).即使对于类型的值Left ()也是有效的源代码文本Either () Bool,这并不意味着可以使用已知可以使用Either () ()源文本定义的类型的特定值Left (),就像它是类型一样Either () Bool.

如果您有多态值,则可以执行以下操作:

? let l = Left ()
l :: Either () b

? l :: Either () ()
Left ()
it :: Either () ()

? l :: Either () Bool
Left ()
it :: Either () Bool
Run Code Online (Sandbox Code Playgroud)

请注意,l这里的原始值是多态的b; 它可以被用来作为Either () b用于任何

但是你的fmap情况略有不同.该函数 fmap是多态的b,但其参数的值是"在多态的范围内"; 在你有你的论证的时候,fmap的调用者b已经选择了某种特定的类型,所以它是"某种未知的类型,可以通过任何东西",而不是"我想选择的任何类型".无法以某种方式将类型的值转换为类型的值,因此您必须提取值,然后创建包含它的值.Either a bEither a caEither a c

  • @FtheBuilder主要是因为我们使用类型来强制执行不变量,因此我们不喜欢在我们的类型背后完成任务.一个可比较的,如果更简单的情况是在Haskell中没有隐式的数字转换.在任何情况下,你的隐式转换都是有限的用途,因为它只能在你对`Left`模式匹配时起作用.没有全部功能`无论是ab - > ac'(这里的"总数"是"在所有情况下均适用"的技术术语). (3认同)