问题
This is a concrete version of my question asked here. I have an algorithm that produces some output and can produce some auxiliary information, which I may or may not care about in a given case (I call this auxiliary information "annotation" below). I am using Euclid's algorithm for an illustration:
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
The function gcd a b
returns an AnnotatedGcd
which in the example could be GcdOnly
which is just the gcd g, or it could be GcdWithSteps
in addition counting how many steps it took, or it could be GcdExtended
also providing coefficients k,l such that 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)
Now what if I want to count the number of steps and get the coefficients? Rather than keeping writing more and more instances of AnnotatedGcd
I would want to have the notion of an annotation and say that tuples of annotations are annotations again:
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)
This is valid Haskell code except for the last instance declaration. The question is: how could one actually make such a declaration?
The general version would be: given two parametric classes Class a
and Class b
how can one turn tuples (a,b)
into instances of both classes (one with respect to the first parameter, one with respect to the second).
来源:https://stackoverflow.com/questions/60079676/partially-evaluated-type-in-classes