Подтвердить что ты не робот

Как извлечь значение из монадического действия

Есть ли встроенная функция с сигнатурой :: (Monad m) => m a -> a?

Hoogle говорит, что такой функции нет.

Можете ли вы объяснить, почему?

4b9b3361

Ответ 1

Монада выполняет только две функции:

return :: Monad m => a -> m a
(>>=) :: Monad m => m a -> (a -> m b) -> m b

Оба из них возвращают что-то типа m a, поэтому нет возможности каким-либо образом объединить их, чтобы получить функцию типа Monad m => m a -> a. Для этого вам потребуется больше, чем эти две функции, поэтому вам нужно знать больше о m, чем о том, что это монада.

Например, монада Identity имеет runIdentity :: Identity a -> a, а несколько монад имеют схожие функции, но нет возможности ее предоставить в общих чертах. Фактически, невозможность "убежать" от монады необходима для монад вроде IO.

Ответ 2

Вероятно, есть лучший ответ, но один из способов понять, почему вы не можете иметь тип (Monad m) => m a -> a, - это рассмотреть пустую монаду:

data Null a = Null

instance Monad Null where
    return a = Null
    ma >>= f = Null

Теперь (Monad m) => m a -> a означает Null a -> a, то есть получить что-то из ничего. Вы не можете этого сделать.

Ответ 3

Это не существует, потому что Monad является шаблоном для композиции, а не шаблоном для разложения. Вы всегда можете добавить больше деталей вместе с интерфейсом, который он определяет. Он ничего не говорит о том, чтобы что-то обойти.

Запрашивать, почему вы не можете что-то предпринять, так же, как спросить, почему интерфейс Java Iterator не содержит метода добавления элементов в то, что он итерирует. Это просто не тот интерфейс Iterator.

И ваши аргументы о конкретных типах, имеющих вид функции извлечения, следуют точно так же. Некоторая конкретная реализация Iterator может иметь функцию add. Но так как это не то, для чего Iterator, наличие этого метода в каком-то конкретном экземпляре не имеет значения.

И наличие fromJust столь же неуместно. Это не часть поведения Monad, предназначенная для описания. Другие дали множество примеров типов, для которых нет работы extract. Но эти типы по-прежнему поддерживают предполагаемую семантику Monad. Это важно. Это означает, что Monad является более общим интерфейсом, чем вы даете ему кредит.

Ответ 4

Предположим, что существует такая функция:

extract :: Monad m => m a -> a

Теперь вы можете написать "функцию" следующим образом:

appendLine :: String -> String
appendLine str = str ++ extract getLine

Если функция extract не гарантировалась никогда не прерываться, это нарушит ссылочную прозрачность, потому что результат appendLine "foo" будет (а) зависеть от чего-то другого, кроме "foo", (b) оценивать разные значения при оценке в разных контекстах.

Или в более простых словах, если бы была действительно полезная операция extract, Haskell не была бы чисто функциональной.

Ответ 5

Есть ли встроенная функция с сигнатурой :: (Monad m) => m a -> a?

Если Hoogle говорит, что нет... тогда, вероятно, нет, предполагая, что ваше определение "встроенный" есть "в базовых библиотеках".

Hoogle говорит, что такой функции нет. Можете ли вы объяснить, почему?

Это легко, потому что Hoogle не нашел никакой функции в базовых библиотеках, соответствующих этой сигнатуре!

Более серьезно, я полагаю, вы просили монадическое объяснение. Проблемы - это безопасность и смысл. (См. Также мои предыдущие мысли о magicMonadUnwrap :: Monad m => m a -> a)

Предположим, что я говорю вам, что у меня есть значение, имеющее тип [Int]. Поскольку мы знаем, что [] является монадой, это похоже на то, что я имею значение, имеющее тип Monad m => m Int. Поэтому предположим, что вы хотите получить Int из этого [Int]. Ну, а Int ты хочешь? Первый? Последний? Что, если значение, о котором я вам говорил, на самом деле является пустым списком? В этом случае нет даже Int, чтобы дать вам! Поэтому для списков небезопасно пытаться извлечь одно значение волей-неволей. Даже если это безопасно (непустой список), вам нужна функция, специфичная для списка (например, head), чтобы уточнить, что вы имеете в виду, желая f :: [Int] -> Int. Надеюсь, вы можете понять, что значение Monad m => m a -> a просто не определено. Он может содержать несколько значений для одной монады, или это может означать абсолютно ничего для некоторых монадов, а иногда просто просто не безопасно.

Ответ 6

Потому что это может быть бессмысленным (на самом деле, не имеет смысла во многих случаях).

Например, я могу определить Parser Monad следующим образом:

data Parser a = Parser (String ->[(a, String)])

Теперь нет абсолютно разумного способа по умолчанию, чтобы получить String из Parser String. На самом деле, вообще нет способа получить строку из этого только с помощью Монады.

Ответ 7

Ну, технически есть unsafePerformIO для монады IO.

Но, как предполагает само название, эта функция зла, и вы должны использовать ее только в том случае, если вы действительно знаете, что делаете (и если вам нужно спросить, знаете ли вы, или нет, то вы этого не сделаете)