haskell - 将字典变成约束

标签 haskell ghc

我有课Cyc c r它具有 c m r 形式的数据的功能, 其中 m是幻影类型。例如,

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

我确实有充分的理由不制作 m一个类参数。就本示例而言,主要原因是它减少了函数约束的数量。在我的实际示例中,对这个接口(interface)的一个更迫切的需求是我可以处理不断变化和隐藏的幻像类型,所以这个接口(interface)让我得到一个 Cyc任何幻像类型的约束。

该选择的一个缺点是我无法制作 Num (c m r) Cyc 的父类(super class)约束.我的意图是c m r应该是 Num每当(Cyc c r, Foo m) .当前的解决方案很烦人:我在类 Cyc 中添加了方法
witNum :: (Foo m) => c m r -> Dict (Num (c m r))

哪一种完成同样的事情。现在,当我有一个采用通用 Cyc 的函数时并且需要一个 Num (c m r)约束,我可以写:
foo :: (Cyc c r, Foo m) => c m r -> c m r
foo c = case witNum c of
  Dict -> c*2

当然,我可以添加 Num (c m r)foo 的约束,但我正在尝试减少约束的数量,记得吗? (Cyc c r, Foo m)应该暗示 Num (c m r)约束(我需要 Cyc c rFoo 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

虽然这种方法让我得到了我正在寻找的一切,但它似乎很脆弱。我主要担心的是:
  • 我对 Num 的通用实例头保持警惕在模块 Foo .
  • 如果将任何重叠实例导入 Foo ,我突然需要IncoherentInstancesNumfoo 的约束将实例选择推迟到运行时。

  • 是否有其他方法可以避免使用 Dict在每个需要 Num (c m r) 的函数中避免这些缺点中的任何一个?

    最佳答案

    经过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
    

    最后,我更改了所有使用通用 c m r 的函数。键入以使用 W c m r类型:
    foo :: (Cyc c, EntailCyc c, Foo m, Num r) => W c m r -> W c m r
    foo = (*2)
    

    这里的重点是 foo可能需要许多约束(例如 Eq (W c m r)Show (W c m r) 等),每个约束都需要各自的约束。但是,W c m r 的通用实例对于 Eq , Show等都具有完全的约束 (EntailCyc c, Foo m, Eq/Show/... a) ,所以对 foo 的约束以上是我需要写的唯一约束!

    关于haskell - 将字典变成约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29482576/

    相关文章:

    haskell - 从可执行文件中查找 ghc 版本

    haskell - 如何卸载旧版本的 GHC?

    multithreading - 有没有办法杀死 GHCi session 中的所有 fork 线程而不重新启动它?

    windows - 如何在Windows 10下使用hmatrix正确构建cabal项目?

    haskell - 遍历字符串并用haskell中的子字符串替换单个字符

    unicode - GHC 截断 Unicode 字符输出

    haskell - Template Haskell 中的类型声明

    haskell - 关于Cygwin中Haskell程序的stdout缓冲区的问题

    haskell - 现实世界 Haskell 第 3 章练习 : Binary Tree with 1 value constructor - follow up

    haskell - 将字符串文字限制为仅文本