it-swarm.com.ru

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

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

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

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

32

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

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.

44
hammar

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

22
Owen

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

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

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

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

13
Carl

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

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

Теперь вы можете написать «функцию», например:

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

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

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

8
Luis Casillas

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

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

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

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

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

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

6
Dan Burton

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

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

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

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

5
Cubic

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

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

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

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

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

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

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

0
hugomg