尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

PolyKinds探秘:haskell-exercises中的种类多态,为什么Proxy无所不能

PolyKinds探秘:haskell-exercises中的种类多态,为什么Proxy无所不能 PolyKinds探秘haskell-exercises中的种类多态为什么Proxy无所不能【免费下载链接】haskell-exercisesA little course to learn about some of the more obscure GHC extensions.项目地址: https://gitcode.com/gh_mirrors/has/haskell-exercises在 haskell-exercises 这个 GHC 扩展练习项目中第八课将目光对准了 Haskell 类型系统里最低调也最强大的开关之一——PolyKinds种类多态。如果你已经掌握了类型多态却对类型的类型感到陌生这篇文章将带你零基础理解种类多态的核心思想并回答那个经典疑问为什么小小的Proxy在开启 PolyKinds 之后突然变得无所不能前置知识建议先完成 haskell-exercises 中的 KindSignatures 练习 与 DataKinds 练习理解种类签名与类型提升promotion的基本概念本课会大量用到它们。先补个课Kind种类到底是什么很多人初学 Haskell 时被三个概念绕晕值、类型、种类。其实它们是一条清晰的层级链层级例子一句话解释值Value42 :: Int程序运行时的数据类型TypeInt :: Type即*值的类型种类KindType - Type类型的类型Maybe :: Type - TypeEither :: Type - Type - Type而Int :: Type。种类就是类型构造器的签名就像类型是函数的签名一样。类型多态没问题那种类多态呢值层面的多态你早已熟悉id :: a - a对任何类型a都成立无需为Int、String各写一份。haskell-exercises 的第八课开篇就抛出一个灵魂拷问类型层面能不能也这么多态type family Id (x :: a) :: a where Id x x在 08-PolyKinds/src/PolyKinds.hs 中GHC 会毫不犹豫地报错Unexpected kind variable a Perhaps you intended to use PolyKinds这里的a是一个种类变量意味着我们希望这个类型族在输入、输出的类型上也是多态的。没有 PolyKinds 扩展时GHC 只允许你写出固定的种类比如Type - Type开启 PolyKinds 后类型构造器的参数种类可以是一个变量由编译器在使用时统一推导。这就是种类多态的全部意义——把类型的类型也变成一等公民。为什么 Proxy 无所不能三个字的答案k - *Proxy是一个用途广泛的占位类型它不携带任何数据只用来在类型层面标记某个类型。在 GHCi 里试一下你会看到有趣的现象 data Proxy a Proxy :k Proxy Proxy :: * - * -- 没开 PolyKinds只能装类型 :set -XPolyKinds data Proxy a Proxy :k Proxy Proxy :: k - * -- 开了 PolyKinds任何种类都能装GHC 会偷看类型的构造器Proxy的构造器完全没有用到类型参数a不像Maybe的Just会用到。既然没用上那参数的种类是什么都无所谓——于是 GHC 将它推广为k - *k可以是Type、Nat、Symbol、Constraint……任你挑选。这正是Proxy无所不能的真相它从只接受类型升级为接受任何种类的东西与 DataKinds 提升出来的True、Z、标签等类型级数据完美配合成为类型级编程中最趁手的占位符。在 04-DataKinds 练习 里你见过的类型级自然数、字符串现在都能通过Proxy在函数签名里被自由引用。隐藏大招类型族可以偷看种类种类多态还有一个值层面多态没有的特权。看这个例子type family Smuggler (x :: k) :: k where Smuggler (IO (Secrets, a)) IO a Smuggler 0 1 Smuggler a a类型族虽然声明对任何种类k都有效但它的规则却可以根据具体种类来分派遇到IO (Secrets, a)就剥掉外层遇到0就返回1。换句话说类型族不是参数化的——type family Id (x :: k) :: k这个签名比id :: a - a透露的实现信息要少得多。这种额外能力意味着种类多态不只是省几行代码它让类型族可以把种类当作额外的参数来模式匹配为依赖类型风格的编程比如按种类生成单例打开了大门。如何动手练习一条命令跑起来获取 haskell-exercises 并进入第八课目录git clone https://gitcode.com/gh_mirrors/has/haskell-exercises cd 08-PolyKinds cabal repl # 或 stack repl配合 ghcid 还能实时反馈类型错误ghcid -c cabal repl练习主战场在 08-PolyKinds/src/Exercises.hs理论讲解在 08-PolyKinds/src/PolyKinds.hs整个课程结构见 README.md。第六课六道递进练习从约束到单例再到依赖对练习一把约束列表推广到任意种类All c xs把约束c应用到类型列表xs的每个元素上。它为什么被限制在Type和Constraint上能否用 PolyKinds 推广得更一般这是理解种类作为参数的第一课。练习二让 Tagged 的标签更灵活data Tagged (name :: Symbol) (a :: Type) Tagged { runTagged :: a }类型级字符串标签Tagged Important Int很酷但能否把Symbol和Type都推广成任意种类以及为什么实践中更偏爱用 DataKinds 提升的和类型当标签练习三类型等价 (::) 的最一般种类a :: b是单构造子的 GADT用Refl证明类型相等。它的种类是什么PolyKinds 会改变它吗最一般的种类又该如何声明这题帮你建立种类也是可以量化的直觉。练习四为 Bool 和 Nat 编写 Sing 实例type family Sing (x :: k) :: Type单例库的核心——从种类到其单例类型的映射。利用类型族能在种类上模式匹配的特性为Bool和Nat各写一个Sing实例几乎是一行一个的抄写题但会给你极大的成就感。练习五写出 Sigma 依赖对data Sigma (f :: Nat - Type) where依赖对Sigma 类型把某个种类的单例和被该类型索引的数据打包在一起存在量化掉具体的长度。写出它的定义再想想能不能推广到任何单例族这正是 PolyKinds 大显身手的地方。练习六用 Sigma 组织通信协议用提升的Label Client | Server索引一个 GADT让同一条消息既能装客户端数据也能装服务端数据再写一个serverLog函数按标签筛选。最后一问还留了个悬念这个场景用 Sigma 是不是杀鸡用牛刀结合前面的章节有没有更轻量的方案结语种类多态类型级编程的最后一公里回到最初的问题为什么Proxy无所不能因为它借助 PolyKinds从* - *一跃成为k - *从此任何种类的类型级数据都能被它标记。更妙的是PolyKinds 还让类型族能够感知种类、按种类分派这是值层面多态永远无法企及的能力。种类多态是通往依赖类型编程的重要台阶。haskell-exercises 的第八课用六道层层递进的练习把约束、单例、依赖对这些高级话题串成了一条清晰的学习路径。完成它你会发现原来 Haskell 的类型系统比你想的还要深邃得多。【免费下载链接】haskell-exercisesA little course to learn about some of the more obscure GHC extensions.项目地址: https://gitcode.com/gh_mirrors/has/haskell-exercises创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表