当前位置: 首页 > 知识库问答 >
问题:

通过蕴涵削弱乙烯基的召回约束

冷翼
2023-03-14

在vinyl库中,有一个recall类型族,让我们询问,对于类型级别列表中的每个类型,部分应用的约束都是真的。例如,我们可以这样写:

myShowFunc :: RecAll f rs Show => Rec f rs -> String

这一切都很可爱。现在,如果我们有约束recallfrsc,其中c是未知的,并且我们知道cx包含dx(借用Ekmett的contstraints包的语言),那么我们如何获得recallfrsd呢?

我问的原因是我正在处理一些函数中的记录,这些函数需要满足几个类型类约束。为此,我使用了exists包中Control.Constraints.Combine模块中的:&:组合子。(注意:如果您安装了其他功能,则该包将无法构建,因为它依赖于contravariant的超旧版本。不过,您可以复制我提到的一个模块。)这样,我就可以在最小化typeclass BroilerPlate的同时获得一些非常漂亮的约束。例如:

RecAll f rs (TypeableKey :&: FromJSON :&: TypeableVal) => Rec f rs -> Value

但是,在这个函数的主体中,我调用了另一个要求较弱约束的函数。它看起来可能是这样的:

RecAll f rs (TypeableKey :&: TypeableVal) => Rec f rs -> Value

GHC看不出第二个声明是从第一个开始的。我以为会是这样的。我看不到的是如何证明它,使它具体化,并帮助GHC出来。到目前为止,我得到的是:

import Data.Constraint

weakenAnd1 :: ((a :&: b) c) :- a c                                                                    
weakenAnd1 = Sub Dict -- not the Dict from vinyl. ekmett's Dict.

weakenAnd2 :: ((a :&: b) c) :- b c                                                                    
weakenAnd2 = Sub Dict
-- The two Proxy args are to stop GHC from complaining about AmbiguousTypes
weakenRecAll :: Proxy f -> Proxy rs -> (a c :- b c) -> (RecAll f rs a :- RecAll f rs b)
weakenRecAll _ _ (Sub Dict) = Sub Dict
Table.hs:76:23:
    Could not deduce (a c) arising from a pattern
    Relevant bindings include
      weakenRecAll :: Proxy f
                      -> Proxy rs -> (a c :- b c) -> RecAll f rs a :- RecAll f rs b
        (bound at Table.hs:76:1)
    In the pattern: Constraint.Dict
    In the pattern: Sub Constraint.Dict
    In an equation for ‘weakenRecAll’:
        weakenRecAll _ _ (Sub Constraint.Dict) = Sub Constraint.Dict

Table.hs:76:46:
    Could not deduce (RecAll f rs b)
      arising from a use of ‘Constraint.Dict’
    from the context (b c)
      bound by a pattern with constructor
                 Constraint.Dict :: forall (a :: Constraint).
                                    (a) =>
                                    Constraint.Dict a,
               in an equation for ‘weakenRecAll’
      at Table.hs:76:23-37
    or from (RecAll f rs a)
      bound by a type expected by the context:
                 (RecAll f rs a) => Constraint.Dict (RecAll f rs b)
      at Table.hs:76:42-60
    Relevant bindings include
      weakenRecAll :: Proxy f
                      -> Proxy rs -> (a c :- b c) -> RecAll f rs a :- RecAll f rs b
        (bound at Table.hs:76:1)
    In the first argument of ‘Sub’, namely ‘Constraint.Dict’
    In the expression: Sub Constraint.Dict
    In an equation for ‘weakenRecAll’:
        weakenRecAll _ _ (Sub Constraint.Dict) = Sub Constraint.Dict

共有1个答案

戚祺
2023-03-14

让我们首先回顾dict(:-)的用法。

ordToEq :: Dict (Ord a) -> Dict (Eq a)
ordToEq Dict = Dict

Dict(Ord a)类型的值Dict进行模式匹配将约束Ord a引入作用域,从该作用域可以推导出Eq a(因为EqOrd的超类),因此Dict::Dict(Eq a)是良好类型的。

ordEntailsEq :: Ord a :- Eq a
ordEntailsEq = Sub Dict

类似地,sub在其参数的持续时间内将其输入约束纳入作用域,从而也允许该Dict::Dict(EQA)具有良好的类型。

然而,虽然dict上的模式匹配将一个约束纳入范围,但sub dict上的模式匹配并没有将一些新的约束转换规则纳入范围。事实上,除非输入约束已经在作用域中,否则您根本无法对sub dict进行模式匹配。

-- Could not deduce (Ord a) arising from a pattern
constZero :: Ord a :- Eq a -> Int
constZero (Sub Dict) = 0

-- okay
constZero' :: Ord a => Ord a :- Eq a -> Int
constZero' (Sub Dict) = 0

这就解释了您的第一个类型错误,“Could not desduce(a c)ering from a pattern”:您尝试对sub dict进行模式匹配,但输入约束a c不在作用域中。

当然,另一个类型错误是,您设法进入范围的约束不足以满足recallfrsb约束。那么,哪些是需要的,哪些是缺失的呢?让我们看看recall的定义。

type family RecAll f rs c :: Constraint where
  RecAll f [] c = ()     
  RecAll f (r : rs) c = (c (f r), RecAll f rs c)
