简而言之,“类型系列”与“数据系列”?

2023-12-30

我很困惑如何选择data family and type family。维基页面上类型族 http://www.haskell.org/haskellwiki/GHC/Type_families#Injectivity.2C_type_inference.2C_and_ambiguity涉及很多细节。有时它非正式地指 Haskelldata family作为散文中的“类型家庭”,但当然也有type family在哈斯克尔。

有一个简单的示例,显示了两个版本的代码的显示位置,区别仅在于是否data family or a type family正在声明:

-- BAD: f is too ambiguous, due to non-injectivity
-- type family F a
 
-- OK
data family F a 

f :: F a -> F a
f = undefined
 
g :: F Int -> F Int
g x = f x

type and data这里的意思是一样的,但是type family版本无法进行类型检查,而data family版本很好,因为data family“创建新类型,因此是单射的”(维基页面说)。

我从这一切中得到的收获是“尝试data family对于简单的情况,如果它不够强大,那么尝试type family“。哪个很好,但我想更好地理解它。是否有维恩图或决策树我可以遵循来区分何时使用哪个?


(将评论中的有用信息提升为答案。)

独立声明与类内声明

声明 a 的两种语法不同的方式类型家族 and/or 数据族,它们在语义上是等价的:

独立:

type family Foo
data family Bar

或作为类型类的一部分:

class C where
   type Foo 
   data Bar 

两者都声明了一个类型族,但在类型类中family部分是由class上下文,因此 GHC/Haskell 缩写了该声明。

“新类型”与“类型同义词”/“类型别名”

data family F创建一个新类型,类似于data F = ...创建一个新类型。

type family F不创建新类型,类似于type F = Bar Baz 不会创建新类型(它只是创建现有类型的别名/同义词)。

非内射性的例子type family

一个例子(稍作修改)来自Data.MonoTraversable.Element https://hackage.haskell.org/package/mono-traversable-0.2.0.0/docs/Data-MonoTraversable.html:

import Data.ByteString as S
import Data.ByteString.Lazy as L

-- Declare a family of type synonyms, called `Element` 
-- `Element` has kind `* -> *`; it takes one parameter, which we call `container`
type family Element container

-- ByteString is a container for Word8, so...
-- The Element of a `S.ByteString` is a `Word8`
type instance Element S.ByteString = Word8 

-- and the Element of a `L.ByteString` is also `Word8`
type instance Element L.ByteString = Word8  

在类型族中,方程的右侧Word8命名现有类型;左侧的事物创建了新的同义词:Element S.ByteString and Element L.ByteString

拥有同义词意味着我们可以互换Element Data.ByteString with Word8:

-- `w` is a Word8....
>let w = 0::Word8

-- ... and also an `Element L.ByteString`
>:t w :: Element L.ByteString
w :: Element L.ByteString :: Word8

-- ... and also an `Element S.ByteString`
>:t w :: Element S.ByteString
w :: Element S.ByteString :: Word8

-- but not an `Int`
>:t w :: Int
Couldn't match expected type `Int' with actual type `Word8'

这些类型同义词是“非内射的”(“单向”),因此是不可逆的。

-- As before, `Word8` matches `Element L.ByteString` ...
>(0::Word8)::(Element L.ByteString)

-- .. but  GHC can't infer which `a` is the `Element` of (`L.ByteString` or `S.ByteString` ?):

>(w)::(Element a)
Couldn't match expected type `Element a'
            with actual type `Element a0'
NB: `Element' is a type function, and may not be injective
The type variable `a0' is ambiguous

更糟糕的是,GHC 甚至无法解决明确的情况!:

type instance Element Int = Bool
> True::(Element a)
> NB: `Element' is a type function, and may not be injective

