Проблема в том, что, поскольку decrement
требует только тип a
, невозможно определить, какие типы b
и c
должны быть, даже в точке, где вызывается функция (таким образом, преобразование полиморфизма в определенный тип), поэтому GHC не сможет решить, какой экземпляр использовать.
Например: предположим, у вас есть два экземпляра таблицы: Table Int String Bool
и таблица Int Bool Float
; Вы вызываете свою функцию d
в контексте, где она должна отображать Int на другую Int - проблема в том, что соответствует экземплярам! (a
является Int для обоих).
Обратите внимание, как, если вы сделаете свою функцию равной evalutate
:
d = evalutate
тогда компилятор принимает это. Это связано с тем, что, поскольку evalutate
зависит от трех параметров типа a, b и c, контекст на сайте вызова позволит не неоднозначно разрешать экземпляры - просто проверьте, какие типы для a, b и c находятся в место, где это называется.
Это, конечно, обычно не проблема для классов с одним параметром - нужно решить только один тип; когда мы имеем дело с несколькими параметрами, все усложняется ...
Одним из распространенных решений является использование функциональных зависимостей - сделать b
и c
зависимыми от a
:
class Table a b c | a -> b c where
decrement :: a -> a
evalutate :: a -> b -> c
Это говорит компилятору, что для каждого экземпляра Table для данного типа a
будет один и только один экземпляр (b
и c
будут однозначно определены a
); поэтому он будет знать, что не будет никаких двусмысленностей, и с радостью примет ваше d = decrement
.