显示`newtype T a = T(a - > Int)`是一个不是Functor的Type构造函数

时间:2017-05-22 02:24:58

标签: haskell category-theory

有人声称newtype T a = T (a -> Int)是一个不是仿函数的类型构造函数(但它是一个逆变函子)。怎么会这样?或者什么是逆变函子(我认为这很明显为什么它不能成为普通函子)?

3 个答案:

答案 0 :(得分:6)

假设T是一个仿函数。然后我们有

fmap :: (a -> b) -> T a -> T b

现在,让我们尝试实施它。

instance Functor T where
  fmap f (T g) = T $ \x -> _y

显然,我们从这样的事情开始,并以某种方式合并值fgx以计算y的{​​{1}}值。正确的类型。我们怎么做?好吧,请注意我称之为_y,这告诉GHC我需要一些帮助来确定放在那里的内容。 GHC有什么要说的?

<interactive>:7:28: error:
    • Found hole: _y :: Int
      Or perhaps ‘_y’ is mis-spelled, or not in scope
    • In the expression: _y
      In the second argument of ‘($)’, namely ‘\ x -> _y’
      In the expression: T $ \ x -> _y
    • Relevant bindings include
        x :: b (bound at <interactive>:7:23)
        g :: a -> Int (bound at <interactive>:7:13)
        f :: a -> b (bound at <interactive>:7:8)
        fmap :: (a -> b) -> T a -> T b (bound at <interactive>:7:3)

所以现在我们要清楚所有相关的类型,对吗?我们需要以某种方式返回Int,我们必须构建它是:

        x :: b
        g :: a -> Int
        f :: a -> b

嗯,好吧,我们唯一能够创建Int的{​​{1}}就是g,所以让我们把它填入,留下g&#39; s参数空白,要求GHC提供更多帮助:

instance Functor T where
  fmap f (T g) = T $ \x -> g _y

<interactive>:7:31: error:
    • Found hole: _y :: a
      Where: ‘a’ is a rigid type variable bound by
               the type signature for:
                 fmap :: forall a b. (a -> b) -> T a -> T b
               at <interactive>:7:3
      Or perhaps ‘_y’ is mis-spelled, or not in scope
    • In the first argument of ‘g’, namely ‘(_y)’
      In the expression: g (_y)
      In the second argument of ‘($)’, namely ‘\ x -> g (_y)’
    • Relevant bindings include
        x :: b (bound at <interactive>:7:23)
        g :: a -> Int (bound at <interactive>:7:13)
        f :: a -> b (bound at <interactive>:7:8)
        fmap :: (a -> b) -> T a -> T b (bound at <interactive>:7:3)

好的,我们可以自己预测:要调用g,我们需要从某处获得a类型的值。但是我们在范围内没有a类型的任何值,并且我们也没有任何返回a类型值的函数!我们陷入困境:现在不可能产生我们想要的类型的价值,尽管我们每一步都做了唯一可行的事情:我们没有任何东西可以备份并尝试不同的方式。

为什么会这样?因为如果我给你一个a -> Int类型的函数并且说'#34;但顺便说一句,这里是a -> b的函数,请给我一个来自b -> Int的函数相反&#34;,你实际上使用来自a -> b的功能,因为没有人给你任何a来调用它!如果我从b -> a给你一个函数,那将非常有用,对吧?您可以从b -> Int生成一个函数,然后首先调用b -> a函数获取a,然后调用原始a -> Int函数以获取所需的Int 1}}。

这就是逆变函子的意义所在:我们将传递给fmap的函数中的箭头反转,因此它可以映射你需要的东西&#34; (函数参数)而不是你&#34;有&#34; (具体值,函数的返回值等)。

除此之外:我之前声称我们已经做过&#34;唯一可能的事情&#34;在每一步,这有点像。我们无法在Intfg中构建x,但当然我们可以在空中弥补各种数字。我们对类型b一无所知,因此我们无法以某种方式获得Int来自它的instance Functor T where fmap f (T g) = T $ const 0 ,但我们只能说&# 34;让我们总是返回零&#34;,这在技术上满足了类型检查器:

f

显然这看起来很错误,因为看起来gfmap id = id 应该非常重要,我们忽略了它们!但它是类型检查,所以我们没问题,对吧?

不,这违反了Functor法律之一:

fmap id (T $ const 5) = (T $ const 0) /= (T $ const 5)

我们可以很容易地证明这一点:

Int

现在我们真的已经尝试了一切:我们必须在不使用b类型的情况下构建const的唯一方法就是从无到有,所有此类用途都与使用function setPrimeLimits() { var selectedStart = $('#space_global_start').val(); var selectedEnd = $('#space_global_end').val(); $('#prime_global_start, #prime_global_end').timepicker({ minTime: selectedStart, maxTime: selectedEnd, }); } $('#space_global_start, #space_global_end').on('changeTime', setPrimeLimits); 同构,这将违反Functor法律。

答案 1 :(得分:6)

鉴于

newtype T a = T (a -> Int)

让我们尝试为此数据类型构建Contravariant实例。

这是所讨论的类型类:

class Contravariant f where
  contramap :: (a -> b) -> f b -> f a

基本上,contramap类似于fmap,但不是将函数a -> b提升为f a -> f b,而是将其提升为f b -> f a

让我们开始编写实例......

instance Contravariant T where
  contramap g (T f) = ?

在我们填写?之前,让我们考虑一下gf的类型:

g :: a -> b
f :: b -> Int

为清楚起见,我们不妨提一下

f a ~ T (a -> Int)
f b ~ T (b -> Int)

所以我们可以按如下方式填写?

instance Contravariant T where
  contramap g (T f) = T (f . g)

要变得超级迂腐,您可以将g重命名为aToB,将f重命名为bToInt

instance Contravariant T where
  contramap aToB (T bToInt) = T (bToInt . aToB)

您可以为Contravariant撰写T a个实例的原因可归结为aT (a -> Int)中处于逆变位置的事实。让自己相信T a不是Functor的最好方法是尝试(并且失败)自己编写Functor个实例。

答案 2 :(得分:5)

这是另一个观点。正如liminalisht所示,TContravariant。关于协变和逆变的类型我们能说些什么呢?

import Data.Void


change1, change1', change2 :: (Functor f, Contravariant f) => f a -> f b
change1 = contramap (const ()) . fmap (const ())
change1' = (() >$) . (() <$)
change2 = fmap absurd . contramap absurd

前两个实现基本相同(change1'change1的优化);他们每个人都使用()是&#34;终端对象&#34; Hask change2使用的事实是Void是&#34;初始对象&#34;。

这些功能中的每一项都会a替换f ab的所有a,而不了解bf a或之间的关系他们,其他一切都一样。应该很清楚,这意味着a并不真正依赖f。也就是说,T的参数必须是幻像。对于Sub GreenOrRed() Dim i As Integer For i = 2 To i = 27293 If (Cells(i, "S").Value = 0 And Cells(i, "T").Value = 0 And Cells(i, "U").Value = 0) Then Cells(i, "D").Interior.ColorIndex = 10 Else Cells(i, "D").Interior.ColorIndex = 9 End If Next i End Sub ,情况,因此它也不能协变。