注意使用“可能不是”!我认为 GHC 很保守,拒绝检查是否Element确实是内射的。 (也许是因为程序员could加上另一个type instance后来,在导入预编译模块后,增加了歧义。

单射性的例子data family

相反:在数据族中,每个右侧都包含一个唯一的构造函数 ,因此定义是单射(“可逆”)方程。

-- Declare a list-like data family
data family XList a

-- Declare a list-like instance for Char
data instance XList Char = XCons Char (XList Char) | XNil

-- Declare a number-like instance for ()
data instance XList () = XListUnit Int

-- ERROR: "Multiple declarations of `XListUnit'"
data instance XList () = XListUnit Bool
-- (Note: GHCI accepts this; the new declaration just replaces the previous one.)

With data family,看到右侧的构造函数名称(XCons, or XListUnit) 足以让类型推断器知道我们必须使用XList () not an XList Char。由于构造函数名称是唯一的,因此这些定义are单射/可逆。

If type“just”声明了一个同义词,为什么它在语义上有用?

通常,type同义词只是缩写,但是type家族同义词具有额外的力量:它们可以创建一个简单的类型(kind*) 成为“type with kind”的同义词* -> *应用于论证”:

type instance F A = B

makes B match F a。例如,这用于Data.MonoTraversable制作一个简单的类型Word8匹配类型的函数Element a -> a (Element定义如上)。

例如,(有点傻),假设我们有一个版本const仅适用于“相关”类型:

> class Const a where constE :: (Element a) -> a -> (Element a)
> instance Const S.ByteString where constE = const

> constE (0::Word8) undefined
ERROR: Couldn't match expected type `Word8' with actual type `Element a0'

-- By providing a match `a = S.ByteString`, `Word8` matches `(Element S.ByteString)`
> constE (0::Word8) (undefined::S.ByteString)  
0

-- impossible, since `Char` cannot match `Element a` under our current definitions.
> constE 'x' undefined 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

简而言之,“类型系列”与“数据系列”? 的相关文章

  • 不同编程语言中的浮点数学

    我知道浮点数学充其量可能是丑陋的 但我想知道是否有人可以解释以下怪癖 在大多数编程语言中 我测试了 0 4 到 0 2 的加法会产生轻微的错误 而 0 4 0 1 0 1 则不会产生错误 两者计算不平等的原因是什么 在各自的编程语言中可以采
  • Haskell:不在范围内:数据构造函数

    今天开始在学校学习 haskell 我遇到了函数问题 我不明白为什么它不在范围内 代码如下 ff Char gt Char gt Char ff A B x 0 y 1 x lt A y lt B x 1 y 0 和错误 md31 hs 2
  • 为什么在 where 子句中使用类型签名如此罕见?

    它是否有助于编译器优化 或者只是添加额外类型签名的多余工作 例如 人们经常看到 foo a gt b foo x bar x where bar x undefined 而不是 foo a gt b foo x bar x where ba
  • 自定义 monad 的 MonadTransControl 实例

    的文档monad control提供有关如何创建实例的示例MonadTransControl using defaultLiftWith and defaultRestoreT 该示例适用于以下情况newtype newtype Count
  • 如何使用foldr为列表创建显示实例?

    我想为我的数据类型 我的列表 编写自己的显示实例 到目前为止 我的方法是有效的 但我总是在末尾有一个逗号 我已经尝试用最后一个元素启动折叠并将其从列表中删除 但它很麻烦而且不起作用 有没有更简单的方法来获得正确的解决方案 实际 1 2 3
  • cabal install wx 缺少 C 库

    Env 操作系统 feodra 16 Haskell 平台 wxGTK 开发 GHHC 7 0 4 我正在尝试安装 wxHaskell 阴谋集团安装wx 然后给出这些错误 缺少对外国库的依赖 缺少 C 库 wx baseu 2 8 wx b
  • 管道 - 将多个来源/生产者合并为一个

    我正在使用读取文件sourceFile 但我还需要在处理操作中引入随机性 我认为最好的方法是拥有一个这样的制片人 Producer m StdGen ByteString 其中 StdGen 用于生成随机数 我打算让生产者执行 source
  • Haskell 中多核编程的现状如何?

    Haskell 中多核编程的现状如何 现在有哪些项目 工具和库可用 有哪些经验报道 2009年至2012年期间 发生了以下事件 2012 从 2012 年开始 并行 Haskell 状态更新开始出现在并行 Haskell 摘要 http w
  • 使用 Haskell 绘制图表

    是否可以使用 Haskell 绘制一个简单的图表 你们中的任何人都可以告诉我该怎么做吗 该图应至少包含 3 个点 Haskell 图表 https github com timbod7 haskell chart似乎不错 The wiki
  • Haskell / cabal 包的解决方法受到 Nix 和 Cabal 的限制?

    我最近开始开发反射平台 https github com reflex frp reflex platform 有一些额外的配置类似于优秀的反射项目骨架 https github com ElvishJerricco reflex proj
  • Python 比编译的 Haskell 更快?

    我有一个用 Python 和 Haskell 编写的简单脚本 它读取包含 1 000 000 个换行符分隔的整数的文件 将该文件解析为整数列表 对其进行快速排序 然后将其写入已排序的不同文件中 该文件与未排序的文件具有相同的格式 简单的 这
  • Haskell数据类型转换问题

    我目前正在学习 Haskell 并且一直在编写一些非常简单的程序来练习 我的程序之一是 import System IO main do putStrLn Give me year y lt getLine let res show cal
  • 如何使用类型系统编码和强制执行合法的 FSM 状态转换?

    假设我有一个类型Thing拥有国有财产A B C 合法的状态转换是A gt B A gt C C gt A 我可以写 transitionToA Thing gt Maybe Thing 这会返回Nothing if Thing处于无法转换
  • Haskell 为替代的 Either 数据类型定义 Functor 实例

    通过 Typeclassopedia 获得一些使用类型类的路由 想要替代Either的一个实例Functor 但即使检查定义Either作为一个例子Functor总是给我带来麻烦 有这个 但不会编译 data Alt a b Success
  • 来自数据类型的 Haskell 随机数

    我对 Haskell 还很陌生 我有一个数据类型 data Sentence Prop Int No Sentence And Sentence Or Sentence deriving Eq 我已经为它写了一个 Show 实例 然而 无论
  • 为什么 GHC 在这里推断出单态类型,即使禁用了单态限制?

    这是由解析 f f pure 的类型 https stackoverflow com questions 55388119 resolving the type of f f pure 55388309 noredirect 1 comme
  • 在 Haskell 中调试时打印时间戳

    我仍在学习 Haskell 并调试一些函数 并且通常有一个时间戳函数来了解某些操作何时开始和停止 doSomeAction String gt IO doSomeAction arg1 do putStrLn lt lt makeTime
  • 函数式语言与语言实现的角度有何不同

    出现了全新的 函数式编程 范式 与过程式编程相比 它需要彻底改变思维模式 它使用高阶函数 纯度 单子等 我们通常在命令式和面向对象语言中不会看到这些 我的问题是如何执行这些语言与命令式或面向对象语言的不同之处在于 例如内存管理或指针等内部结
  • Haskell:确定函数数量的函数?

    可以写一个函数吗arity a gt Integer确定任意函数的数量 使得 gt arity map 2 gt arity foldr 3 gt arity id 1 gt arity hello 0 是的 这可以非常非常容易地完成 ar
  • monadicIO 的工作原理

    我有以下代码 fastShuffle a gt IO a fastShuffle a

随机推荐