将数据类型设为Functor实例以映射到参数类型的字段上 [英] Making a datatype an instance of Functor to map on a field which is of parametric type
问题描述
在 ,它是参数化的类型),例如Just 3
,Right "hello"
,"hello"
,[1..10]
等;因此,在第二字段上的映射和在第二字段 的内容上的映射似乎是不同的.
我真的很困惑,但是我想最后一段足以说明我的努力.
Functor
类型类太笼统,无法在第二个字段的类型t k
上应用映射,但是可以 在第二个字段类型内的具体类型k
内应用地图.因此,使用您的问题中的术语,我们不能使用Functor
来映射类型为t k
的第二个字段,但是可以使用它来映射类型为k
的第二个字段中的内容t k
(提供的t
是一种可以映射其内容的结构).
关于尝试使用Functor
映射类型t k
的问题是,它允许进行的转换会违反Barry
类型的定义.以下功能:
censor :: (Functor f) => f a -> f ()
censor = (() <$)
应该应用于任何仿函数实例,将目标类型a
的字段替换为单元()
.例如:
> censor (Just 5)
Just ()
> censor [1..5]
[(),(),(),(),()]
如果Barry
在某种程度上是其第二个字段的类型t k
的仿函数,那么我将能够获得有效的Barry
值:
> let myBarry = BarryV 10 "hello" :: Barry [] Char Int
并对其应用censor
来检查其第二个字段:
> censor myBarry
BarryV 10 ()
但是此值的类型是什么?对于某些t
和k
这样的t k = ()
显然是Barry t k Int
,但这是不可能的.无法将类型()
拆分"为两个部分t
和k
.因此,BarryV 10 ()
不是有效的Barry
类型的值,它的存在意味着我们在程序中构造了无效的Barry
类型.
另一方面,我们可以在k
参数中为Barry
创建一个Functor
实例.我们无法直接执行此操作,因为Haskell语法仅允许我们为面向其最后一个"参数的类型表达式定义Functor
实例.因此,通过为Barry t k
定义Functor
实例,可以在最后一个参数p
中将Barry t k p
设置为Functor
,但不能在中间参数k
中将其设置为Functor
. /p>
如果我们的变体的参数顺序不同:
data Larry p t k = LarryV p (t k) deriving (Show)
然后我们可以定义Functor
实例:
instance Functor (Larry p t) where
fmap f (LarryV p tk) = LarryV p (fmap f tk)
这会产生类型错误,表示t
没有Functor
实例,但是如果我们仅在拥有Functor t
时才限制定义该实例,那么它会很好地工作:
instance Functor t => Functor (Larry p t) where
fmap f (LarryV p tk) = LarryV p (fmap f tk)
现在,只要t
是Functor
,我们就有Larry p t
一个Functor
.例如:
> let myLarry = LarryV 10 "hello"
> :t myLarry
myLarry :: Num p => Larry p [] Char
> import Data.Char
> fmap toUpper myLarry
LarryV 10 "HELLO"
这是有效的,因为t = []
是Functor
,所以我们得到了所需的实例.
请注意,在实际代码中,不是引入新的类型Larry
,在"middle"参数中定义Functor
实例的标准方法是使用newtype
包装器,例如:
newtype Barry' p t k = Barry' (Barry t k p)
instance Functor t => Functor (Barry' p t) where
fmap f (Barry' (BarryV p tk)) = Barry' (BarryV p (fmap f tk))
Follow up on this question about Learn You a Haskell for Great Good.
The author, at the end of Chapter 8 declares this datatype (slighly simplified, I hope it's fine)
data Barry t k p = BarryV p (t k) deriving (Show)
and then makes it an instance of Functor
instance Functor (Barry a b) where
fmap f (BarryV x y) = BarryV (f x) y
then concluding
There we go! We just mapped the
f
over the first field.
Yes. The first. So my question is: what if I want to map over, say, the second field?
Actually the second field cannot be of a type as simple as Int
, Char
, Float
, and so on; it has to be of a type which can be obtained as a type constructor applied to a concreate type (the italicised text is the same as "parametric type", right? no, it is parametrized type), such as Just 3
, Right "hello"
, "hello"
, [1..10]
, and so on; therefore mapping on the second field and mapping on the content of the second field seems different.
I'm really confused, but I guess the last paragraph is enough of an effort that I show.
The Functor
type class is too general to apply a map over the type t k
of the second field, but it could apply a map over the concrete type k
within the type of the second field. So, using the terminology from your question, we can't use Functor
to map over the second field of type t k
, but we can use it to map over the content of type k
within the second field of type t k
(provided t
is the sort of structure that allows mapping over its contents).
With respect to trying to use Functor
to map over the type t k
, the problem is that it allows transformations that would violate the definition of the Barry
type. The following function:
censor :: (Functor f) => f a -> f ()
censor = (() <$)
should apply to any functor instance, replacing fields of the targetted type a
with unit ()
. For example:
> censor (Just 5)
Just ()
> censor [1..5]
[(),(),(),(),()]
If Barry
was somehow a functor in the type t k
of its second field, then I would be able to take a valid Barry
value:
> let myBarry = BarryV 10 "hello" :: Barry [] Char Int
and apply censor
to it to censor its second field:
> censor myBarry
BarryV 10 ()
But what is the type of this value? It's clearly Barry t k Int
for some t
and k
such that t k = ()
, but that's impossible. There's no way to "split" the type ()
into two parts t
and k
. So, BarryV 10 ()
isn't a value of a valid Barry
type, and it's existence would mean we'd constructed an invalid Barry
type in our program.
On the other hand, we could create a Functor
instance for Barry
in the k
parameter. We can't do this directly, because Haskell syntax only allows us to define Functor
instances for a type expression that target its "last" parameter. So Barry t k p
can be made a Functor
in the last parameter p
by defining a Functor
instance for Barry t k
, but it can't be made a Functor
in the middle parameter k
.
If we had a variant with the parameters in a different order:
data Larry p t k = LarryV p (t k) deriving (Show)
then we could define the Functor
instance:
instance Functor (Larry p t) where
fmap f (LarryV p tk) = LarryV p (fmap f tk)
This gives a type error, saying that there's no Functor
instance for t
, but if we restrict ourselves to defining this instance only when we have Functor t
, it works fine:
instance Functor t => Functor (Larry p t) where
fmap f (LarryV p tk) = LarryV p (fmap f tk)
Now, as long as t
is a Functor
, we have Larry p t
a Functor
. For example:
> let myLarry = LarryV 10 "hello"
> :t myLarry
myLarry :: Num p => Larry p [] Char
> import Data.Char
> fmap toUpper myLarry
LarryV 10 "HELLO"
This works because t = []
is a Functor
, so we get the instance we need.
Note that in practical code, instead of introducing a new type Larry
, the standard way of defining a Functor
instance in a "middle" parameter is to use a newtype
wrapper, something like:
newtype Barry' p t k = Barry' (Barry t k p)
instance Functor t => Functor (Barry' p t) where
fmap f (Barry' (BarryV p tk)) = Barry' (BarryV p (fmap f tk))
这篇关于将数据类型设为Functor实例以映射到参数类型的字段上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!