防止相互递归的默认方法在运行时循环



我目前有一个如下结构:

class F a where
f :: ...
default f :: (G a...) => ...
f = (some definition in terms of g)
class F a => G a where
g :: ...
default g :: (C a...) => ...
g = (some definition in terms of f)

希望我能用通俗易懂的英语,永远用g来写f。我有时可以根据f来写g,即当a满足C约束时。

我在这里看到的问题是,如果有人写了一个满足C T的类型T

instance F T
instance G T

这将在运行时编译和循环。虽然两个默认定义都是正确的,但重要的是至少定义一个。

如果fg在同一个类中,我可以用MINIMAL杂注来解决这个问题,但在本例中它们不是。

同样,将fg放在同一类中似乎是不可能的,因为虽然对于g的每个定义都有f的定义,但对于f的每个定义却没有g的定义。一种可能性是将g移动到F中,但也对其施加C a约束,但这将阻止我用不满足C a的任何a的非默认定义来定义g

有什么方法可以重组这件事来解决我面临的困境吗?

我之前的答案是无稽之谈,所以这里有一个(希望)更好的答案。这至少会在编译时给您一个警告。技巧是如果两个a都有FG的实例,则在类G中实现f'g,但如果G的实例不可能,则仅实现f

{-# LANGUAGE DefaultSignatures #-}
class C a where
class F a where
f :: a -> a
default f :: (G a) => a -> a
f = g
class F a => G a where
{-# MINIMAL (f'|g) #-}
f' :: a -> a
f' = f
g :: a -> a
default g :: (C a) => a -> a
g = f'
instance F Integer where
f = succ
instance F Int
instance G Int where
g = succ
instance C Float
instance F Float
instance G Float where
f' = succ
-- This will give a compile time warning, but will still
-- loop at runtime:
instance C Double
instance F Double
instance G Double

相关内容

最新更新