Необходимость подписи внешнего типа вызвана ограничением мономорфизма .
. Отдача для этого - левая часть вашего определения.
dummyTest = ...
Поскольку это определение не имеет аргументов, компилятор попытается сделать это определение мономорфным.Добавление сигнатуры типа отменяет это поведение.
Однако, как вы указали, этого недостаточно.По какой-то причине компилятор не может определить тип внутреннего выражения.Зачем?Давайте разберемся.Пора играть в механизм вывода типов!
Давайте начнем с внешнего типа и попробуем определить тип внутреннего выражения.
dummyTest :: forall m. Monad m => m ()
dummyTest = def "dummy" (return ())
Тип def
равен FcnDef β m => String -> β
, но здесь мы применили def
к двум аргументам.Это говорит нам о том, что β
должен быть типом функции.Давайте назовем это x -> y
.
Затем мы можем легко сделать вывод, что y
должно быть равно m ()
, чтобы удовлетворить внешний тип.Кроме того, тип аргумента return ()
равен Monad m1 => m1 ()
, поэтому мы можем сделать вывод, что искомый тип def
должен иметь тип FcnDef (m1 () -> m ()) m0 => def :: String -> m1 () -> m ()
.
Далее мы продолжимискать экземпляр для использования.Единственный доступный экземпляр - , не достаточно универсальный , так как он требует, чтобы m1
и m
были одинаковыми.Поэтому мы громко жалуемся на следующее сообщение:
Could not deduce (FcnDef (m1 () -> m ()) m0)
arising from a use of `def'
from the context (Monad m)
bound by the type signature for dummyTest :: Monad m => m ()
at FcnDef.hs:10:1-51
Possible fix:
add (FcnDef (m1 () -> m ()) m0) to the context of
the type signature for dummyTest :: Monad m => m ()
or add an instance declaration for (FcnDef (m1 () -> m ()) m0)
In the expression: def "dummy" ((return ()))
In an equation for `dummyTest':
dummyTest = def "dummy" ((return ()))
Ключевым моментом здесь является то, что тот факт, что конкретный случай лежал без дела, не влиял на наш выбор, когда мы пытались вывеститип.
Таким образом, мы застряли с необходимостью указать это ограничение вручную, используя переменные типа scoped, или мы можем закодировать его в классе типов.
class Monad m => FcnDef β m | β -> m where
def :: String -> β -> β
instance Monad m => FcnDef (m α) m where
def s body = body
-- dummyTest :: forall m. Monad m => m ()
dummyTest = def "dummy" (return ())
Теперь механизм вывода типов может легкоопределите, что монада return
должна совпадать с монадой в результате, и, используя NoMonomorphismRestriction
, мы также можем удалить сигнатуру внешнего типа.
Конечно, я не уверен на 100%, чтоВы пытаетесь достичь этого, поэтому вам придется судить, имеет ли это смысл в контексте того, что вы пытаетесь сделать.