【问题标题】:Haskell type functions?Haskell 类型的函数?
【发布时间】:2017-07-19 23:46:47
【问题描述】:

考虑一下这个 ADT:

data Property f a = Property String (f a) | Zilch
  deriving Show

这里的f 是什么?它是作用于a 的函数吗?它是“类型函数”吗?讲师说 Haskell 有图灵完备的类型语言……那么在这种情况下,我假设类型也可以具有功能?

*Main> var = Property "Colors" [1,2,3,4]
*Main> :t var
var :: Num a => Property [] a

f 在这方面表现如何?由于[] 是空列表的构造函数,那么f 是否总是会成为a 类型的最外层空构造函数,如下例所示?

*Main> var = Property "Colors" [(1,"Red"),(2,"Blue")]
*Main> :t var
var :: Num t => Property [] (t, [Char])

*Main> var = Property "Colors" (1,"Red")
*Main> :t var
var :: Num t => Property ((,) t) [Char]

后一个我不太明白,但如果有人说 Haskell 推断出那个元组的空构造函数,我可以买那个。另一方面,

*Main> var = Property "Colors" 20
*Main> :t var
var :: Num (f a) => Property f a

这里的f 是什么?不能是身份,因为id :: a -> a 但我们需要(f a)

我设法使我的 ADT 成为函子:

instance Functor f => Functor (Property f) where
  fmap fun (Property name a) = Property name (fmap fun a)
  fmap g Zilch               = Zilch 

所以像下面这样的工作

*Main> var = Property "Colors" [1,2,3,4]
*Main> fmap (+1) var
Property "Colors" [2,3,4,5]

但是如果我把它交给前面的元组示例呢?

我真的在寻找解释性的答案(在暑期课程中只见过 Haskell 两个月),而不是引用诸如 FlexibleContexts 之类的东西以允许 ... 说 fmap 可以在任意 a 上工作。

【问题讨论】:

  • Haskells 类型系统是不是图灵完备的。 GHC 中有扩展使其图灵完备。
  • re: Property "Colors" (1,"Red") ,在 GHCi 提示符下尝试 :t (1,3) :: ((,) Int) Integer

标签: haskell types


【解决方案1】:

[] 在 Haskell 的不同上下文中表示两种不同的东西,这让你很难理解其余的实验。

在值级别[] 确实是列表的空构造函数。但是当您询问Property "Colors" [1,2,3,4] 的类型并看到Property [] a 时,您看到的是type 表达式,而不是值表达式。在类型级别没有空列表。1[] 是列表类型的类型构造函数。您可以有[Int](整数列表的类型)、[Bool](布尔列表的类型)或[a]a 的多态列表类型); [] 在这些示例中应用于IntBoola

如果你愿意,你实际上可以把[Int]写成[] Int,虽然它看起来很奇怪,所以当你想不应用它时,你通常只会在类型级别看到[]

让我们再看看你的数据声明:

data Property f a = Property String (f a) | Zilch

在左侧,您声明了 type Property 的形状; Property f a 形成一个类型。在右侧,您通过列出可能的值构造函数(PropertyZilch)以及其中的“槽”类型来声明该类型中的 的形状构造函数(Zilch 没有;String 类型的一个插槽和f a 类型的另一个插槽,Property)。

因此我们可以看出,无论fa 是什么,类型表达式f af 应用于a)必须形成一个有值的类型。但是f 本身不一定是(事实上它不可能是)一种正常类型的值! Property 值构造函数中没有 f 类型的槽。

一个更清晰的例子是这样的:

*Main> var = Property "Stuff" (Just True)
*Main> :t var
var :: Property Maybe Bool

如果您不知道,Maybe 是一个内置类型,其声明如下所示:

data Maybe a = Just a | Nothing

这个例子很好,因为我们没有在类型级别和值级别使用相同的名称,这样可以避免在您尝试了解事物如何工作时产生混淆。

