Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a default implementation of typeclass method to omit an argument

I want to be able to define a (mulit-parameter-) typeclass instance whose implementation of the class's method ignores one of its arguments. This can be easily done as follows.

instance MyType MyData () where
    specific _ a = f a

As I'm using this pattern in several places, I tried to generalize it by adding a specialized class method and adequate default implementations. I came up with the following.

{-# LANGUAGE MultiParamTypeClasses, AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

class MyType a b where
    specific :: b -> a -> a
    specific = const dontCare
    dontCare :: a -> a
    dontCare = specific (undefined :: b)
    {-# MINIMAL specific | dontCare #-}

This however yields the error Could not deduce (MyType a b0) arising from a use of ‘dontCare’ [..] The type variable ‘b0’ is ambiguous. I don't see why the latter should be the case with the type variable b being scoped from the class signature to the method declaration. Can you help me understand the exact problem that arises here?

Is there another reasonable way to achieve what I intended, namely to allow such trimmed instances in a generic way?

like image 918
Gerhood Avatar asked Jun 08 '20 21:06

Gerhood


People also ask

Why can't default methods be used in an interface?

The abstract class can have a state, and its methods can access the implementation's state. Although default methods are allowed in an interface, they can't access the implementation's state. Any logic we write in the default method should be with respect to other methods of the interface — those methods will be independent of the object's state.

What is a default method in Java?

Default methods − Unlike other abstract methods these are the methods that can have a default implementation. If you have a default method in an interface, it is not mandatory to override (provide body) it in the classes that are already implementing this interface.

Are default interfaces explicit or implicit?

With the interface default implementation that is not changing, they will be explicit.

What is the default method of foreach in Java 8?

Java 8 introduces default method so that List/Collection interface can have a default implementation of forEach method, and the class implementing these interfaces need not implement the same. public interface vehicle { default void print () { System.out.println ("I am a vehicle!"); } }


1 Answers

The problem is in the default definition of specific. Let's zoom out for a second and see what types your methods are actually given, based on your type signatures.

specific :: forall a b. MyType a b => b -> a -> a
dontCare :: forall a b. MyType a b => a -> a

In the default definition of specific, you use dontCare at type a -> a. So GHC infers that the first type argument to dontCare is a. But nothing constrains its second type argument, so GHC has no way to select the correct instance dictionary to use for it. This is why you ended up needing AllowAmbiguousTypes to get GHC to accept your type signature for dontCare. The reason these "ambiguous" types are useful in modern GHC is that we have TypeApplications to allow us to fix them. This definition works just fine:

class MyType a b where
    specific :: b -> a -> a
    specific = const (dontCare @_ @b)
    dontCare :: a -> a
    dontCare = specific (undefined :: b)
    {-# MINIMAL specific | dontCare #-}

The type application specifies that the second argument is b. You could fill in a for the first argument, but GHC can actually figure that one out just fine.

like image 141
dfeuer Avatar answered Oct 25 '22 18:10

dfeuer