了解数据类型,类型类的签名,并使数据类型成为类型类的实例

时间:2019-02-11 07:04:17

标签: haskell typeclass type-kinds

热衷于阅读以了解Haskell为您带来的好处!并且在理解实例和种类方面遇到很大麻烦。

Q1:那么t中的类型Tofu t充当具有类型签名(* -> (* -> *)) -> *的函数吗? tofu的整体种类签名为* -> *,不是吗?因为(* -> *) -> *的结果是*(* -> (* -> *)) -> *的结果也是

第二季度:当我们要创建类型为Frank a b的{​​{1}}实例时,数据类型Tofu t也必须与Frank a b具有相同的种类。这意味着t的种类为a*的种类为b,而* -> *的种类为b a,结果为(* -> *) -> * 。正确吗?

Q3:*中的x代表tofu x,因为两者都具有j a的类型。 *及其种类Frank应用于(* -> (* -> *)) -> *。但是我不确定将x表示为j a会如何区分x中的x中的tofu x和{{ 1}},即j a

我对在数据类型或类(例如:x中的Frank xa j中的b)内部具有函数的想法很陌生有点混乱

我在此保留链接,因为引用会使帖子看起来不必要地冗长。 link

Frank a b

2 个答案:

答案 0 :(得分:5)

第一季度:

  

因此,豆腐t中的类型t充当具有类型签名(*->(*-> *))-> *?

的函数

t的种类是* -> (* -> *) -> *,或更明确地说是* -> ((* -> *) -> *),而不是(* -> (* -> *)) -> *

  

豆腐的整体特征是*-> *,不是吗?

tofu没有类型签名,只有类型构造函数有。其类型为*。它的参数和结果的类型也是如此。而且对于任何功能都一样。

Q2:您从一个错误的假设开始:instance Tofu Frank使Frank类型的构造函数成为Tofu而不是Frank a b的实例。因此,Frank的类型必须与t相同,而不是Frank a b(其类型为*)。

  

b a将会是(*-> *)-> *

否,b ab类型的* -> *a类型的*的应用程序,因此该应用程序是*类型的应用程序。就像b是类型x -> y的函数,而a是类型x的值一样,b a的类型为y,不是(x -> y) -> x:只需将xy替换为*

第三季度:

  

豆腐x中的x代表j a

“具有类型”,而不是“代表”。

  

因为两者都有*

x没有种类,因为它不是种类。

  

以实物类型(*->(*-> *))-> *应用于x

不,在

tofu x = Frank x

Frank的{​​{1}} data constructor,而不是类型构造函数。这是一个带有签名x的函数(将b a1 -> Frank a1 b重命名为a的函数)。所以tofub ~ j

答案 1 :(得分:2)

Alexey已经可以回答您的问题了。取而代之的是,我将在您的示例中详细说明所有相关的细节。

class Tofu t where
  tofu :: j a -> t a j
          ^^^    ^^^^^
          ^^^^^^^^^^^^

突出显示的位必须具有种类*。 (类型级别)箭头两侧的任何内容都必须具有类型* [1] ,并且箭头术语本身(即整个j a -> t a j术语)也具有类型*。实际上,可以被值占用的任何“类型” [2] 都具有类型*。如果它有其他任何一种,就没有任何值(它只是用来在别处构造适当的类型)。

因此,在tofu的签名中,以下内容成立

j a :: *
t a j :: *

因为它们是(->)的参数,所以被用作“有人居住”类型。

这是限制类的唯一因素。特别地,a可以是任何种类。使用PolyKinds [3]

a :: k   -- for any kind k
j :: k -> *
t :: k     ->   (k -> *) -> *
     ^          ^^^^^^^^    ^
 kind of a      kind of j   required since is used as inhabited type by ->

因此,我们找到了所需的t类型。

我们可以对Frank使用类似的推理。

data Frank a b = Frank {frankField :: b a}
     ^^^^^^^^^                        ^^^

同样,突出显示的位必须具有种类*,因为它们可以具有值。否则没有约束。概括地说,我们有

a :: k
b :: k -> *
Frank a b :: *

因此

Frank :: k -> (k -> *) -> *

我们可以看到Frank的种类与Tofu所需的种类匹配。但这对于更具体的种类也很有意义,例如:

data KatyPerry a b = KatyPerry a (b Int)

尝试推断她的种类,并检查它是否比Tofu要求的种类更具体。


[1]如果我们假设TypeInType,则在种类级别的箭头上也是如此。没有TypeInType,“种类”称为 sorts ,没有人担心它们。通常在该级别上没有发生任何有趣的事情。

[2]我将“类型”用引号引起来,因为从技术上讲,只有类型为*的东西才称为类型,其他所有东西都称为类型构造函数。我试图做到这一点很精确,但是我找不到一次同时引用两者的非笨拙方式,因此该段变得非常混乱。所以是“类型”。

[3]如果没有PolyKinds,则诸如k这样不受约束的任何事物都将专用于*。这也意味着Tofu的类型可能取决于您首先在哪种类型上对其进行实例化,或者是在同一模块中还是在不同模块中将其实例化。这不好。 PolyKinds很好。