Какова логика, позволяющая объединять только те же типы монад с оператором `>>`?

Хотя это нормально связывать IO [[Char]] а также IO () но его нельзя связывать Maybe с IO, Может кто-нибудь привести пример, как это расслабление приведет к плохому дизайну? Почему свобода в полиморфном типе Монады допускается, но не самой Монадой?

2 ответа

Есть много хороших теоретических причин, в том числе "это не то, что Monad есть. "Но давайте на минутку отойдем от этого и просто посмотрим на детали реализации.

Прежде всего - Monad не волшебство Это просто класс стандартного типа. Экземпляры Monad создается только тогда, когда кто-то пишет.

Написание этого экземпляра - это то, что определяет, как (>>) работает. Обычно это делается неявно через определение по умолчанию с точки зрения (>>=), но это только доказательство того, что (>>=) является более общим оператором, и его написание требует принятия тех же решений, что и написание (>>) взял бы.

Если у вас был другой оператор, который работал с более общими типами, вы должны ответить на два вопроса. Во-первых, какими будут типы? Во-вторых, как бы вы пошли о предоставлении реализации? Из вашего вопроса действительно не ясно, какими будут желаемые типы. Я думаю, одно из следующего:

class Poly1 m n where
    (>>) :: m a -> n b -> m b

class Poly2 m n where
    (>>) :: m a -> n b -> n b

class Poly3 m n o | m n -> o where
    (>>) :: m a -> n b -> o b

Все они могут быть реализованы. Но вы теряете два действительно важных фактора для их практического использования.

  1. Вам нужно написать экземпляр для каждой пары типов, которые вы планируете использовать вместе. Это значительно более сложное мероприятие, чем просто экземпляр для каждого типа. Кое-что о n против n^2,
  2. Вы теряете предсказуемость. Что вообще делает операция? Здесь теория и практика пересекаются. Теория позади Monad накладывает множество ограничений на операции. Эти ограничения называются "законами монады". Они за пределами возможности проверить в Хаскеле, но любой Monad экземпляр, который не подчиняется им, считается ошибочным. Конечным результатом является то, что вы можете быстро построить интуицию для того, что Monad операции делают и не делают. Вы можете использовать их, не просматривая детали каждого типа, потому что вы знаете свойства, которым они подчиняются. Ни один из тех возможных классов, которые я предложил, не дает вам никаких подобных заверений. Вы просто не представляете, что они делают.

Я не уверен, что правильно понимаю ваш вопрос, но определенно можно составить Maybe с IO или же [] в том же смысле, что вы можете сочинить IO с [],

Например, если вы проверяете типы в GHCI, используя :t,

getContents >>= return . lines

дает вам IO [String], Если вы добавите

            >>= return . map Text.Read.readMaybe

вы получаете тип IO [Maybe a], который является составом IO, [] а также Maybe, Затем вы можете передать его

            >>= return . Data.Maybe.catMaybes

сгладить IO [a], Затем вы можете передать список проанализированных допустимых входных строк в функцию, которая снова выравнивает его и вычисляет вывод.

Собирая это вместе, программа

import Text.Read (readMaybe)
import Data.Maybe (catMaybes)

main :: IO ()
main = getContents >>=                    -- IO String
       return . lines >>=                 -- IO [String]
       return . map readMaybe >>=         -- IO [Maybe Int]
       return . catMaybes >>=             -- IO [Int]
       return . (sum :: [Int] -> Int) >>= -- IO Int
       print                              -- IO ()

с входом:

1

2
Ignore this!
3

печать 6,

Также было бы возможно работать с IO (Maybe [String]), Maybe [IO String], так далее.

Вы можете сделать это с >> также. Придуманный пример: getContents >> (return . Just) False читает входные данные, игнорирует их и возвращает IO (Maybe Bool),

Другие вопросы по тегам