Монада "связать" функцию вопроса - PullRequest
1 голос
/ 16 июля 2010

Если я определю функцию «привязки» следующим образом:

(>>=) :: M a -> (a -> M' b) -> M' b

Поможет ли это определение, если я хочу, чтобы результат был нового типа монады, или я должен использовать ту же монаду, но с b в том же поле монады, что и раньше?

Ответы [ 4 ]

7 голосов
/ 16 июля 2010

Как я уже упоминал в комментарии, я не думаю, что такую ​​операцию можно безопасно определить для общих монад (например, M = IO, M' = Maybe).

Однако, если M безопасно конвертируется в M ', тогда эта привязка может быть определена как:

convert :: M1 a -> M2 a
...

(>>=*) :: M1 a -> (a -> M2 b) -> M2 b
x >>=* f = convert x >>= f

И наоборот,

convert x = x >>=* return

Некоторые из такихбезопасные методы преобразования: maybeToList (Возможно → []), listToMaybe ([] → Возможно), stToIO (ST RealWorld → IO), ... обратите внимание, что универсального метода convert длялюбые монады.

1 голос
/ 17 июля 2010

Это может быть сложным делом, но в некоторых контекстах это выполнимо. По сути, если это монады, которые вы видите внутри (например, Maybe или написанная вами монада), то вы можете определить такую ​​операцию.

Одна вещь, которая иногда весьма удобна (в GHC) - это заменить класс Monad на свой собственный. Если вы определите return, >>=, fail, вы все равно сможете использовать запись do. Вот пример, который может быть таким, как вы хотите:

class Compose s t where
  type Comp s t

class Monad m where
  return :: a -> m s a
  fail  :: String -> m a
  (>>=) :: (Compose s t) => m s a -> (a -> m t b) -> m (Comp s t) b
  (>>)  :: (Compose s t) => m s a -> m t b -> m (Comp s t) b
  m >> m' = m >>= \_ -> m'

Затем вы можете контролировать, какие типы могут быть упорядочены, используя оператор связывания, основываясь на том, какие экземпляры Compose вы определяете. Естественно, вы часто будете хотеть Comp s s = s, но вы также можете использовать это, чтобы определить все виды сумасшедших вещей.

Например, возможно, у вас есть какие-то операции в вашей монаде, за которыми совершенно не могут следовать никакие другие операции. Хотите применить это статически? Определите пустой тип данных data Terminal и не предоставляйте экземпляры Compose Terminal t.

Этот подход не подходит для переноса (скажем) Maybe в IO, но его можно использовать для переноса некоторых данных на уровне типов о том, что вы делаете.

Если вы действительно хотите изменить монады, вы можете изменить приведенные выше определения классов примерно на

class Compose m n where
  type Comp m n
  (>>=*) :: m a -> (a -> n b) -> (Compose m n) b

class Monad m where
  return :: a -> m a
  fail :: String -> m a
  (>>=) :: Compose m n => m a -> (a -> n b) -> (Compose m n) b
  m >>= f = m >>=* f
  (>>) :: Compose m n => m a -> (n b) -> (Compose m n) b
  m >> n = m >>=* \_ -> n

Я использовал предыдущий стиль для полезных целей, хотя я полагаю, что эта последняя идея также может быть полезна в определенных контекстах.

1 голос
/ 17 июля 2010

Мало того, что это определение не поможет, но оно серьезно запутает будущих читателей вашего кода, поскольку оно нарушит все ожидания его использования.

Например, оба M и M 'должныбыть монад?Если так, то как они определены?Помните: определение >>= является частью определения Monad и используется везде для определения других функций, использующих Monad - каждая функция, кроме return и fail самих себя.

Кроме того, выполучить, чтобы выбрать, какие М и М 'вы используете, или компьютер?Если так, то как вы выбираете?Работает ли он для любых двух экземпляров Monad, или есть какое-то подмножество Monad, которое вы хотите - или выбор M определяет выбор M '?

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

0 голосов
/ 17 июля 2010

Вы можете посмотреть на этот образец от Олега: http://okmij.org/ftp/Computation/monads.html#param-monad

...