haskell - Haskell类型函数?

标签 haskell types

考虑以下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类型的最外面的空构造函数,如以下示例所示?
*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

这里的a是什么?由于f不能成为身份,但我们需要id :: a -> 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]

但是,如果我将其提供给先前的元组示例,该怎么办?

我真的在寻找解释性的答案(在夏季 class 中,我只见过Haskell不到两个月的时间),而不是引用诸如(f a)这样的东西来...说FlexibleContexts可在任意fmap上工作。

最佳答案

令人困惑的事实是,[]在Haskell中的不同上下文中具有两种不同的含义,这使您感到困惑,这使您难以解释其余的实验。

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

尽管看起来很奇怪,但实际上您可以根据需要将[Int]编写为[] Int,因此,当您不使用它时,通常只在类型级别看到[]

让我们再次看一下您的数据声明:

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

在左侧,您已经声明了的形状,类型为 PropertyProperty f a形成一个类型。在右侧,通过列出可能的值构造函数(PropertyZilch)以及这些构造函数中“槽”的类型(无Zilch;一个槽的)来声明该类型的的形状。类型String和另一个类型f a(对于Property)。

因此,从中我们可以看出,无论fa是什么,类型表达式f a(应用于fa)都必须形成具有值的类型。但是f不一定非要(实际上也可以不是)正常的值类型! f值构造函数中没有Property类型的插槽。

使用一个更清晰的示例是:
*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类型构造函数应用于Bool类型。 Maybe Bool值放入f a值构造函数的Property插槽中,该插槽很简单:fMaybeaBool

所以回到您的原始示例:
*Main> var = Property "Colors" [1,2,3,4]
*Main> :t var
var :: Num a => Property [] a

您正在使用f a填充[1, 2, 3, 4]插槽。那是某种数字的列表,所以它是Num t => [t]。因此,a中的f 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],但是我不确定是否更清楚。但是归结为,我们可以通过将第一个“单元” f a作为(,) tf作为[Char]将其与a匹配。然后,这给了我们Property ((,) t) [Char](只有我们还必须放回我们之前忘记的Num t约束)。

最后是这个:
*Main> var = Property "Colors" 20
*Main> :t var
var :: Num (f a) => Property f a

在这里,我们用f a(某种数字)填充20插槽。我们尚未确切指定数字的类型,因此Haskell愿意相信它可以是Num类中的任何类型。但是我们仍然需要该类型具有可以与f a匹配的“形状”:将某种类型构造函数应用于其他某种类型。整个类型表达式f a需要与20的类型匹配,因此这就是Num约束的条件。但是关于fa可能是什么,我们没有说什么,而且20可以满足Num约束的任何类型,因此它可以是我们想要的任何Num (f a) => f a,因此,为什么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扩展可以允许类型几乎反映任何值的结构,因此您可以拥有类型级别的列表。但这不是这里所发生的事情,它并不是真正可以使用的功能,除非您对香草Haskell中的类型和值的工作方式有了很好的了解,所以我建议您忽略此脚注并假装它不存在。

关于haskell - Haskell类型函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45202843/

相关文章:

haskell - `mfix` 未按预期工作

C 和无限大小变量

c++ - 从模板函数中取消引用的迭代器推断类型

swift - 不同类(class)类型

haskell 错误 "No instance for (Eq a0) arising from a use of..."

haskell - eta减少什么时候可以改变函数的类型?

typescript - 如何在 typescript 的接口(interface)或对象文字类型中使某些属性成为必需但全部允许?

java - 无法解析为类型 - .java 文件位于包中并且未被看到

haskell - 'fmapDefault' 中的 'Data.Traversable' 有什么意义?

haskell - 有逆变单子(monad)吗?