将数据类型设为Functor实例以映射到参数类型的字段上 [英] Making a datatype an instance of Functor to map on a field which is of parametric type

查看:73
本文介绍了将数据类型设为Functor实例以映射到参数类型的字段上的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

,它是参数化的类型),例如Just 3Right "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 ()

但是此值的类型是什么?对于某些tk这样的t k = ()显然是Barry t k Int,但这是不可能的.无法将类型()拆分"为两个部分tk.因此,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)

现在,只要tFunctor,我们就有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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