5

我想我是通过约束的“不寻常形式”来理解它的。只是检查 ...

该部分中提供的约束(第二个)的示例似乎都涉及数据构造函数中的 GADT/existentials(?)有了这里的答案,我不确定是否涉及存在(在 Vinyl 库中),但是for的类型比传递给它rhead的参数具有更具体的类型。f并且 tbhPatternSynonym似乎是一条红鲱鱼:OnlyRecord不出现在frhs 上的论点。

所需的约束(两个中的第一个,或者如果有一个,则只有一个)似乎提供与现在已弃用的相同功能DatatypeContexts(?)

data NoCSet a  where                      -- no constraints on the datatype
  NilSet_  :: NoCSet a
  ConsSet_ :: a -> NoCSet a -> NoCSet a
         deriving (Eq, Show, Read)

pattern ConsSet :: (Eq a) => ()     => a -> NoCSet a -> NoCSet a
                -- Req'd  => Prov'd => type;         Prov'd is empty, so omittable
pattern ConsSet x xs = ConsSet_ x xs

pattern NilSet :: (Eq a) => () => NoCSet a
pattern NilSet  = NilSet_

ccUnit = ConsSet () NilSet                     -- accepted
-- ccid   = ConsSet id NilSet                  -- rejected no instance Eq (a -> a)
-- ncid   = NilSet :: NoCSet (a -> a)          --   ditto

有了额外的优势,我可以在NilSet没有参数的情况下对模式设置必需的约束,甚至不允许构建具有不可接受类型的空集。DatatypeContexts对于这样的构造函数,只需忽略约束。

编辑/重点问题:(回复@Noughtmare 评论)

pattern ConsSet上面定义的和这里的构造函数之间是否有明显的区别DCConsSet

data (Eq a) => DCSet a  =          -- constraint on the datatype
    DCNilSet
  | DCConsSet a (DCSet a)
         deriving (Eq, Show, Read)

我的意思是“差异”,而不是一个模式,一个是构造函数。我已经尝试在构建和匹配值中使用它们;我得到同样的行为。

4

1 回答 1

0

不,通常,必需的约束不会简单地提供与数据类型上下文相同的功能。数据类型上下文引入了构造或破坏值所需的约束,除了创建数据类型的程序员想要强制该数据类型的用户满足该约束之外。它们已被弃用,因为现在认为需要对不需要约束的操作(数据类型构造或销毁)进行约束是不好的做法。相反,应将约束移到更靠近“使用站点”的位置。当以需要约束的方式使用数据类型时,这就是约束应该出现的地方。(听起来你对这个推理很熟悉。)

相反,如果使用得当,模式所需的约束会引入构造或破坏值所需的约束,因为该值的构造或破坏使用了提供的实例。

例如,给定:

{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE PatternSynonyms #-}

pattern Length n <- (length -> n)

推断的类型Length是:

pattern Length :: Foldable t => Int -> t a

这里,Foldable t是一个必需的约束,因为它在匹配模式时实际上是需要的。

正如您所注意到的,您可以滥用具有所需约束的模式来模仿已弃用的数据类型上下文扩展,但是虽然您在技术上没有使用已弃用的扩展,但您仍然在做一些被广泛认为是不好的做法。

至于您的最后一个问题,编译器对具有上下文和具有所需约束的模式的数据类型进行了相当不同的处理,因此肯定会有差异。一个明显的区别是DataKinds启用时,DCNilSet是一种类型,而NilSet不是。

如果你真正的问题是“模式所需约束的存在是否代表 Haskell 社区承认数据类型上下文实际上是一个不应该被弃用的有价值的特性?”,我认为答案可能是“不”。

于 2021-06-17T17:46:19.533 回答