定义你自己的 Functor
Defining your own Functor
我一直在努力理解 Haskell 中的 Functor 是什么,为此我想要一个没有任何其他属性的 Functor 示例。
我想出的工作示例是
data MyFunctor a b = MyFunctor a b deriving Show
instance Functor (MyFunctor a) where
fmap g (MyFunctor a b) = MyFunctor a $ g (b)
我猜这是一个半实用的 Functor,因为可以在对右值进行操作时安全地存储左值。然后我想在操作之前将左值替换为右值。这样...
Main> fmap (+2) MyFunctor 2 5
MyFunctor 5 7
然而,更改实例声明来执行此操作会产生错误。
data MyFunctor a b = MyFunctor a b deriving Show
instance Functor (MyFunctor a) where
fmap g (MyFunctor a b) = MyFunctor b $ g (b)
我不明白,或者知道要搜索什么来找到足够相似的问题来帮助我。
C:\Haskell\func.hs:3:28: error:
* Couldn't match type `a1' with `a'
`a1' is a rigid type variable bound by
the type signature for:
fmap :: forall a1 b. (a1 -> b) -> MyFunctor a a1 -> MyFunctor a b
at C:\Haskell\func.hs:3:3
`a' is a rigid type variable bound by
the instance declaration at C:\Haskell\func.hs:2:10
Expected type: MyFunctor a b
Actual type: MyFunctor a1 b
* In the expression: MyFunctor b $ g (b)
In an equation for `fmap':
fmap g (MyFunctor a b) = MyFunctor b $ g (b)
In the instance declaration for `Functor (MyFunctor a)'
* Relevant bindings include
b :: a1 (bound at C:\Haskell\func.hs:3:23)
a :: a (bound at C:\Haskell\func.hs:3:21)
g :: a1 -> b (bound at C:\Haskell\func.hs:3:8)
fmap :: (a1 -> b) -> MyFunctor a a1 -> MyFunctor a b
(bound at C:\Haskell\func.hs:3:3)
错误消息非常冗长,并且讲述了整个故事。
首先,它告诉我们 fmap
的类型,在那种情况下应该是
fmap :: forall a1 b. (a1 -> b) -> MyFunctor a a1 -> MyFunctor a b
因此,如果 f
是任何函数 a1 -> b
,我们必须使 fmap f
具有类型 MyFunctor a a1 -> MyFunctor a b
。
注意 return 类型是 MyFunctor a b
。事实上,这正是 GHC 所期望的:
Expected type: MyFunctor a b
但它发现了其他东西:
Actual type: MyFunctor a1 b
怎么可能?好吧,fmap
returns MyFunctor b (g b)
和 g b
的类型是 b
(正确,正如预期的那样),但是 b
(值)的类型是a1
,而不是预期的 a
。
更实际的是,fmap
只能改变MyFunctor a b
中的b
组件,不能影响a
组件。这可以从fmap
.
的类型签名看出
我认为您不能那样交换值。您已声明 MyFunctor
以包含泛型类型 a
和 b
的两个值。 这些不一定是同一类型。例如,您可以创建这样的值:
MyFunctor "foo" 42
当您声明 Functor (MyFunctor a)
的实例时,您基本上是说对于任何泛型类型 a
,您可以将 MyFunctor a b
映射到 MyFunctor a c
。
例如,如果a
是String
,如上例,你可以将MyFunctor String b
映射到MyFunctor String c
,如果你有一个函数b -> c
.
您不能交换参数,因为第一个值必须保留其类型。
如果 c
是除 String
之外的任何其他类型,则在上面的示例中不能这样做。
I then want to have the left value be replace with the right value before the operation.
Functor 这样做是非法的:GHC 正确地告诉您类型不正确。特别是,MyFunctor
值的左侧部分可能与右侧部分的类型不同,如 MyFunctor 5 "hello"
。并且由于 fmap
必须仅对您的类型的最后一个类型参数进行操作,因此它必须单独保留第一部分。让我们看一个更具体的例子。
回想一下 fmap
的类型是
fmap :: Functor f => (a -> b) -> f a -> f b
假设您有一个想要 fmap
的对象:
obj :: MyFunctor Int String
那么,f
的类型必须是什么才能调用 fmap f obj
?为了统一涉及的类型,我们必须有
f :: (String -> a)
和
fmap f obj :: MyFunctor Int a
所以你可以看到,不可能 "replace" 第一个 Int 字段与第二个 String 字段的先前值:那个地方唯一允许的是之前的值,一个诠释!
现在,您可能会想象,如果您更改 MyFunctor 定义,使两个字段具有相同的类型,就可以使类型生效:
data MyFunctor a = MyFunctor a a
-- Also illegal
instance Functor MyFunctor where
fmap f (MyFunctor a b) = MyFunctor b (f b)
但是你也不能这样做,因为 b
和 f b
可能是不同的类型,调用者可以选择 f
使用什么,所以你的实现fmap
的人可能不认为它们是相同的。
事实上,对于任何给定的数据类型,最多有一个 Functor 的合法定义:如果你找到一个合法的,你可以确定任何其他定义都是非法的:要么类型不匹配向上,否则它将违反两个 Functor 定律之一:
fmap id == id
fmap f . fmap g == fmap (f . g)
您如何才能在仍然保持类型一致的情况下违反其中一条法律?通过对不属于正在 fmap
ed 的结构的一部分的结构进行操作。例如,经常有人像这样写一个(糟糕的!)仿函数:
data Counter a = Counter Int a
instance Functor Counter where
fmap f (Counter n x) = Counter (n + 1) (f x)
但这违反了两个 Functor 定律,因为它允许您计算 fmap
被调用了多少次,这应该是一个没有公开的细节。
fmap id (Counter 0 0) == Counter 1 0
(fmap tail . fmap tail) /= fmap (tail . tail)
首先,你试图理解这一点真是太好了。它是 Haskell.
中所有奇妙事物的基础之一
其次,我认为有比尝试自己创建实例更好的方法来理解 Functor
。最好的方法之一是首先在尽可能多的现有 Functor
实例上使用 use fmap
。 Prelude中已经有一堆了。查看它们的 源代码 以了解它们是如何实例化的。这会让您对它们是什么有一个直觉。
你上面"created"的类型实际上已经在前奏中了,例如。它被称为(,)
。 (错误地说这是 Either
之前 - 它不是,因为那个类型是 data Either a b = Left a | Right b
抱歉。)
你要做的是先感受一下 Prelude 中的大约十个 Functor
。但不要只对 Functor
部分有感觉。确保您了解底层数据类型和结构。出于这个原因,除非您真正了解类型、代数数据类型(求和和乘积)、多参数类型和类型类(不是如何使用或实例化类型类,而是它们是什么),否则这将是一个徒劳的练习,以及该机制的工作原理)。
如果你想要一个很好的直观介绍来使用这些东西的基础知识,我会说通读我帮助完成这件事的书:http://happylearnhaskelltutorial.com)
使用 Functor
实例和创建它们之间有很大区别。我认为如果你使用它们中的一堆,你会对它们的含义和是什么有更好的直觉。只有这样,恕我直言,您才应该自己实例化一个。在那一点上,查阅法律也是个好主意,因为它们很重要。这应该会消除您对它们是什么或可能是什么的任何误解。
不过,为了让您走上正轨,它们是关于在某些结构的最浅层中应用功能。该结构 是 所讨论类型的结构。所以,在 List
中,它将是所有的 "values within the list"(因为需要更好的方式来描述列表),因为如果你查看 List
的定义类型,你你会看到这实际上是列表的最浅层。但是,要了解原因,您需要真正了解 Haskell.
中列表的类型
因此,我将检查以下类型如何作为 Functor
工作:Maybe
、List
(又名 []
)、Either
、 Data.Map
、(->)
、(,)
。 Data.Tree
(玫瑰树),Control.Monad.Identity
中的 Identity
类型(这几乎是您可以拥有的最简单的参数化代数数据类型,它包装了它的值:data Identity a = Identity a
)。搜索更多(有堆!)
祝您玩得愉快!
我一直在努力理解 Haskell 中的 Functor 是什么,为此我想要一个没有任何其他属性的 Functor 示例。
我想出的工作示例是
data MyFunctor a b = MyFunctor a b deriving Show
instance Functor (MyFunctor a) where
fmap g (MyFunctor a b) = MyFunctor a $ g (b)
我猜这是一个半实用的 Functor,因为可以在对右值进行操作时安全地存储左值。然后我想在操作之前将左值替换为右值。这样...
Main> fmap (+2) MyFunctor 2 5
MyFunctor 5 7
然而,更改实例声明来执行此操作会产生错误。
data MyFunctor a b = MyFunctor a b deriving Show
instance Functor (MyFunctor a) where
fmap g (MyFunctor a b) = MyFunctor b $ g (b)
我不明白,或者知道要搜索什么来找到足够相似的问题来帮助我。
C:\Haskell\func.hs:3:28: error:
* Couldn't match type `a1' with `a'
`a1' is a rigid type variable bound by
the type signature for:
fmap :: forall a1 b. (a1 -> b) -> MyFunctor a a1 -> MyFunctor a b
at C:\Haskell\func.hs:3:3
`a' is a rigid type variable bound by
the instance declaration at C:\Haskell\func.hs:2:10
Expected type: MyFunctor a b
Actual type: MyFunctor a1 b
* In the expression: MyFunctor b $ g (b)
In an equation for `fmap':
fmap g (MyFunctor a b) = MyFunctor b $ g (b)
In the instance declaration for `Functor (MyFunctor a)'
* Relevant bindings include
b :: a1 (bound at C:\Haskell\func.hs:3:23)
a :: a (bound at C:\Haskell\func.hs:3:21)
g :: a1 -> b (bound at C:\Haskell\func.hs:3:8)
fmap :: (a1 -> b) -> MyFunctor a a1 -> MyFunctor a b
(bound at C:\Haskell\func.hs:3:3)
错误消息非常冗长,并且讲述了整个故事。
首先,它告诉我们 fmap
的类型,在那种情况下应该是
fmap :: forall a1 b. (a1 -> b) -> MyFunctor a a1 -> MyFunctor a b
因此,如果 f
是任何函数 a1 -> b
,我们必须使 fmap f
具有类型 MyFunctor a a1 -> MyFunctor a b
。
注意 return 类型是 MyFunctor a b
。事实上,这正是 GHC 所期望的:
Expected type: MyFunctor a b
但它发现了其他东西:
Actual type: MyFunctor a1 b
怎么可能?好吧,fmap
returns MyFunctor b (g b)
和 g b
的类型是 b
(正确,正如预期的那样),但是 b
(值)的类型是a1
,而不是预期的 a
。
更实际的是,fmap
只能改变MyFunctor a b
中的b
组件,不能影响a
组件。这可以从fmap
.
我认为您不能那样交换值。您已声明 MyFunctor
以包含泛型类型 a
和 b
的两个值。 这些不一定是同一类型。例如,您可以创建这样的值:
MyFunctor "foo" 42
当您声明 Functor (MyFunctor a)
的实例时,您基本上是说对于任何泛型类型 a
,您可以将 MyFunctor a b
映射到 MyFunctor a c
。
例如,如果a
是String
,如上例,你可以将MyFunctor String b
映射到MyFunctor String c
,如果你有一个函数b -> c
.
您不能交换参数,因为第一个值必须保留其类型。
如果 c
是除 String
之外的任何其他类型,则在上面的示例中不能这样做。
I then want to have the left value be replace with the right value before the operation.
Functor 这样做是非法的:GHC 正确地告诉您类型不正确。特别是,MyFunctor
值的左侧部分可能与右侧部分的类型不同,如 MyFunctor 5 "hello"
。并且由于 fmap
必须仅对您的类型的最后一个类型参数进行操作,因此它必须单独保留第一部分。让我们看一个更具体的例子。
回想一下 fmap
的类型是
fmap :: Functor f => (a -> b) -> f a -> f b
假设您有一个想要 fmap
的对象:
obj :: MyFunctor Int String
那么,f
的类型必须是什么才能调用 fmap f obj
?为了统一涉及的类型,我们必须有
f :: (String -> a)
和
fmap f obj :: MyFunctor Int a
所以你可以看到,不可能 "replace" 第一个 Int 字段与第二个 String 字段的先前值:那个地方唯一允许的是之前的值,一个诠释!
现在,您可能会想象,如果您更改 MyFunctor 定义,使两个字段具有相同的类型,就可以使类型生效:
data MyFunctor a = MyFunctor a a
-- Also illegal
instance Functor MyFunctor where
fmap f (MyFunctor a b) = MyFunctor b (f b)
但是你也不能这样做,因为 b
和 f b
可能是不同的类型,调用者可以选择 f
使用什么,所以你的实现fmap
的人可能不认为它们是相同的。
事实上,对于任何给定的数据类型,最多有一个 Functor 的合法定义:如果你找到一个合法的,你可以确定任何其他定义都是非法的:要么类型不匹配向上,否则它将违反两个 Functor 定律之一:
fmap id == id
fmap f . fmap g == fmap (f . g)
您如何才能在仍然保持类型一致的情况下违反其中一条法律?通过对不属于正在 fmap
ed 的结构的一部分的结构进行操作。例如,经常有人像这样写一个(糟糕的!)仿函数:
data Counter a = Counter Int a
instance Functor Counter where
fmap f (Counter n x) = Counter (n + 1) (f x)
但这违反了两个 Functor 定律,因为它允许您计算 fmap
被调用了多少次,这应该是一个没有公开的细节。
fmap id (Counter 0 0) == Counter 1 0
(fmap tail . fmap tail) /= fmap (tail . tail)
首先,你试图理解这一点真是太好了。它是 Haskell.
中所有奇妙事物的基础之一其次,我认为有比尝试自己创建实例更好的方法来理解 Functor
。最好的方法之一是首先在尽可能多的现有 Functor
实例上使用 use fmap
。 Prelude中已经有一堆了。查看它们的 源代码 以了解它们是如何实例化的。这会让您对它们是什么有一个直觉。
你上面"created"的类型实际上已经在前奏中了,例如。它被称为(,)
。 (错误地说这是 Either
之前 - 它不是,因为那个类型是 data Either a b = Left a | Right b
抱歉。)
你要做的是先感受一下 Prelude 中的大约十个 Functor
。但不要只对 Functor
部分有感觉。确保您了解底层数据类型和结构。出于这个原因,除非您真正了解类型、代数数据类型(求和和乘积)、多参数类型和类型类(不是如何使用或实例化类型类,而是它们是什么),否则这将是一个徒劳的练习,以及该机制的工作原理)。
如果你想要一个很好的直观介绍来使用这些东西的基础知识,我会说通读我帮助完成这件事的书:http://happylearnhaskelltutorial.com)
使用 Functor
实例和创建它们之间有很大区别。我认为如果你使用它们中的一堆,你会对它们的含义和是什么有更好的直觉。只有这样,恕我直言,您才应该自己实例化一个。在那一点上,查阅法律也是个好主意,因为它们很重要。这应该会消除您对它们是什么或可能是什么的任何误解。
不过,为了让您走上正轨,它们是关于在某些结构的最浅层中应用功能。该结构 是 所讨论类型的结构。所以,在 List
中,它将是所有的 "values within the list"(因为需要更好的方式来描述列表),因为如果你查看 List
的定义类型,你你会看到这实际上是列表的最浅层。但是,要了解原因,您需要真正了解 Haskell.
因此,我将检查以下类型如何作为 Functor
工作:Maybe
、List
(又名 []
)、Either
、 Data.Map
、(->)
、(,)
。 Data.Tree
(玫瑰树),Control.Monad.Identity
中的 Identity
类型(这几乎是您可以拥有的最简单的参数化代数数据类型,它包装了它的值:data Identity a = Identity a
)。搜索更多(有堆!)
祝您玩得愉快!