Как извлечь ценность из монадического действия
Есть ли встроенная функция с подписью :: (Monad m) => m a -> a
?
Гугл говорит, что такой функции нет.
Вы можете объяснить, почему?
8 ответов
Монада выполняет только две функции:
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
,
Вероятно, есть лучший ответ, чем этот, но один из способов понять, почему вы не можете иметь тип (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
т.е. получить что-то из ничего. Вы не можете сделать это.
Этого не существует, потому что Monad
это образец для композиции, а не образец для разложения. Вы всегда можете собрать больше частей вместе с интерфейсом, который он определяет. Это ничего не говорит о разборе чего-либо.
Спрашивать, почему вы не можете что-то извлечь, это все равно, что спрашивать, почему Java Iterator
Интерфейс не содержит метод для добавления элементов к тому, что он перебирает. Это просто не то, что Iterator
интерфейс для.
И ваши аргументы о конкретных типах, имеющих функцию извлечения, следуют точно так же. Некоторая конкретная реализация Iterator
может иметь add
функция. Но так как это не то, что Iterator
s для, присутствие, что метод в некотором конкретном случае не имеет значения.
И наличие fromJust
так же не имеет значения. Это не часть поведения Monad
предназначен для описания. Другие приводили множество примеров типов, в которых нет значения для extract
работать на. Но эти типы все еще поддерживают предполагаемую семантику Monad
, Это важно. Это означает, что Monad
это более общий интерфейс, чем вы полагаете.
Предположим, была такая функция:
extract :: Monad m => m a -> a
Теперь вы можете написать "функцию", например:
appendLine :: String -> String
appendLine str = str ++ extract getLine
Если только extract
функция гарантированно никогда не завершится, это нарушит ссылочную прозрачность, потому что результат appendLine "foo"
будет (а) зависеть от чего-то другого, чем "foo"
, (b) оценивать разные значения при оценке в разных контекстах.
Или, проще говоря, если бы был действительно полезный extract
Операция Haskell не будет чисто функциональной.
Есть ли встроенная функция с подписью
:: (Monad m) => m a -> a
?
Если 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
просто не очень хорошо определено. Он может иметь несколько значений для одной и той же монады или вообще ничего не значить для некоторых монад, а иногда это просто небезопасно.
Потому что это может не иметь смысла (на самом деле, во многих случаях не имеет смысла).
Например, я мог бы определить Монаду Парсера следующим образом:
data Parser a = Parser (String ->[(a, String)])
Теперь нет абсолютно никакого разумного способа получить String
из Parser String
, На самом деле, нет никакого способа вытащить String из этого с помощью только Monad.
Есть полезное extract
функция и некоторые другие функции, связанные с этим на http://hackage.haskell.org/package/comonad-5.0.4/docs/Control-Comonad.html
Он определен только для некоторых функторов / монад и не обязательно дает вам полный ответ, а скорее дает ответ. Таким образом, будут возможные подклассы comonad, которые дадут вам промежуточные этапы выбора ответа, где вы могли бы его контролировать. Вероятно, связано с возможными подклассами Traversable. Я не знаю, определены ли такие вещи где-нибудь.
Почему Google не перечисляет эту функцию вообще, по-видимому, потому что пакет comonad не индексируется, в противном случае я думаю, что ограничение Monad будет предупреждено и extract
будет в результатах для тех монад с Comonad
пример. Возможно, это связано с тем, что парсер hoogle не завершен и не работает с некоторыми строками кода.
Мои альтернативные ответы:
- Вы можете выполнить - возможно, рекурсивный - анализ случая, если вы импортировали конструкторы типа
- Вы можете вставить свой код, который будет использовать извлеченные значения в монаду, используя
monad >>= \a -> return $ your code uses a here
в качестве альтернативной структуры кода и до тех пор, пока вы можете преобразовать монаду в "IO ()" таким образом, чтобы вывести свои выходные данные, что вы сделали. Это не похоже на извлечение, но математика не такая, как в реальном мире.
Ну, технически есть небезопасное PerformIO для монады ввода-вывода.
Но, как следует из названия, эта функция злая, и вы должны использовать ее, только если вы действительно знаете, что делаете (и если вам нужно спросить, знаете ли вы, или нет, то нет)