简体   繁体   English

翻转函子实例 Haskell

[英]Flip functor instance Haskell

I need to write the Functor instances for the Flip datatype:我需要为Flip数据类型编写 Functor 实例:

data K a b = K a
newtype Flip f a b = Flip (f b a) deriving (Eq, Show)

instance Functor (Flip K a) where
fmap=undefined

The solution I was given in class is: class给我的解决方案是:

instance Functor (Flip K a) where
    fmap f (Flip (K b)) = Flip (K (f b))

I really don't understand what's going on here and I'm beginning to doubt my whole understanding of data types and functors.我真的不明白这里发生了什么,我开始怀疑我对数据类型和仿函数的整体理解。 What I do understand is this (please correct me if any of this is wrong):我的理解是这样的(如果有任何错误请纠正我):

  • K is a data type that turns 2 parameters into a structure K a ( that only keeps the first parameter) K是一种将 2 个参数转换为结构K a (仅保留第一个参数)的数据类型
  • Flip is a datatype that turns 3 arguments into a structure with one Flip是一种数据类型,它将 3 arguments 转换为一个结构
  • Because in fmap:: (a-> b) -> fa -> fb , fa has kind * , to write the Functor instance of Flip , we write it on the last type in Flip .因为在fmap:: (a-> b) -> fa -> fb中, fa有种类* ,要编写Flip的 Functor 实例,我们将其写在Flip的最后一个类型上。 Aka f and a are "constants" in a way, and we write the functor for the type b . aka fa在某种程度上是“常量”,我们为类型b编写仿函数。 I would write something like:我会写这样的东西:
instance Functor (Flip f a) where
   fmap f (Flip x y z) = fmap Flip x y (f z)

I know that that is completely wrong but I'm not sure why.我知道那是完全错误的,但我不确定为什么。

Also, why would we bring K into the Functor instance of Flip?另外,为什么我们要将K带入 Flip 的 Functor 实例中? Can someone explain thoroughly the process of coming up with this solution and why it is correct?有人可以彻底解释提出这个解决方案的过程以及为什么它是正确的吗?

  • K is a data type that turns 2 parameters into a structure K a ( that only keeps the first parameter) K是一种将 2 个参数转换为结构K a (仅保留第一个参数)的数据类型

This isn't quite right.这不太对。 K ab is a data type formed using two parameters, but it's not really right to say that it "turns them into" anything. K ab是一种使用两个参数形成的数据类型,但说它“将它们变成”任何东西并不正确。 Instead, it's simply just stating to the world that there now exists a new type: K ab .相反,它只是向世界声明现在存在一种新类型: K ab "So what?" “所以呢?” you might ask.你可能会问。 Well, the second half of the data type defines how to make new values of this type.那么,数据类型的后半部分定义了如何生成这种类型的新值。 That part says, "You can make a new value of type K ab with this function I'll call K which has type a -> K ab ."那部分说,“你可以用这个 function 创建一个类型为K ab的新值,我将调用K类型a -> K ab 。” It's really important to recognize that there is a distinction between the type K and the constructor K .认识到类型K构造函数K之间的区别非常重要。

So, it's not that K "only keeps the first parameter"—it's that the constructor K (which is a function) happens to not take any arguments of type b .因此,并不是K “只保留第一个参数”——而是构造函数K (它是一个函数)恰好没有采用任何类型为b的 arguments。


  • Flip is a datatype that turns 3 arguments into a structure with one Flip是一种数据类型,它将 3 arguments 转换为一个结构

Just as above, this is not quite right.正如上面所说,这不太对。 The Flip declaration states that there can be values of type Flip fab , and the only way to make them is by using the constructor Flip that has type fba -> Flip fab . Flip声明声明可以有Flip fab类型的值,唯一的方法是使用具有fba -> Flip fab类型的构造函数Flip

In case you're wondering how I'm coming up with the type signatures for the constructors K and Flip , it's not actually mysterious, and you can double check by typing :t K or :t Flip into GHCi.如果您想知道我是如何得出构造函数KFlip的类型签名的,它实际上并不神秘,您可以通过在 GHCi 中键入:t K:t Flip来仔细检查。 These types are assigned based entirely on the right hand side of the data type declaration.这些类型完全根据数据类型声明的右侧分配。 Also, note that the type name and constructor don't have to be the same .另外,请注意类型名称和构造函数不必相同 For instance, consider this data type:例如,考虑这种数据类型:

data Foo a = Bar Int a | Foo String | Baz a a

This declares a type Foo a with three constructors:这声明了一个具有三个构造函数的类型Foo a

Bar :: Int -> a -> Foo a
Foo :: String -> Foo a
Baz :: a -> a -> Foo a

