Как извлечь ценность из монадического действия - PullRequest
34 голосов
/ 20 декабря 2011

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

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

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

Ответы [ 8 ]

44 голосов
/ 20 декабря 2011

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

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.

22 голосов
/ 20 декабря 2011

Возможно, есть лучший ответ, чем этот, но один из способов понять, почему вы не можете иметь тип (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т.е. получить что-то из ничего.Вы не можете этого сделать.

14 голосов
/ 20 декабря 2011

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

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

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

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

8 голосов
/ 20 декабря 2011

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

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

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

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

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

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

6 голосов
/ 20 декабря 2011

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

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

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, просто не определено.Он может иметь несколько значений для одной и той же монады или вообще ничего не значить для некоторых монад, а иногда это просто небезопасно.

5 голосов
/ 20 декабря 2011

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

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

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

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

1 голос
/ 24 августа 2018

Существует полезная функция extract и некоторые другие функции, связанные с этим в http://hackage.haskell.org/package/comonad-5.0.4/docs/Control-Comonad.html

Он определен только для некоторых функторов / монад и не обязательно дает вам полный ответ, а скорее дает ответ. Таким образом, будут возможные подклассы comonad, которые дадут вам промежуточные этапы выбора ответа, где вы могли бы его контролировать. Вероятно, связано с возможными подклассами Traversable. Я не знаю, определены ли такие вещи где-либо.

Почему hoogle вообще не отображает эту функцию, потому что пакет comonad не проиндексирован, в противном случае я думаю, что ограничение Monad будет предупреждено, и extract будет в результатах для тех монад с Comonad пример. Возможно, это связано с тем, что парсер hoogle не завершен и в некоторых строках кода происходит сбой.

Мои альтернативные ответы:

  1. вы можете выполнить - возможно, рекурсивный - анализ случая, если вы импортировали конструкторы типа
  2. Вы можете вставить свой код, который будет использовать извлеченные значения, в монаду, используя monad >>= \a -> return $ your code uses a here в качестве альтернативной структуры кода и до тех пор, пока вы можете преобразовать монаду в «IO ()» таким образом, чтобы вы печатали свои выходные данные. сделано. Это не похоже на извлечение, но математика не такая, как в реальном мире.
0 голосов
/ 21 декабря 2011

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

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

...