gpt4 book ai didi

haskell - 我可以在不过分宽容的情况下自动为转换函数生成类型类实例吗?

转载 作者:行者123 更新时间:2023-12-03 23:59:55 26 4
gpt4 key购买 nike

最近,I asked a question about an instance I had created generating a runtime infinite loop ,我得到了一个很好的答案!现在我明白了发生了什么,我有一个新问题:我可以修复我的尝试来实现我最初的目标吗?

让我重申并具体说明我的问题是什么:我想创建一个类型类,用于在我的代码中的一些等效数据类型之间进行转换。我创建的类型类既非常简单又非常通用:它包含一个在任意数据类型之间进行转换的转换函数:

class Convert a b where
convert :: a -> b

但是,此类型类具有特定用途:在具有规范表示的特定值类之间进行转换。因此,有一种特定的数据类型是“规范的”,我想利用这种数据类型的特性来减轻我的类型类的实现者的负担。

data Canonical = ...

class ConvertRep a b where
convertRep :: a -> b

具体来说,考虑两种不同的表示,RepARepB。我可能会合理地定义一些实例来将这些表示与 Canonical 相互转换:

instance ConvertRep RepA Canonical where ...
instance ConvertRep Canonical RepA where ...

instance ConvertRep RepB Canonical where ...
instance ConvertRep Canonical RepB where ...

现在,这立即有用,因为我现在可以将 convertRep 用于两种表示形式,但它主要用作重载 convertRep 名称的一种方式。我想做一些更强大的事情:毕竟,我现在已经有效地定义了以下类型的四个函数:

RepA      -> Canonical
Canonical -> RepA
RepB -> Canonical
Canonical -> RepB

在我看来,根据这些定义,我还应该能够生成以下类型的两个函数:

RepA -> RepB
RepB -> RepA

基本上,由于两种数据类型都可以与规范表示相互转换,所以我想自动直接生成相互之间的转换函数。正如我在前面提到的问题中提到的,我的尝试是这样的:

instance (ConvertRep a Canonical, ConvertRep Canonical b) => ConvertRep a b where
convertRep = convertRep . (convertRep :: a -> Canonical)

不幸的是,这个实例过于宽松,它 causes the generated code to recurse when provided two types I think should be invalid —我还没有定义规范转换的类型。


为了尝试解决这个问题,我考虑了另一种更简单的方法。我想我可以使用两个类型类而不是一个来防止这个递归问题:

class ToCanonical a where
toCanonical :: a -> Canonical

class FromCanonical a where
fromCanonical :: Canonical -> a

现在可以定义一个新函数来执行我最初感兴趣的 convertRep 转换:

convertRep :: (ToCanonical a, FromCanonical b) => a -> b
convertRep = fromCanonical . toCanonical

但是,这是以灵 active 为代价的:不再可能在两个非规范表示之间创建直接转换实例。

例如,也许我知道RepARepB 会非常频繁地互换使用,因此它们之间会进行相当多的转换。因此,转换为/自 Canonical 的额外步骤是浪费时间。我想有选择地定义一个直接转换实例:

instance ConvertRep RepA RepB where
convertRep = ...

它提供了两种常见类型之间的“快速路径”转换。


总结一下:有没有办法使用 Haskell 的类型系统来实现所有这些目标?

  1. “生成”表示之间的转换,给定在表示和规范形式之间转换的函数。
  2. 可选择在通常转换的实例之间提供“快速路径”。
  3. 拒绝没有明确定义的实例;也就是说,不允许 ConvertRep Canonical Canonical(和类似的)实例被创建无限重复并产生底部。

Haskell 类型系统非常令人印象深刻,但我担心在这种情况下它的实例解析规则不够强大,无法一次完成所有这些目标。

最佳答案

OverlappingInstances可用于回退行为:

data A = A deriving (Show)
data B = B deriving (Show)
data C = C deriving (Show) -- canonical

class Canonical a where
toC :: a -> C
fromC :: C -> a

class Conv a b where
to :: a -> b
from :: b -> a

instance (Canonical a, Canonical b) => Conv a b where
to = fromC . toC
from = fromC . toC

instance {-# overlapping #-} Conv A B where
to _ = B
from _ = A

instance Canonical A where
toC _ = C
fromC _ = A

instance Canonical B where
toC _ = C
fromC _ = B
如果存在直接实例,

Conv 会直接转换,否则会回退到通过 C 进行转换。 overlapping 编译指示表示我们希望覆盖默认实例。或者,我们可以在默认实例上放置一个 overlappable pragma,但这更危险,因为这会让所有其他实例(可能在外部模块中定义)能够静默覆盖。

但是,我认为这种转换方案不是特别有用或很好的做法。重叠实例引入了不同实例在不同模块中解析的风险,并且它们可能通过导入和存在实例最终出现在同一个模块中,可能会造成麻烦。

关于haskell - 我可以在不过分宽容的情况下自动为转换函数生成类型类实例吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37419946/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com