将字典变成约束

我有一个类Cyc cr ,它具有形式为cmr数据的功能,其中m是幻像类型。 例如,

class Cyc c r where
  cyc :: (Foo m, Foo m') => c m r -> c m' r

我确实有很好的理由不让m类参数。 就本例而言,主要原因是它减少了函数约束的数量。 在我的实际例子中,这个接口更引人注目的需求是我使用变化和隐藏的幻像类型,所以这个接口让我得到任何幻像类型的Cyc约束。

这种选择的一个缺点是我不能让Num (cmr)成为Cyc的超类约束。 我的意图是,每当(Cyc cr, Foo m) cmr应该是Num 。 目前的解决方案非常烦人:我向Cyc类添加了方法

witNum :: (Foo m) => c m r -> Dict (Num (c m r))

哪种完成相同的事情。 现在,当我有一个采用通用Cyc并需要Num (cmr)约束的函数时,我可以这样写:

foo :: (Cyc c r, Foo m) => c m r -> c m r
foo c = case witNum c of
  Dict -> c*2

在课程中,我可以为foo添加一个Num (cmr)约束,但我试图减少约束的数量,请记住? (Cyc cr, Foo m)应该暗示Num (cmr)约束(我需要Cyc crFoo m用于其他目的),所以我不想写出Num约束。

在写这个问题的过程中,我找到了一个更好的方法来完成这个任务,但它有它自己的缺点。

模块Foo:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, ScopedTypeVariables #-}
module Foo where

import Data.Constraint

class Foo m

class Cyc c r where
  cyc :: (Foo m, Foo m') => c m r -> c m' r  
  witNum :: (Foo m) => c m r -> Dict (Num (c m r))

instance (Foo m, Cyc c r) => Num (c m r) where
  a * b = case witNum a of
            Dict -> a * b
  fromInteger a = case witNum (undefined :: c m r) of
                    Dict -> fromInteger a

-- no Num constraint and no Dict, best of both worlds
foo :: (Foo m, Cyc c r) => c m r -> c m r
foo = (*2)

模块栏:

{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, OverlappingInstances #-}
module Bar where

import Foo
import Data.Constraint

data Bar m r = Bar r deriving (Show)

instance (Num r) => Cyc Bar r where
  witNum _ = Dict

instance (Num r, Foo m) => Num (Bar m r) where
  (Bar a) * (Bar b) = Bar $ a*b
  fromInteger = Bar . fromInteger

instance Foo ()  

bar :: Bar () Int
bar = foo 3

虽然这种方法让我找到了我所需要的一切,但它似乎很脆弱。 我主要关心的是:

  • 我对模块Foo Num的通用实例头非常谨慎。
  • 如果有任何重叠的实例被导入到Foo ,我突然需要IncoherentInstancesfoo上的Num约束来将实例选择推迟到运行时。
  • 是否有其他方法可以避免在需要Num (cmr)每个函数中使用Dict ,以避免这些缺点?


    经过6个月的思考,我终于得到了上述newtype评论的答案:添加一个新类型的包装!

    我将Cyc课堂分成两部分:

    class Foo m
    
    class Cyc c where
      cyc :: (Foo m, Foo m') => c m r -> c m' r
    
    class EntailCyc c where
      entailCyc :: Tagged (c m r) ((Foo m, Num r) :- (Num (c m r)))
    

    然后我如上定义我的Cyc实例:

    data Bar m r = ...
    
    instance Cyc Bar where ...
    
    instance (Num r, Foo m) => Num (Bar m r) where ...
    
    instance EntailCyc Bar where
      witNum _ = Dict
    

    然后我定义一个新类型包装器并给它一个通用的Cyc实例:

    newtype W c m r = W (c m r)
    
    instance Cyc (W c m r) where cyc (W a) = W $ cyc a
    
    instance (EntailCyc c, Foo m, Num r) => Num (W c m r) where
      (W a) + (W b) = a + b  witness entailCyc a
    

    最后,我更改了所有使用通用cmr类型的函数以使用W cmr类型:

    foo :: (Cyc c, EntailCyc c, Foo m, Num r) => W c m r -> W c m r
    foo = (*2)
    

    这里的要点是foo可能需要许多约束条件(例如, Eq (W cmr)Show (W cmr)等),每个约束条件都需要各自的约束条件。 然而,对于EqShow等的W cmr的泛型实例都具有完全的约束(EntailCyc c, Foo m, Eq/Show/... a) ,所以上面foo的约束是我需要编写的唯一约束!

    链接地址: http://www.djcxy.com/p/43079.html

    上一篇: Turning a Dict into a constraint

    下一篇: Bundling constraints with fundeps