Haskell Data.List.Class和语法

tin*_*lyx 8 syntax haskell typeclass

我正在尝试阅读Haskell包Data.List.Class的源代码.(列表0.4.2).但我坚持使用一些语法.

在开头,它写着:

data ListItem l a =
    Nil |
    Cons { headL :: a, tailL :: l a }
Run Code Online (Sandbox Code Playgroud)

我不熟悉第3行的语法.我猜这最后一行相当于Cons a (l a)??.但我不太确定.我注意到文件的标题说:{-# LANGUAGE FlexibleContexts, TypeFamilies #-}.

然后,当我继续时,声明有一种奇怪的用法type: type ItemM l :: * -> *我无法理解.

Data.List.Class
-- | A class for list types. Every list has an underlying monad.
class (MonadPlus l, Monad (ItemM l)) => List l where
    type ItemM l :: * -> *
    runList :: l a -> ItemM l (ListItem l a)
    joinL :: ItemM l (l a) -> l a
    cons :: a -> l a -> l a
    cons = mplus . return
Run Code Online (Sandbox Code Playgroud)

谁能帮忙解释一下这些意思?我对Data.List有一个完美的理解,但这种类型的东西对我来说并不是很清楚.我还搜索了有关使用Data.List.{Class,Tree}的wiki,示例和/或教程,但似乎没有任何内容,除了代码附带的注释.这里有什么指示?

谢谢.

- 更新 - 第一个答案(@Chris)帮助我理解了Kind签名和Record Syntax,这非常有用.但是,就如何捕获/定义List的行为以及它为熟悉的Data.List定义添加的值而言,我仍然无法理解整个代码片段.以下是一些进一步的细节,其中只有两个实例语句.这个Identity词也来自import Data.Functor.Identity (Identity(..)).你能帮我解释一下这是类型类的功能,以便像我们通常所知的那样捕获列表的特征吗?我再次在网上搜索它,但除了代码本身之外,Data.List.Class确实没有文档.有谁知道?

另外,type在类型类约束中是否有另一个使用类似于此示例中的语句的示例?我搜索了learnyouahaskell.com/(@Landei),但找不到这样的例子.我假设type这里的用法类似于你typedef在C++模板中用's'来定义'类型上的函数',对吗?

再次感谢.

instance List [] where
    type ItemM [] = Identity
    runList [] = Identity Nil
    runList (x:xs) = Identity $ Cons x xs
    joinL = runIdentity
    cons = (:)

instance Functor m => Functor (ListItem m) where
    fmap _ Nil = Nil
    fmap func (Cons x xs) = Cons (func x) (fmap func xs)
Run Code Online (Sandbox Code Playgroud)

Chr*_*lor 9

记录语法

这个

data ListItem l a = Nil | Cons { headL :: a, tailL :: l a }
Run Code Online (Sandbox Code Playgroud)

被称为记录语法.当您猜测结构与您键入的结构相同时,您就是正确的

data ListItem l a = Nil | Cons a (l a)
Run Code Online (Sandbox Code Playgroud)

但是,您还可以获得两个访问器功能:

headL :: ListItem l a -> a
headL (Cons a _) = a

tailL :: ListItem l a -> l a
tailL (Cons _ as) = as
Run Code Online (Sandbox Code Playgroud)

记录语法是语法糖 - 这里它可以节省大约4行代码.您可以按照正常方式进行模式匹配,如本段正上方的代码,或者您可以在模式匹配中使用记录语法:

safeHeadL :: ListItem l a -> Maybe a
safeHeadL Nil                = Nothing
safeHeadL (Cons {headL = a}) = Just a
Run Code Online (Sandbox Code Playgroud)

再次,这是对标准模式匹配的贬低.

亲切的签名

这个

class (MonadPlus l, Monad (ItemM l)) => List l where
  type ItemM l :: * -> *
  runList :: l a -> ItemM l (ListItem l a)
  joinL   :: ItemM l (l a) -> l a

  cons :: a -> l a -> l a
  cons = mplus . return
Run Code Online (Sandbox Code Playgroud)

是一个类型家庭声明.这条线

  type ItemM l :: * -> *
Run Code Online (Sandbox Code Playgroud)

是一种亲切的签名.当我们说某事有点时*,我们的意思是它是一种基本类型,如IntFloat.要说某种东西有种* -> *意味着它是一种类型构造函数,即它需要一个类型并返回另一种类型.

Maybe类型的构造有这样的签名:

Maybe :: * -> *
Run Code Online (Sandbox Code Playgroud)

请记住,Maybe它本身不是一种类型.它必须被赋予一个类型,然后它返回一个类型.给它Int,你得到Maybe Int.给它Double,你得到Maybe Double.

类型构造函数ItemM l采用类型参数(类型*)并返回类似的东西*.请注意,既然l是实物*,你就有

ItemM :: * -> * -> *
Run Code Online (Sandbox Code Playgroud)

ie ItemM接受两种类型,并返回一个类型(等效地,它采用一种类型并返回一元类型的构造函数).

通过在类中包含类型声明,您可以强加约束,即在类的所有实例中,lin ItemM l必须与lin 匹配List l.创建一个不匹配的类实例是不可能的.