
Daniel Peebles wrote:
I've been playing with multiparameter typeclasses recently and have written a few "uncallable methods" in the process. For example, in
class Moo a b where moo :: a -> a
Another solution would be to artificially force moo to take a "dummy" b so that the compiler can figure out which instance you meant. That's what I've been doing in the mean time, but wouldn't it be simpler and less hackish to add a some form of "instance annotation", like a type annotation, that would make it possible to specify what instance you wanted when it's ambiguous?
Syntax aside, dummy arguments have a disadvantage when it comes to optimizing code, because the compiler doesn't know that the dummy argument is unused in the function; indeed you could define instances where the dummy argument is used. For this reason it's technically better to use a newtype instead: newtype Lambda t a = Lambda { unLambda :: a } and, say, class Moo a b where moo :: Lambda b (a -> a) Note that we can convert between functions taking dummy arguments and such "lambda" types easily: lambdaToDummy :: Lambda t a -> t -> a lambdaToDummy a _ = unLambda a dummyToLambda :: (t -> a) -> Lambda t a dummyToLambda f = Lambda (f undefined) In fact, lambdaToDummy makes a great infix operator: (@@) :: Lambda t a -> t -> a (@@) = lambdaToDummy infixl 1 @@ Now we can write moo @@ (undefined :: x) where with dummy arguments we would write moo (undefined :: x) . The compiler will inline (@@) and lambdaToDummy (they're both small) and produce unLambda moo , that is, the plain value of type a -> a that we wanted to use in the first place. All this happens after type checking and fixing the instance of Moo to use. Bertram