为什么长度为包含2个元素的元组返回1,并为具有更多元素的元组提供错误?

时间:2016-04-06 19:38:17

标签: haskell

我正在使用#34; Haskell Programming from First Principles"这本书来学习Haskell,并且在第4章结束时,#34;基本数据类型",我已经遇到困扰我的事情。这本书提到了一个函数length,并说它适用于Lists。一切都很好,但当我尝试使用各种length的{​​{1}}函数时,我看到的东西让我很困惑:

首先,让我们看一下Tuple的类型:

length

好的,所以我在上面读到"带了一个Foldable,我认为这是一个方便的列表,并返回一个Int,即列表中元素的数量。"因此,我的第一个困惑:为什么以下工作:

:t length
length :: Foldable t => t a -> Int

因为对我而言,似乎我只是将带有两个元素的元组传递给length (1, 1) 1 ,然后它返回1.是元组列表吗?元组是否可折叠?当然,为什么length

现在我更进一步:

1

另一次尝试:

length (1, 1, 1)

<interactive>:6:1:
    No instance for (Foldable ((,,) t0 t1))
      arising from a use of ‘length’
    In the expression: length (1, 1, 1)
    In an equation for ‘it’: it = length (1, 1, 1)

<interactive>:6:9:
    No instance for (Num t0) arising from the literal ‘1’
    The type variable ‘t0’ is ambiguous
    Note: there are several potential instances:
      instance Num Integer -- Defined in ‘GHC.Num’
      instance Num Double -- Defined in ‘GHC.Float’
      instance Num Float -- Defined in ‘GHC.Float’
      ...plus two others
    In the expression: 1
    In the first argument of ‘length’, namely ‘(1, 1, 1)’
    In the expression: length (1, 1, 1)

<interactive>:6:12:
    No instance for (Num t1) arising from the literal ‘1’
    The type variable ‘t1’ is ambiguous
    Note: there are several potential instances:
      instance Num Integer -- Defined in ‘GHC.Num’
      instance Num Double -- Defined in ‘GHC.Float’
      instance Num Float -- Defined in ‘GHC.Float’
      ...plus two others
    In the expression: 1
    In the first argument of ‘length’, namely ‘(1, 1, 1)’
    In the expression: length (1, 1, 1)

但是以下工作:

length (1::Int, 1::Int, 1::Int)

<interactive>:7:1:
    No instance for (Foldable ((,,) Int Int))
      arising from a use of ‘length’
    In the expression: length (1 :: Int, 1 :: Int, 1 :: Int)
    In an equation for ‘it’: it = length (1 :: Int, 1 :: Int, 1 :: Int)

对于我上面观察到的行为有什么好的解释吗?我误读了length (1::Int, 1::Int) 1 的类型吗?或者幕后还有其他事情发生了吗?

2 个答案:

答案 0 :(得分:23)

你遇到了一个Haskell 导致célèbre,引发了很多讨论和咬牙切齿。

基本上,出于Foldable(提供length的类型类)的目的,2元组不被视为两个元素的容器,而是 one 的容器元素伴随着一些背景。

您可以从任何a中提取Foldable a类型的元素列表。请注意,对于2元组,Foldable的类型变量是元组的 second 元素的类型变量,它可以与第一个元素的类型不同。

如果你有一个('c',2) :: (Char,Int)元组,那么在这种情况下你无法提取两个Int就不足为奇了!但是当类型相同时会变得混乱。

至于为什么length (1::Int, 1::Int, 1::Int)失败,3元组没有定义Foldable个实例,但为了保持一致,它们可能应该有一个Identity实例。 3元组的长度也为1。

顺便说一下,Foldable仿函数,可以被认为是一种1元组,也是Foldable,当然也有长度为1。

元组的Execute sp_MSforeachtable 'Execute master.dbo.xp_cmdshell ''sqlcmd -S DATABASE -E -d mydb -q "SET NOCOUNT ON SELECT * FROM ?" -W -o C:\TEMP\?.bak -s "|"''' 实例是否存在 ?我认为赞成肯定的基本哲学是,我们称之为“充实”。如果类型可以以明确定义的合法方式成为类型类的实例,则应该具有该实例。即使它似乎没有用,在某些情况下,也可能令人困惑。

答案 1 :(得分:12)

我喜欢danidiaz的答案,因为它提供了关于元组的Foldable实例如何工作以及它直观意味着什么的高层次直觉。然而,它的机制似乎仍然存在一些混乱;所以在这个答案中,我将重点关注&#34;幕后花絮&#34;位。相关Foldable个实例的全文为available online,如下所示:

instance Foldable ((,) a) where
    foldMap f (_, y) = f y
    foldr f z (_, y) = f y z

您已经可以从这个实例中看到,在所有Foldable方法中,每个元组的第一部分都被完全忽略。但是,要完成图片,我们需要查看minimumlength的定义。由于此实例不包含minimumlength的定义,因此我们应该查看这些定义的默认定义。 Foldable的类声明看起来像这样(不相关的位被省略):

class Foldable t where
    length :: t a -> Int
    length = foldl' (\c _ -> c+1) 0

    foldl' :: (b -> a -> b) -> b -> t a -> b
    foldl' f z0 xs = foldr f' id xs z0
      where f' x k z = k $! f z x

    minimum :: forall a . Ord a => t a -> a
    minimum = fromMaybe (error "minimum: empty structure") .
       getMin . foldMap (Min #. (Just :: a -> Maybe a))

现在,让我们扩展这些定义,看看他们从哪里获得。

length (a, b)
= { definition of length }
foldl' (\c _ -> c+1) 0 (a, b)
= { definition of foldl' }
foldr (\x k z -> k $! (\c _ -> c+1) z x) id (a, b) 0
= { definition of foldr }
(\x k z -> k $! (\c _ -> c+1) z x) b id 0
= { beta reduction }
id $! (\c _ -> c+1) 0 b
= { id $! e = e }
(\c _ -> c+1) 0 b
= { beta reduction }
1

请注意,无论我们为ab插入什么内容,结论都会成立。现在让我们做minimum。出于我们的目的,我们将(#.)替换为(.) - 唯一的区别是效率,我们不关心这一特定推理线。

minimum (a, b)
= { definition of minimum }
( fromMaybe (error "minimum: empty structure")
. getMin
. foldMap (Min . Just)
) (a, b)
= { definition of (.) }
( fromMaybe (error "minimum: empty structure")
. getMin
) (foldMap (Min . Just) (a, b))
= { definition of foldMap }
( fromMaybe (error "minimum: empty structure")
. getMin
) ((Min . Just) b)
= { definition of (.) }
fromMaybe (error "minimum: empty structure")
(getMin (Min (Just b)))
= { definition of getMin }
fromMaybe (error "minimum: empty structure") (Just b)
= { definition of fromMaybe }
b