Haskell 了解其中一个是函子的实例
在我的空闲时间我正在学习哈斯克尔,所以这是一个初学者的问题 在我的阅读资料中,我遇到了一个例子,说明了Haskell 了解其中一个是函子的实例,haskell,typeclass,functor,either,Haskell,Typeclass,Functor,Either,在我的空闲时间我正在学习哈斯克尔,所以这是一个初学者的问题 在我的阅读资料中,我遇到了一个例子,说明了或如何成为函子的实例: instance Functor (Either a) where fmap f (Right x) = Right (f x) fmap f (Left x) = Left x 现在,我试图理解为什么在右值构造函数的情况下实现映射,而在左的情况下不映射 以下是我的理解: 首先让我将上面的实例重写为 instance Functor (Either a)
或如何成为函子的实例:
instance Functor (Either a) where
fmap f (Right x) = Right (f x)
fmap f (Left x) = Left x
现在,我试图理解为什么在右
值构造函数的情况下实现映射,而在左
的情况下不映射
以下是我的理解:
首先让我将上面的实例重写为
instance Functor (Either a) where
fmap g (Right x) = Right (g x)
fmap g (Left x) = Left x
现在:
我知道fmap::(c->d)->fc->fd
如果我们用a
替换f
,我们得到fmap::(c->d)->a c->a d
Right(gx)
的类型是a(gx)
,而gx
的类型是d
,因此我们有Right(gx)
的类型是或者是d
,这是我们从fmap
中所期望的(见上文2)
现在,如果我们看一下左(gx)
,我们可以用同样的推理说它的类型是要么(gx)b
,也就是要么db
,这不是我们从fmap
中所期望的(见上文2)d
应该是第二个参数,而不是第一个!因此,我们无法映射到左侧
我的推理正确吗?这是正确的。这种行为还有另一个相当重要的原因:您可以将a b
视为一个计算,它可能成功并返回b
,或者失败并显示错误消息a
。(这也是monad实例的工作方式)。因此,函子实例不会触及Left
值是很自然的,因为您希望映射到计算,如果计算失败,就没有什么可操作的了。您的帐户当然是正确的。也许我们在处理这样的实例时遇到困难的原因是我们实际上一次定义了无限多个函子实例——每个可能的左类型一个。但是函子实例是一种对系统中无限多个类型进行操作的系统方法。因此,我们定义了无限多的系统操作方式,在系统中无限多的类型上进行操作。这个例子涉及两个方面的普遍性
不过,如果你分阶段来做,也许就没那么奇怪了。这些类型中的第一种是使用单位类型()
及其唯一合法值()
的长篇版本:
在这里,我们可能会感到疲劳,并进行抽象:
data Unless a b = Mere a | Genuine b
现在我们创建函子实例,没有问题,第一个看起来很像的实例,可能是:
instance Functor MightBe where
fmap f (Nope ()) = Nope () -- compare with Nothing
fmap f (Yep x) = Yep (f x) -- compare with Just (f x)
instance Functor UnlessError where
fmap f (Bad str) = Bad str -- a more informative Nothing
fmap f (Good x) = Good (f x)
instance Functor ElseInt where
fmap f (Else n) = Else n
fmap f (Value b) = Value (f b)
但是,再一次,为什么要麻烦呢,让我们做一个抽象:
instance Functor (Unless a) where
fmap f (Mere a) = Mere a
fmap f (Genuine x) = Genuine (f x)
由于未触及()
、字符串
和Int
值,因此不会触及仅仅是a
术语
现在,我想知道为什么
在应用程序中的实现映射
正确的值构造函数,但不正确
如果是左撇子
在这里插上电源,可能会有意义
假设a=String(错误消息)
您可以将a应用于Float
所以你有一个f:Float->Integer,比如舍入
(任意字符串)(浮点)=任意字符串浮点
现在(FMAPF)::任意字符串浮点->任意字符串整数
那么你打算用f做什么?f不知道如何处理字符串,所以在那里你不能做任何事情。也就是说,显然您唯一可以操作的是右值,而左值保持不变
换句话说,a是函子,因为有这样一个明显的fmap:
- 对于正确的值,应用f
- 对于左值,什么都不做
正如其他人所提到的,类型在其两个参数中都是函子。但在Haskell中,我们只能(直接)在类型的最后一个参数中定义函子。在这种情况下,我们可以使用newtype
s来绕过限制:
newtype FlipEither b a = FlipEither { unFlipEither :: Either a b }
因此,我们有一个构造函数flipother::other a b->flipother b a
,它用交换的类型参数将other
包装到newtype
中。我们有dectructorunflipother::flipother b a->或a b
,可以将其展开。现在我们可以在FlipOrther
的最后一个参数中定义一个函子实例,它实际上是Orther
的第一个参数:
instance Functor (FlipEither b) where
fmap f (FlipEither (Left x)) = FlipEither (Left (f x))
fmap f (FlipEither (Right x)) = FlipEither (Right x)
请注意,如果我们忘记了FlipOrther
一段时间,我们只会得到Orther
的Functor
的定义,只交换了Left
/Right
。现在,每当我们在任一
的第一个类型参数中需要一个函子
实例时,我们可以将该值包装到FlipOrther
中,然后将其展开。例如:
fmapE2 :: (a -> b) -> Either a c -> Either b c
fmapE2 f = unFlipEither . fmap f . FlipEither
更新:看一下,其中和(,)
是的实例。每个都有两个参数,每个参数中都有一个函子。这反映在Bifunctor
的方法first
和second
中
的Bifunctor
的定义非常对称:
instance Bifunctor Either where
bimap f _ (Left a) = Left (f a)
bimap _ g (Right b) = Right (g b)
first f = bimap f id
second f = bimap id f
我还没有了解单子:)我读到,或者a b
按照您描述的方式使用,a
表示错误信息:但这只是一个约定,对吗?@MarcoS:正确,这只是一个约定。任何一种都可以解释为析取:a b类型的值包含a类型的值或b类型的值,但不能同时包含两者;左右类型之间没有区别。但是作为一个函子,在这些例子中,你可以用fmap覆盖as(任意一个字符串),这是完全不对称的。进入具体函子insta的类型
instance Bifunctor Either where
bimap f _ (Left a) = Left (f a)
bimap _ g (Right b) = Right (g b)
first f = bimap f id
second f = bimap id f