空约束有什么用?

What is an empty constraint useful for?

在Haskell写

是绝对合法的
f :: () => Int -> Int

甚至

f :: (Num i, ()) => i -> i

为什么允许 ()?在我看来它似乎没有提供任何信息,但由于它是有效的语法,我想在某些情况下它会派上用场。

您可以使用 ConstraintKinds extension 为约束定义别名,例如:

{-# LANGUAGE ConstraintKinds #-}

type <strong>Foo a</strong> = (Num a, Ord a)

myFun :: <strong>Foo a</strong> => a -> Bool
myFun = (0 <)

但因此本身并不知道您将列出多少约束条件。因此它接受任何元数的元组,因为约束的数量可以是零、一或更多。

空约束在产生约束的类型别名和类型族中很有用。例如,这是一个人为的例子。

type family F :: Type -> Constraint where
   F (a -> b) = ()
   F a        = (Show a, Read a)

那么,foo :: F t => ... 只需要 non-function 类型的 ShowRead。结合 GADTs / 单例,它可以被利用来创建 type-level 机器。

可以写一个简单的约束而不是 (),比如 Num IntBool ~ Bool,但是使用像 () 这样的特殊语法可以更好地表达意图。

当您进入 ConstraintKindsTypeFamilies 等扩展时,约束可以 计算 ,而不仅仅是静态列出。

因此,有时接口需要您提供某种类型的东西 Constraint,但您可能不需要任何实际约束。然后,您需要一种表示“空”约束的方法;一个没有强加任何实际要求(并且在它拥有时也没有授予额外权力),但仍然是一种善意的东西 Constraint。鉴于我们可以通过编写 (C1, C2, C3, ...) 将多个约束组合成一个 Constraint 类型的项目,那么空约束的明显语法是 ()。 (以完全相同的方式,我们得到 () 作为“unit”的语法,可以解释为“空元组”)

鉴于 () 是类型 Constraint 的有效对象,您可以在任何需要类型 Constraint 的地方使用它。这包括类型签名约束元组的一个成员,例如 foo :: (EQ a, ()) => ...。它还包括 全部 约束,例如 bar :: () => ...。这样做没有用,但根据适用于任何其他 Constraint.

的所有相同规则,它的含义非常清楚

一般来说,通过仔细思考规则,使它们的任何可能组合都有意义,然后尽可能严格地遵守它们,你就可以创建一种易于理解的编程语言。试图添加越来越多的特殊情况以排除对特定功能的“无用”使用是一项永无止境的任务,这使得使用该系统的每个人都必须记住所有特殊情况以及正常规则,迟早有人会被束缚想出一个你没有想到的上下文,毕竟“无用”的东西实际上并不是无用的。在所有可以编写的“无用”代码中,只有一小部分 极小 有望被琐碎的句法规则检测到,例如“在 => 左侧的类型签名中你可以写除 () 之外的任何约束”。所以一般来说,编程语言设计者需要一个很好的理由来禁止像空约束这样的东西;问为什么 允许 这样的事情经常得到无聊的答案:“为什么不呢?”