Basically, each of the types after the constructor name are the arguments, in order, to the constructor.基本上,构造函数名称后面的每个类型都是 arguments,按顺序排列到构造函数。


  • Because in fmap:: (a-> b) -> fa -> fb , fa has kind * , to write the Functor instance of Flip , we write it on the last type in Flip .因为在fmap:: (a-> b) -> fa -> fb中, fa有种类* ,要编写Flip的 Functor 实例,我们将其写在Flip的最后一个类型上。 Aka f and a are "constants" in a way, and we write the functor for the type b . aka fa在某种程度上是“常量”,我们为类型b编写仿函数。

This is basically right!这基本上是对的! You could also say that f has kind * -> * .你也可以说f有种类* -> * Since Flip has kind (* -> *) -> * -> * -> * , you need to provide it two type arguments (the first of kind * -> * and the second of kind * ) to get it to the right kind.因为Flip有种类(* -> *) -> * -> * -> * ,你需要给它提供两个类型 arguments (第一个种类* -> *和第二个种类* )让它正确种类。 Those first two arguments become fixed ("constants" in a way) in the instance.前两个 arguments 在实例中变为固定值(在某种程度上是“常量”)。


I would write something like: ... I know that that is completely wrong but I'm not sure why.我会这样写:......我知道那是完全错误的,但我不确定为什么。

The reason your instance is completely wrong is that you've mixed up the type with the constructor .您的实例完全错误的原因是您将类型构造函数混淆了。 It doesn't make sense to put (Flip xyz) in the pattern position where you did because the constructor Flip only takes one argument—remember, it's type is Flip:: fba -> Flip fab : So you'd want to write something like:(Flip xyz)放在模式 position 中是没有意义的,因为构造函数Flip只接受一个参数——记住,它的类型是Flip:: fba -> Flip fab :所以你想写点东西像:

instance Functor (Flip f a) where
   fmap f (Flip fxa) = ...

Now, what do you fill in for the ... ?现在,您要为...填写什么? You have a value fxa:: fxa , and you have a function f:: x -> y , and you need to produce a value of type fya .你有一个值fxa:: fxa ,你有一个 function f:: x -> y ,你需要生成一个fya类型的值。 Honestly, I don't know how to do that.老实说,我不知道该怎么做。 After all, what is a value of typ fxa ?毕竟,typ fxa值是多少? We don't know what f is?!我们不知道f是什么?!


Also, why would we bring K into the Functor instance of Flip ?另外,为什么我们要将K带入Flip的 Functor 实例中? Can someone explain thoroughly the process of coming up with this solution and why it is correct?有人可以彻底解释提出这个解决方案的过程以及为什么它是正确的吗?

We saw just above that we can't write the Functor instance for an arbitrary f , but what we can do is write it for a particular f .我们在上面看到我们不能为任意的f编写Functor实例,但我们可以做的是为特定f编写它。 It turns out that K is just such a particular f that works.事实证明, K就是这样一个有效的特定f Let's try to make it work:让我们尝试让它工作:

instance Functor (Flip K a) where
  fmap f (Flip kxa) = ...