Just TrueMaybe Bool 类型的值。在值级别,我们将Just 数据构造函数应用于值True。在类型级别,我们将Maybe 类型构造函数应用于类型BoolMaybe Bool 值进入 Property 值构造函数的 f a 槽,这很简单:fMaybeaBool

回到你原来的例子:

*Main> var = Property "Colors" [1,2,3,4]
*Main> :t var
var :: Num a => Property [] a

您正在用[1, 2, 3, 4] 填充f a 槽。那是某种数字的列表,所以它是Num t => [t]。所以f a 中的at(需要附带Num 约束),f列表类型构造函数 []。这个[]Maybe,不像Nothing

*Main> var = Property "Colors" (1,"Red")
*Main> :t var
var :: Num t => Property ((,) t) [Char]

这里f a 槽被(1, "Red") 填充,它的类型为Num t => (t, [Char])(请记住String 只是[Char] 的另一种写法)。现在要理解这一点,我们必须有点挑剔。现在忘记约束,只关注(t, [Char])。不知何故,我们需要将其解释为应用于其他事物的事物,因此我们可以将其与f a 匹配。事实证明,虽然我们为元组类型(如(a, b))提供了特殊的语法,但它们实际上就像您可以在没有特殊语法的情况下声明的普通ADT。 2 元组类型是一个类型构造函数,我们可以编写 (,) 应用于其他两种类型,在本例中为 t[Char]。而且我们可以使用部分应用的类型构造函数,因此我们可以将(,) 应用于t 视为一个单元,而该单元应用于[Char]。我们可以将这种解释写成 Haskell 类型表达式((,) t) [Char],但我不确定这是否更清楚。但归根结底,我们可以通过将第一个“单元”(,) t 设为 f 并将 [Char] 设为 a 将其与 f a 匹配。然后给我们Property ((,) t) [Char](只是我们还必须放回我们之前忘记的Num t约束)。

最后是这个:

*Main> var = Property "Colors" 20
*Main> :t var
var :: Num (f a) => Property f a

这里我们用20 填充f a 槽,某种数字。我们还没有具体说明这个数字是什么类型,所以 Haskell 愿意相信它可以是 Num 类中的任何类型。但是我们仍然需要该类型具有可以与f a 匹配的“形状”:某些类型构造函数应用于其他类型。它是整个类型表达式f a 需要匹配20 的类型,所以这就是具有Num 约束的东西。但是我们还没有说什么fa 可能是什么,20 可以是 any 满足Num 约束的类型,所以它可以是任何@987654418 @ 我们想要它,因此为什么 var 的类型在 fa 中仍然是多态的(只是添加了约束)。

您可能只见过IntegerIntDouble 等数字类型,所以想知道f a 怎么可能是一个数字;所有这些示例都只是单一的基本类型,而不是应用于某物的东西。但是您可以编写自己的 Num 实例,因此 Haskell 永远不会假定给定类型(或类型的形状)不可能是数字,如果您真的尝试使用它,它只会抱怨它找不到Num 实例。所以有时你会得到类似可能错误的东西,但 Haskell 接受(目前)Num 类型的东西是你没想到的。

事实上内置库中已经有类型,这些类型确实具有复合类型级结构并具有Num 实例。一个例子是Ratio 类型,用于将小数表示为两个整数的比率。您可以使用Ratio IntRatio Integer,例如:

Main*> 4 :: Ratio Int
4 % 1

所以你可以说:

*Main> var = Property "Colors" (20 :: Ratio Integer)
*Main> :t var
var :: Property Ratio Integer

1 实际上可以有,启用DataKinds 扩展以允许镜像几乎任何值的结构的类型,因此您可以拥有类型级列表。但这不是这里发生的事情,它不是一个真正可以使用的功能,直到你很好地掌握了 vanilla Haskell 中类型和值的工作方式,所以我建议你忽略这个脚注,假装它还不存在.

【讨论】:

  • 这是一个很好的答案,非常感谢。显然,我有很多阅读/方法要走。如果有人能谈到当我将 type function f 限制为 functor 时发生的事情,那就太完美了。
【解决方案2】:

这里的f 是什么?它是作用于a 的函数吗?它是“类型函数”吗?

