Языковой отчет не допускает экземпляры формы instance Class a where...
, поэтому единственный способ избежать FlexibleInstances
(что не так страшно) - использовать оболочку нового типа,
newtype LinearType a = Linear a
liftLin2 :: (a -> b -> c) -> LinearType a -> LinearType b -> LinearType c
liftLin2 op (Linear x) (Linear y) = Linear (op x y)
instance Num a => Linear (LinearType a) where
add = liftLin2 (+)
Тьфу.
Расширение UndecidableInstances
необходимо, потому что ограничение Num a
не меньше, чем заголовок экземпляра (он использует переменные одного типа одинаковое количество раз), поэтому компилятор может:заранее доказать, что проверка типов будет прекращена.Таким образом, вы должны пообещать компилятору, что проверка типов прекратится, чтобы он принял программу (он фактически не зацикливается на GHC, у которого есть стек контекста, который контролирует глубину рекурсии средства проверки типов, поэтому, если проверка типа не делаетЕсли он скоро закончится, компиляция завершится с «превышен стек контекста» - вы можете установить размер с помощью -fcontext-stack=N
).
Это расширение звучит гораздо страшнее, чем есть.По сути, все, что он делает, это говорит компилятору: «Поверьте мне, проверка типов прекратится», поэтому компилятор запустится, не зная точно, что он завершится.
Но чего вы пытаетесь достичь?То, что у вас есть,
instance (Num a) => Linear a where
add = (+)
говорит: «каждый тип является экземпляром Linear, и если вы попытаетесь использовать add для типа, а не экземпляра Num, это ошибка во время компиляции».Это не очень полезно.Вы не можете добавить дополнительные экземпляры для типов, не принадлежащих к Num
, если вы не включите также OverlappingInstances
и, возможно, IncoherentInstances
.И эти расширения страшны, их следует использовать редко и только тогда, когда вы знаете, что делаете.