You are correct, if you implement an arrow like this:
instance Arrow MyType where
arr f = ...
and then try to use first
or (***)
, you will get an infinite loop since the implementations refer to each other non-productively. However, defining the default methods this way allows you to instantiate Arrow
as either
instance Arrow MyType where
arr f = ...
first t = ...
or
instance Arrow MyType where
arr f = ...
t *** t' = ...
whichever is more convenient/efficient (depending on what you care about), and the missing method will be defined automatically in terms of the one you did specify.
If we try to instantiate Arrow
without giving an implementation of first
or (***)
, we will get the following warning:
warning: [-Wmissing-methods]
• No explicit implementation for
either ‘first’ or ‘***’
• In the instance declaration for ‘Arrow MyType’
This is because the source comes with a MINIMAL
pragma:
{-# MINIMAL arr, (first | (***)) #-}
which tells the compiler that, even though defaults are supplied, an instance is not complete unless it specifies arr
and one of first
or (***)
. So the requirement is encoded and checked.
As for your comment:
Not necesseraly I can leave the default and then the recursion will take place by definition. specific implementation are not the question here...
You can't use methods of a typeclass without an instance. It's seldom even possible to try, because the types of methods always refer to the type, e.g.
class Arrow k where
first :: k a b -> k (a,c) (b,c)
...
When you use first
, you have to have a specific k
in mind in order to use the result, e.g.
print $ first (arr id) (1,2) -- using it at k ~ (->)
print =<< runKleisli (first (arr id)) (1,2) -- using it at Kleisli IO
At some point the type constraints of the program will pin k
down to be something concrete, and that is the instance that is used. You can't use a class without an instance.
(And even in the cases where things line up in such a way that a specific instance is not determined, the classic example being
show . read :: String -> String
The compiler will just yell at you
• Ambiguous type variable ‘a0’ arising from a use of ‘read’
prevents the constraint ‘(Read a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
No infinite recursion if the program doesn't compile!)