О, хорошее горе .
Да, возможно, у вас ошибка типа. Библиотека здесь использует UndecidableInstances
для выполнения какой-то причудливой логики на уровне типов, где «причудливое» означает «заставит средство проверки типов войти в бесконечный цикл, если вам не повезло». Вы можете догадаться, насколько вам повезло в данный момент. К сожалению, хотя результирующая вопиющая ошибка сообщает нам , где происходит цикл, он не настолько информативен, как хотелось бы о , почему .
Также обратите внимание, что совершенно разумно не понимать, почему вы получили эту ошибку. Это грязно и запутанно. Далее следует грубое объяснение и мои мысли о сужении причины:
Прежде всего, цикл явно происходит при вычислении FunctionArgs
. Рассмотрим определение класса:
class FunctionArgs f g r | f -> g r, g r -> f where
...
Часть после |
- это функциональных зависимостей , указывающих, что одни параметры однозначно определяют другие. В этом случае f
и комбинация g
и r
определяют друг друга, так что это своего рода двусторонняя функция, которая может вычислять в любом направлении.
Ваша функция mSum :: CodeGenModule (Function Sig)
, объединение с сигнатурой createNamedFunction
дает нам Sig
в качестве параметра f
, с r
и g
в настоящее время неизвестными. Синоним типа Sig
расширяется до Int32 -> Ptr Int32 -> Function Acc-> IO Int32
. Теперь мы можем взглянуть на список экземпляров для FunctionArgs
и посмотреть, что это нам дает.
Приоритет оператора дает нам крайнюю левую стрелку функции как конструктор внешнего типа для Sig
, поэтому мы находим соответствующий экземпляр: FunctionArgs b b' r => FunctionArgs (a -> b) (Value a -> b') r
. Мы можем подставить типы, при необходимости выполнить объединение и повторить:
FunctionArgs (Ptr Int32 -> Function Acc-> IO Int32) b' r => FunctionArgs (Int32 -> (Ptr Int32 -> Function Acc-> IO Int32)) (Value Int32 -> b') r
FunctionArgs (Function Acc-> IO Int32) b' r => FunctionArgs (Ptr Int32 -> (Function Acc-> IO Int32)) (Value (Ptr Int32) -> b') r
Вы должны быть в состоянии сопоставить эти шаги с трассировкой стека в полученной ошибке. Одна интересная вещь заключается в том, что в трассировке стека есть дополнительные шаги, причина которых я не знаю - как-то связано с тем, как они заполняют типы на основе функциональных зависимостей, я полагаю. Похоже, что сначала он выбирает экземпляр на основе конструктора типа (->)
, затем заполняет конструкторы типа Value a -> b
для параметра g
, затем выполняет рекурсивный шаг (в контексте) перед возвратом, чтобы объединить остальные типы .
Теперь мы знаем, что в этот момент происходит что-то неправильное; это может быть выведено из универсального принципа переполнения стека, который заключается в том, что проблема где-то перед тем, как трассировка стека начинает повторять один и тот же шаблон снова и снова.
Для следующего сокращения, f
создается с Function Acc-> IO Int32
, в то время как g
и r
пока остаются неопределенными (хотя мы знаем, что они должны быть однозначно определены f
). Также, вероятно, будет хорошей идеей взглянуть на определение Function
на этом этапе: type Function a = Value (Ptr a)
FunctionArgs (Value (Ptr Acc) -> IO Int32) g r
Опять мы выбираем экземпляр со стрелкой функции: FunctionArgs b b' r => FunctionArgs (a -> b) (Value a -> b') r
... и здесь происходит что-то подозрительное, потому что, если сравнить приведенную выше трассировку стека, для параметра g
будет показано (Function (...) -> ...)
. Это технически соответствует приведенному выше определению Function
, потому что мы ожидали Value
, который является самым внешним конструктором синонима типа Function
. К сожалению, у нас также есть (Function (...) -> ...)
для параметра f
, что является непоследовательным, поскольку параметр g
должен иметь дополнительный конструктор Value
.
После заполнения неправильной структуры, он переходит к застреванию на шаге, где он должен заполнить оставшиеся переменные типа; похоже, что двунаправленные функциональные зависимости заставляют его подпрыгивать назад и вперед, неоднократно пытаясь объединить a
с Value a
. Итак, с расширением синонимов типа мы получаем:
FunctionArgs (Value (Ptr Acc) -> b) (Value (Ptr Acc) -> b') r
FunctionArgs (Ptr Acc -> b) (Value (Value (Ptr Acc)) -> b') r
... до бесконечности.
На данный момент я действительно не уверен, что привело бы к этому конфликту.Я ожидаю, что результатом будет последовательный выбор экземпляра FunctionArgs (Value (Ptr Acc) -> b) (Value (Value (Ptr Acc)) -> b') r
, и я не могу точно сказать, почему он застрял таким, какой он есть.
Редактировать : Подождите, я 'Я глупый.Сначала я неправильно прочитал ваш код - он довольно явно получает некоторую часть неправильного типа из выведенного типа лямбда-выражения, которое, на мой взгляд, было более полиморфным, чем на самом деле.В частности, параметр fn
задается в качестве аргумента функции call :: CallArgs f g => Function f -> g
, которая создает описанный выше несовместимый тип.Я до сих пор не знаю, почему он входит в бесконечный цикл, но, по крайней мере, это объясняет конфликт.
При условии, что предполагаемый тип из-за call
является правильным, параметр g
должен иметьтип Value Int32 -> Value (Ptr Int32) -> Function Acc-> CodeGenFunction Int32 ()
, что означает f
, должно быть Int32 -> Ptr Int32 -> Ptr Acc-> IO Int32
, в отличие от вашего типа Sig
.
. Это то, что, если вы посмотрите на класс IsFunction
, который такжеПрименительно к f
он ожидает, что аргументы функции будут примитивными типами, такими как Int32
или Ptr
s, к примитивным типам, но не Value
, то есть то, к чему Function
расширяется.
Так что послевсе это, я думаю, что ваша проблема только в том, что ваш тип Sig
немного неправильный.
... привет.Хорошо, тогда.