空约束有什么用?
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 类型的 Show
和 Read
。结合 GADTs / 单例,它可以被利用来创建 type-level 机器。
可以写一个简单的约束而不是 ()
,比如 Num Int
或 Bool ~ Bool
,但是使用像 ()
这样的特殊语法可以更好地表达意图。
当您进入 ConstraintKinds
和 TypeFamilies
等扩展时,约束可以 计算 ,而不仅仅是静态列出。
因此,有时接口需要您提供某种类型的东西 Constraint
,但您可能不需要任何实际约束。然后,您需要一种表示“空”约束的方法;一个没有强加任何实际要求(并且在它拥有时也没有授予额外权力),但仍然是一种善意的东西 Constraint
。鉴于我们可以通过编写 (C1, C2, C3, ...)
将多个约束组合成一个 Constraint
类型的项目,那么空约束的明显语法是 ()
。 (以完全相同的方式,我们得到 ()
作为“unit”的语法,可以解释为“空元组”)
鉴于 ()
是类型 Constraint
的有效对象,您可以在任何需要类型 Constraint
的地方使用它。这包括类型签名约束元组的一个成员,例如 foo :: (EQ a, ()) => ...
。它还包括 全部 约束,例如 bar :: () => ...
。这样做没有用,但根据适用于任何其他 Constraint
.
的所有相同规则,它的含义非常清楚
一般来说,通过仔细思考规则,使它们的任何可能组合都有意义,然后尽可能严格地遵守它们,你就可以创建一种易于理解的编程语言。试图添加越来越多的特殊情况以排除对特定功能的“无用”使用是一项永无止境的任务,这使得使用该系统的每个人都必须记住所有特殊情况以及正常规则,迟早有人会被束缚想出一个你没有想到的上下文,毕竟“无用”的东西实际上并不是无用的。在所有可以编写的“无用”代码中,只有一小部分 极小 有望被琐碎的句法规则检测到,例如“在 =>
左侧的类型签名中你可以写除 ()
之外的任何约束”。所以一般来说,编程语言设计者需要一个很好的理由来禁止像空约束这样的东西;问为什么 允许 这样的事情经常得到无聊的答案:“为什么不呢?”
在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 类型的 Show
和 Read
。结合 GADTs / 单例,它可以被利用来创建 type-level 机器。
可以写一个简单的约束而不是 ()
,比如 Num Int
或 Bool ~ Bool
,但是使用像 ()
这样的特殊语法可以更好地表达意图。
当您进入 ConstraintKinds
和 TypeFamilies
等扩展时,约束可以 计算 ,而不仅仅是静态列出。
因此,有时接口需要您提供某种类型的东西 Constraint
,但您可能不需要任何实际约束。然后,您需要一种表示“空”约束的方法;一个没有强加任何实际要求(并且在它拥有时也没有授予额外权力),但仍然是一种善意的东西 Constraint
。鉴于我们可以通过编写 (C1, C2, C3, ...)
将多个约束组合成一个 Constraint
类型的项目,那么空约束的明显语法是 ()
。 (以完全相同的方式,我们得到 ()
作为“unit”的语法,可以解释为“空元组”)
鉴于 ()
是类型 Constraint
的有效对象,您可以在任何需要类型 Constraint
的地方使用它。这包括类型签名约束元组的一个成员,例如 foo :: (EQ a, ()) => ...
。它还包括 全部 约束,例如 bar :: () => ...
。这样做没有用,但根据适用于任何其他 Constraint
.
一般来说,通过仔细思考规则,使它们的任何可能组合都有意义,然后尽可能严格地遵守它们,你就可以创建一种易于理解的编程语言。试图添加越来越多的特殊情况以排除对特定功能的“无用”使用是一项永无止境的任务,这使得使用该系统的每个人都必须记住所有特殊情况以及正常规则,迟早有人会被束缚想出一个你没有想到的上下文,毕竟“无用”的东西实际上并不是无用的。在所有可以编写的“无用”代码中,只有一小部分 极小 有望被琐碎的句法规则检测到,例如“在 =>
左侧的类型签名中你可以写除 ()
之外的任何约束”。所以一般来说,编程语言设计者需要一个很好的理由来禁止像空约束这样的东西;问为什么 允许 这样的事情经常得到无聊的答案:“为什么不呢?”