When f was arbitrary, we got stuck here, but now we know that kxa:: K xa .f是任意的时,我们被困在这里,但现在我们知道kxa:: K xa Remember that the only way to make a value of type K xa is using the constructor K .请记住,生成类型K xa的值的唯一方法是使用构造函数K Therefore, this value kxa must have been made using that constructor, so we can break it apart as in: kxa ⩳ K x' where x':: x .因此,这个值kxa必须是使用该构造函数创建的,因此我们可以将其拆分为: kxa ⩳ K x' where x':: x Let's go ahead and put that into our pattern:让我们先输入 go 并将其放入我们的模式中:

  fmap f (Flip (K x')) = ...

Now we can make progress!现在我们可以取得进展了! We need to produce a value of type Flip K ay .我们需要生成一个Flip K ay类型的值。 Hmm.唔。 The only way to produce a value of type Flip is using the Flip constructor, so let's start with that:生成Flip类型值的唯一方法是使用Flip构造函数,所以让我们从这里开始:

  fmap f (Flip (K x')) = Flip ...

The Flip constructor at type Flip K ay takes a value of type K ya . Flip K ay类型的Flip构造函数采用K ya类型的值。 The only way to produce one of those is with the K constructor, so let's add that:产生其中之一的唯一方法是使用K构造函数,所以让我们添加:

  fmap f (Flip (K x')) = Flip (K ...)

The K constructor at type K ya takes a value of type y , so we need to provide a value of type y here. K ya类型的K构造函数采用类型y的值,因此我们需要在此处提供类型y的值。 We have a value x':: x and a function f:: x -> y .我们有一个值x':: x和一个 function f:: x -> y Plugging the first into the second gives us the value we need:将第一个插入第二个可以得到我们需要的值:

  fmap f (Flip (K x')) = Flip (K (f x'))

Just rename x' to b , and you have exactly the code your teacher provided.只需将x'重命名为b ,您就拥有了老师提供的代码。

DDub wrote in their answer: DDub在他们的回答中写道:

You have a value fxa:: fxa , and you have a function f:: x -> y , and you need to produce a value of type fya .你有一个值fxa:: fxa ,你有一个 function f:: x -> y ,你需要生成一个fya类型的值。 Honestly, I don't know how to do that.老实说,我不知道该怎么做。 After all, what is a value of type fxa ?毕竟,什么是fxa类型的值? We don't know what f is?!我们不知道f是什么?!

And I agree, but I woulld like to add a bit.我同意,但我想补充一点。 Your teacher's idea as to how to deal with this is pretty cool (things like this K come in quite handy when you are trying to write down some counterexample, like here ), and yet, I reckon we can make this code way broader.你的老师关于如何处理这个问题的想法很酷(当你试图写下一些反例时,像K这样的东西会派上用场,比如这里),但是,我认为我们可以使这段代码更广泛。 I use Data.Bifunctor .我使用Data.Bifunctor

So, what are Bifunctor s ?那么,什么是Bifunctor They are just what their name says: a * -> * -> * type (which we call bifunctors as well sometimes, yet they are not the same thing) which allows mapping over its both arguments (snippet from the source ):它们正如它们的名字所说: * -> * -> *类型(我们有时也称其为双函子,但它们不是一回事),它允许映射到它的两个 arguments(来自源代码的片段):

class Bifunctor p where
  -- | Map over both arguments at the same time.
  --
  -- @'bimap' f g ≡ 'first' f '.' 'second' g@
  bimap :: (a -> b) -> (c -> d) -> p a c -> p b d
  bimap f g = first f . second g
  {-# INLINE bimap #-}

  -- | Map covariantly over the first argument.
  --
  -- @'first' f ≡ 'bimap' f 'id'@
  first :: (a -> b) -> p a c -> p b c
  first f = bimap f id
  {-# INLINE first #-}

  -- | Map covariantly over the second argument.
  --
  -- @'second' ≡ 'bimap' 'id'@
  second :: (b -> c) -> p a b -> p a c
  second = bimap id
  {-# INLINE second #-}

So, here is how I would go about that:所以,这就是我 go 对此的看法:

instance Bifunctor f => Functor (Flip f a) where
    fmap x2y (Flip fxa) = Flip (first x2y fxa)

Speaking of your teacher's code, it's a very nice idea, yet a more narrow one as K is a Bifunctor :说到你老师的代码,这是一个非常好的想法,但由于K是一个Bifunctor ,所以范围更窄:

instance Bifunctor K where
    bimap f _g (K a) = K (f a)

A lawful one:合法的:

bimap id id (K a) = K (id a) = id (K a)

As it says in the link above , having bimap only written down, that's the only law we need to worry about.正如上面链接中所说,只写下bimap ,这是我们唯一需要担心的法则。

We just need to use sane and helpful naming, and suddenly it all becomes simple and clear (as opposed to torturous and contorted):我们只需要使用理智有用的命名,突然间一切都变得简单明了(而不是折磨和扭曲):

data K b a = MkK b                     -- the type (K b a)      "is" just (b)
newtype Flip f a b = MkFlip (f b a)    -- the type (Flip f a b) "is" (f b a)
                     deriving (Eq, Show)

instance Functor (Flip K a) where
    -- fmap :: (b -> c) -> Flip K a b -> Flip K a c
    fmap g (MkFlip (MkK b)) = MkFlip (MkK (g b))
        --        MkK b :: K b a
        --  MkFlip (_ :: K b a) :: Flip K a b

There's not even one question arising in our minds now looking at this, not one doubt we aren't able to immediately resolve.现在看着这个,我们的脑海里甚至没有一个问题出现,没有一个我们无法立即解决的疑问。

Using same names for types and for data constructors while teaching , as well as using f both for "f"unction and "f"unctor, is pure abuse of the students.在教学时对类型和数据构造函数使用相同的名称,以及对“f”函数和“f”函数使用f ,这纯粹是对学生的滥用

Only when you've become fed up with all the Mk s and don't feel they are helpful to you in any way, you can safely and easily throw them away, as experts usually do.只有当您厌倦了所有的Mk并且觉得它们对您没有任何帮助时,您才可以像专家通常做的那样安全、轻松地扔掉它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM