这是我提出的问题的具体版本here https://stackoverflow.com/questions/60072003/reordering-type-parameters-in-haskell。我有一个算法,可以产生一些输出,并且可以产生一些辅助信息,在给定情况下我可能会或可能不关心这些信息(下面我将此辅助信息称为“注释”)。我用欧几里得算法来举例说明:
class AnnotatedGcd m where
actually :: (Integral a) => a -> m a
update :: (Integral a) => a -> m a -> m a
newtype GcdOnly a = Gcd a deriving Show
instance AnnotatedGcd GcdOnly where
actually = Gcd
update q = id
newtype GcdWithSteps a = GcdS (a, Int) deriving Show
instance AnnotatedGcd GcdWithSteps where
actually x = GcdS (x, 0)
update q (GcdS (x, s)) = GcdS (x, s+1)
newtype GcdExtended a = GcdE (a, a, a) deriving Show
instance AnnotatedGcd GcdExtended where
actually x = GcdE (x, 1, 0)
update q (GcdE (x, k, l)) = (GcdE (x, l, k - q*l))
gcd :: (Integral a, AnnotatedGcd m) => a -> a -> m a
gcd a b = if b == 0 then actually a else update q ((Main.gcd) b r)
where q = a `div` b
r = a `rem` b
功能gcd a b
返回一个AnnotatedGcd
在这个例子中可以是GcdOnly
这只是 gcd g,或者也可能是GcdWithSteps
另外计算它走了多少步,或者可以是GcdExtended
还提供系数 k,l 使得 g = k * a + l * b:
*Main> (Main.gcd 253 83) :: (GcdOnly Int)
Gcd 1
*Main> (Main.gcd 253 83) :: (GcdWithSteps Int)
GcdS (1,4)
*Main> (Main.gcd 253 83) :: (GcdExtended Int)
GcdE (1,21,-64)
现在如果我想计算步数怎么办and得到系数?而不是继续写越来越多的实例AnnotatedGcd
我想要有注释的概念,并说注释的元组再次是注释:
class GcdAnnotation a where
emptyAnnotation :: a
annotate :: Integral b => b -> a -> a
instance (GcdAnnotation a, GcdAnnotation b) => GcdAnnotation (a,b) where
emptyAnnotation = (emptyAnnotation, emptyAnnotation)
annotate q (x, y) = (annotate q x, annotate q y)
instance (GcdAnnotation a, GcdAnnotation b, GcdAnnotation c) => GcdAnnotation (a,b,c) where
emptyAnnotation = (emptyAnnotation, emptyAnnotation, emptyAnnotation)
annotate q (x, y, z) = (annotate q x, annotate q y, annotate q z)
instance GcdAnnotation b => AnnotatedGcd (,b) where
actually x = (x, emptyAnnotation)
update q (g, x) = (g, annotate q x)
除了最后一个实例声明之外,这是有效的 Haskell 代码。问题是:如何才能真正做出这样的声明?
一般版本是:给定两个参数类Class a
and Class b
如何转换元组(a,b)
到两个类的实例中(一个与第一个参数相关,一个与第二个参数相关)。