recAllNil :: Dict (RecAll f '[] c)
recAllNil = Dict

recAllCons :: p rs
           -> Dict (c (f r))
           -> Dict (RecAll f rs c)
           -> Dict (RecAll f (r ': rs) c)
recAllCons _ Dict Dict = Dict

接下来,让我们用(:-)而不是dict实现上面的一个版本:

weakenNil :: RecAll f '[] c1 :- RecAll f '[] c2
weakenNil = Sub Dict

weakenCons :: p rs
           -> c1 (f r) :- c2 (f r)
           -> RecAll f rs c1 :- RecAll f rs c2
           -> RecAll f (r ': rs) c1 :- RecAll f (r ': rs) c2
weakenCons _ entailsF entailsR = Sub $ case (entailsF, entailsR) of
    (Sub Dict, Sub Dict) -> Dict

sub将其输入约束recallf(r':rs)c1在其参数的持续时间内纳入作用域,我们已安排该作用域包括函数主体的其余部分。类型族recallf(r':rs)c1的方程扩展到(c1(Fr),recallfrsC1),因此它们也被纳入范围。它们在作用域中的事实允许我们对两个sub dict进行模式匹配,这两个dict将它们各自的约束纳入作用域:C2(fr)recallfrsc2。这两个正是目标约束recallf(r':rs)c2扩展到的,因此我们的dict右侧的类型很好。

要完成weakenallrec的实现,我们需要对rs进行模式匹配,以便确定是将工作委托给weakenil还是weakencons。但是由于rs是类型级别的,我们不能直接对其进行模式匹配。Hasochism论文解释了为了在类型级别的NAT上进行模式匹配,我们需要创建一个包装数据类型NATTYnatty的工作方式是,它的每个构造函数都由相应的nat构造函数索引,因此当我们在值级别对natty构造函数进行模式匹配时,相应的构造函数也在类型级别隐含。我们可以为类型级列表(如rs)定义这样的包装器,但恰好rec frs已经有了与[](:)相对应的构造函数,而且WeakeNallRec的调用方很可能到处都有一个。

weakenRecAll :: Rec f rs
             -> (forall a. c1 a :- c2 a)
             -> RecAll f rs c1 :- RecAll f rs c2
weakenRecAll RNil       entails = weakenNil
weakenRecAll (fx :& rs) entails = weakenCons rs entails
                                $ weakenRecAll rs entails

请注意,Entails的类型必须为。c1 a:-c2 a,而不仅仅是c1 a:-c2 a,因为我们不想声称weakenrecall对调用方选择的任何a都有效,而是我们想要求调用方证明c1 a对每个a都包含c2 a

 类似资料:
  • 我试着理解在Protege中由于使用通用量化而出现的空虚蕴涵的情况。即形式的公理: 它由一个关系和两个高级不相交的类:和组成。 在pet类下,我有三个原始的不相交类:、和。 在person类下面,我有一个定义的类,定义如下: 最后,我创建了一个个人,名为,它不拥有任何宠物。 通用量化:

  • 我正在使用包(用于GHC Haskell)。我有一个类型族,用于确定类型级别列表是否包含一个元素: 这很有效,但有一件事它没有给我的知识是 因为类型族不是“is element of”语句的归纳定义(就像在agda中一样)。我很确定,在GADT可升级到类型级别之前,无法用数据类型表示列表成员身份。 所以,我使用了包来编写这个: 怪异,但它的工作。我可以在蕴含物上进行图案匹配来得到我需要的东西。我想

  • 对于来自Java和其他语言(如和)的程序员来说,关键字是一个非常模糊的东西,因此了解Scala中的关键字非常重要。在Scala中如何使用? 大多数情况下,问题是“在Scala中蕴涵的用法是什么?”是在“如何编写/使用隐式转换?”、“如何使用隐式类型类?”的意义上回答的。等等。 对于新的Scala程序员(至少是我认识的那些人)来说,这样的回答大多数时候给人的印象是实际上只是一个“美化”工具, 只是为

  • 我正在开发一个电子应用程序,对于数据库,我正在使用sqlite3和sequelize。我想在以下两个模型之间建立一对多关系。 > 项目 度量指标可以是升/千克/单位,并且可以在这些指标中的任何一个中测量项目。下面是我如何声明项模型的。 const{Model,DataTypes}=require(“sequelize”);const sequelize=require(“../database/d

  • 问题内容: Java中的脆弱基类问题是什么? 问题答案: 脆弱的基类是继承的普遍问题,它适用于Java和任何其他支持继承的语言。 简而言之,基类是您要从其继承的类,通常将其称为易碎类,因为对此类所做的更改可能会在从其继承的类中产生意外结果。 缓解这种情况的方法很少;但是没有一种简单的方法可以在仍然使用继承的情况下完全避免使用它。您可以通过像Java中一样标记类声明来防止其他类从类继承。 避免这些问

  • 天乙社区是一套基于JAVA技术的网络虚拟社区,采用了Hibernate+Spring+Struts的轻量级J2EE框架,主要特点有:     1、跨平台:天乙社区完全具有java语言跨平台的特性,可以方便的运行在windows、linux、unix等操作系统上。     2、支持多数据库:采用了Hibernate作为数据库持久化的解决方案,支持DB2、PostgreSQL、MySQL、Oracle