Здесь есть несколько больших проблем.
Во-первых, экземпляр Monad
должен иметь kind * -> *
.Это означает, что им нужна хотя бы одна переменная типа , в которой ваша Something
не имеет ни одной.Для сравнения:
-- kind * -> *
Maybe
IO
Either String
-- kind *
Maybe Int
IO ()
Either String Double
Посмотрите, как каждому из Maybe
, IO
и Either String
требуется параметр типа, прежде чем вы сможете их использовать?С Something
нет места для параметра type для заполнения. Поэтому вам нужно изменить свое определение на:
data Something a = Something a
Вторая большая проблема заключается в том, что >>=
в вашем экземпляре Monad неверен,Обычно вы не можете использовать do-notation, потому что это просто вызывает функции Monad
return
и >>=
.Таким образом, вы должны написать его без каких-либо монадических функций, будь то do-нотация или вызов >>=
или return
.
instance Monad Something where
return a = Something a --Wraps a in 'Something'
(Something m) >>= f = f m --unwraps m and applies it to f
Определение >>=
проще, чем вы ожидали.Развернуть m
просто, потому что вам нужно просто сопоставить с шаблоном в конструкторе Something
.Также f :: a -> m b
, так что вам не нужно беспокоиться о том, чтобы обернуть его снова, потому что f
сделает это за вас.
Хотя нет способа развернуть монаду в целом можно развернуть очень много определенных монад.
Имейте в виду, что нет ничего синтаксически неправильного в использовании do-notation или >>=
в объявлении экземпляра монады.Проблема в том, что >>=
определяется рекурсивно, поэтому программа переходит в бесконечный цикл, когда вы пытаетесь его использовать.
(NB Something
, как здесь определено, - монада Identity )
По третьему вопросу: да, будет вызвана функция return
, определенная в экземпляре Monad.Классы типов отправляются по типу, и, как вы указали, тип должен быть Something b
, компилятор автоматически использует экземпляр Monad для Something
.(Я думаю, что вы имели в виду последнюю строку, чтобы быть doMagicTo var
).