Всегда ли Хаскелл знает, какой «возврат» будет звонить? - PullRequest
8 голосов
/ 20 октября 2011

Я определяю экземпляр монады следующим образом:

data Something = Something a

instance Monad Something where
    return a = Something a        --Wraps a in 'Something', correct?
    m >>= f = do
        var <- m
        return $ f var            --I want this to pass var to f, then wrap the result
                                  --back up in the 'Something' monad with the return I
                                  --Just defined

Вопросы ->

1: Есть ли явные ошибки / неправильные представления о том, что я делаю?

2: Знает ли Хаскелл, чтобы я позвонил в ответ, который я определил выше, с m >>= f

3: Если я по какой-то причине определю другую функцию

f :: Something a -> Something b
f x = do
    var <- x
    return $ doMagicTo x

Будет ли return вызывать возврат, который я определил в экземпляре монады, и обернуть x в Something?

Ответы [ 3 ]

16 голосов
/ 21 октября 2011

Здесь есть несколько больших проблем.

Во-первых, экземпляр 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).

8 голосов
/ 21 октября 2011

Основная проблема в том, что ваше определение >>= является круглым.

Синтаксис do в Haskell - это синтетический сахар для цепочек >>= и >>, поэтому ваше определение

m >>= f = do
    var <- m
    return $ f var         

Desugars до

m >>= f = 
    m >>= \var -> 
    return $ f var

Итак, вы определяете m >>= f как m >>= \..., который является круглым.

Что вам нужно сделать с >>=, так это определить, как извлечь значение из m для передачи в f. Кроме того, ваш f должен возвращать монадическое значение, поэтому использование return здесь неправильно (это ближе к тому, как вы определяете fmap).

Определение >>= для Something может быть:

(Something a) >>= f = f a

Это Монада Идентичности - о ней много написано - это хорошая отправная точка для понимания того, как работают монады.

2 голосов
/ 21 октября 2011
  1. Закрыть.return здесь избыточен, и вам нужно добавить параметр типа в ваш конструктор типа Something. Редактировать: этот код по-прежнему неверен.Определение >>= является круглым.См. Другие ответы для получения дополнительной информации.

    data Something a = Something a
    
    instance Monad Something where
        return a = Something a
        m >>= f = do
            var <- m
            f var
    
  2. Поскольку ваше определение для >>= относится к instance Monad Something where, тип >>= равен Something a -> (a -> Something b) -> Something b,Таким образом, он может сказать, что f var должен иметь тип Something b.Гугл термин здесь "вывод типа"

  3. Да.Опять же, это вывод типа.Если компилятор не может определить тип, который вы хотите, он скажет вам об этом.Но обычно это может.

...