是的,它确实是一个类型函数,从某种意义上说,它接受一个类型并产生一个类型,即它的 kindType -> Type——或者,正如 Haskell 传统上写的那样

> :k []
[] :: * -> *

f 在这方面表现如何?由于[] 是空列表的构造函数...

这是个误会。实际上,在 Haskell 中有两个不同的东西叫做 []

  1. 列表值构造函数[] :: [a]。这会生成一个空列表(或任意包含类型 - 因为它实际上包含零个元素,无论如何您并不关心该类型)。

  1. 列表类型构造函数[] :: * -> *。这需要一个 type 并给出包含该类型值的列表的类型。这个论点很重要,因为大多数有趣的列表显然不是空的。

Property [] a 中,您处理的是类型构造函数,与值构造函数不同,它是对类型进行操作的函数,因此您可以为f 实例化它。

Property ((,) t) [Char]

在这里,您发现了另一个类型级函数:元组类型构造函数。这需要两个类型参数并给出一个类型(这些类型的元组的类型)。使用(,) t,您已经将其应用于一个类型参数,但将另一个保持打开状态,因此您可以再次将其用作f 之类的单参数类型函数。

【讨论】:

    【解决方案3】:

    导师说Haskell有图灵完备的类型语言……

    首先,这种说法是错误的。 Haskell 没有图灵完备的类型系统。 GHC 中有 扩展 使其成为图灵完备的,但纯 Haskell 没有图灵完备的类型系统。

    f 在这方面表现如何?既然[] 是空列表的构造函数,那么f 是否总是会像下面的示例中那样成为 a 类型的最外层空构造函数?

    您将类型构造函数与值构造函数混合在一起。 Haskell 将列表定义为:

    data <b>[]</b> a = [] | a : (<b>[]</b> a)

    粗体为[]类型构造函数,非粗体为值构造函数。如果您在类型签名中写入 [],则表示该类型。例如map 的类型为map :: (a -&gt; b) -&gt; [] a -&gt; [] b

    现在正如我们在data [] 的定义中看到的,我们有一个类型参数。我们需要将[] 类型应用于另一种类型,然后它才能成为“具体”类型。因此[] 的“元类型”是* -&gt; *:它需要。

    Property 类型也是如此,它具有元类型 * -&gt; * -&gt; *,因为它需要两个类型参数。另一方面,Property [] 具有元类型 * -&gt; *

    【讨论】:

      【解决方案4】:

      你应该阅读functorskinds

      f 有一个 like 函子,(但它不是函子)首先意味着类型 f 的类型(类型的类型被命名为 @987654327 @ in haskell),是* -&gt; *,意思是它接受一个类型,然后返回一个类型给你。

      这就是为什么var = Property "Colors" [1,2,3,4]var 有这种“稀有”类型不是因为[] 是列表的空构造函数,而是因为[] :k* -&gt; *

      让我们再看一个例子:

      var2 = Property "Perhaps A Bool" (Just True)
      

      var2 :t 的类型:

      var2 :: Property Maybe Bool
      

      这是为什么,因为Maybe也有kind->,它在等待一个类型返回另一个,看类型,它不说 @987654337 @ 它说 Maybe Bool。就像您的另一个示例一样,例如 [] 而不是 [Int]

      更多关于functorskinds

      【讨论】:

      • f 不必是 Functor。例如,Property "functions" [Endo id] 的类型非常好。
      • @DanielWagner 你的意思是Property "functions" (Endo id)。 :)
      • @DanielWagner 你是对的,谢谢你的明智评论,我很感激,如果你想再看看我的编辑,请
      • @WillNess endo id 或 [endo id] 有什么区别? (我更新了答案,如果你想再看看!谢谢
      • Property "functions" [Endo id] :: Property [] (Endo a)[] 是 Functor,所以这不是反例。 Property "functions" (Endo id) :: Property Endo aEndo 不是 Functor。
      猜你喜欢
      • 2021-12-31
      • 1970-01-01
      • 2011-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多