将字典变成约束 [英] Turning a Dict into a constraint

查看:138
本文介绍了将字典变成约束的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  class Cyc c r其中
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 cr,Foo m)=> c m r  - > c m r 
foo c = case $ c
Dict - > c * 2

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



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

模块Foo:

  { - #LANGUAGE MultiParamTypeClasses,FlexibleInstances,ScopedTypeVariables# - } 
module Foo其中

import Data.Constraint

class Foo m

class Cyc cr 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)其中
a * b =案例a
Dict - > a * b
fromInteger a = case witNum(undefined :: c m r)of
Dict - > fromInteger a

- 不是Num约束并且不是Dict,两者都是最好的
foo ::(Foo m,Cyc c r)=> c m r - > cmr
foo =(* 2)

模块栏:

  { - #LANGUAGE FlexibleInstances,MultiParamTypeClasses,OverlappingInstances# - } 
模块Bar其中

导入Foo
import Data.Constraint

data Bar mr = Bar r derived(Show)

instance(Num r)=> Cyc Bar r其中
witNum _ =字典

实例(Num r,Foo m)=> Num(Bar m r)其中
(Bar a)*(Bar b)= Bar $ a * b
fromInteger = Bar。 fromInteger

instance Foo()

bar :: Bar()Int
bar = foo 3

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


  1. 我对 Num 模块 Foo

  2. 如果有任何重叠的实例被导入到 Foo ,我突然需要 IncoherentInstances foo 上的 Num 将实例选择推迟到运行时。

有没有其他方法可以避免使用 Dict 在每个需要 Num(cmr)的函数中,避免这些缺点之一?

解决方案<经过6个月的思考,我终于得到了上述悬而未决的评论的回答:添加 newtype 包装器!



我将 Cyc 类分成两部分:

  class Foo m 

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

class EntailCyc c其中
entailCyc :: Tagged(cmr)((Foo m,Num r): - (Num(cmr)))

然后我像上面那样定义我的 Cyc 实例。

 数据Bar mr = ... 

实例Cyc Bar其中...

instance (Num r,Foo m)=> Num(Bar mr)where ...

instance EntailCyc Bar其中
witNum _ =字典

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

<$ (w cmr)其中cyc(W a)= W $ cyc a
$ b $($)$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ b实例(EntailCyc c,Foo m,Num r)=> Num(W cmr)其中
(W a)+(W b)= a + b \\\证人entailCyc a

最后,我改变了所有使用通用 cmr 类型的函数来使用 W cmr type:

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

这里的要点是 foo 可能需要许多约束(例如 Eq(W cmr) Show (W cmr)等),每个单独需要自己的约束。但是,对于 Eq 显示 W cmr >等所有的 上面的code> foo 是我需要写的约束限制!


I have a class Cyc c r which has functions for datas of the form c m r, where m is a phantom type. For example,

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

I do have good reasons for not making m a class parameter. For the purposes of this example, the primary reason is that it reduces the number of constraints on functions. In my actual example, a more compelling need for this interface is that I work with changing and hidden phantom types, so this interface lets me get a Cyc constraint for any phantom type.

One downside to that choice is that I can't make Num (c m r) a superclass constraint of Cyc. My intention is that c m r should be a Num whenever (Cyc c r, Foo m). The current solution is very annoying: I added method to class Cyc

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

which sort-of accomplishes the same thing. Now when I have a function that takes a generic Cyc and needs a Num (c m r) constraint, I can write:

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

Of courses I could add a Num (c m r) constraint to foo, but I'm trying to reduce the number of constraints, remember? (Cyc c r, Foo m) is supposed to imply a Num (c m r) constraint (and I need Cyc c r and Foo m for other purposes), so I don't want to have to write out the Num constraint also.

In the process of writing this question, I found a better(?) way to accomplish this, but it has its own drawbacks.

Module 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)

Module Bar:

{-# 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

While this approach gets me everything I'm looking for, it seems fragile. My main concerns are:

  1. I'm wary of the generic instance head for Num in module Foo.
  2. If any overlapping instances are imported into Foo, I suddenly need IncoherentInstances or the Num constraint on foo to defer instance selection to runtime.

Is there an alternative way to avoid using Dict in every function that needs Num (c m r) that avoids either of these downsides?

解决方案

After 6 months of thought, I finally have an answer to my dangling comment above: add a newtype wrapper!

I split the Cyc class in two:

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)))

Then I define my Cyc instance as above:

data Bar m r = ...

instance Cyc Bar where ...

instance (Num r, Foo m) => Num (Bar m r) where ...

instance EntailCyc Bar where
  witNum _ = Dict

Then I define a newtype wrapper and give a generic Cyc instance for it:

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

Finally, I change all functions that used a generic c m r type to use a W c m r type:

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

The point here is that foo might need many constraints (e.g., Eq (W c m r), Show (W c m r), etc) that would each individually require their own constraints. However, the generic instances for W c m r for Eq, Show, etc all have exactly the constraints (EntailCyc c, Foo m, Eq/Show/... a), so the constraints on foo above are the only constraints I need to write!

这篇关于将字典变成约束的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