Haskell 单子;绑定";函数问题

Haskell 单子;绑定";函数问题,haskell,monads,Haskell,Monads,如果我这样定义“bind”函数: (>>=) :: M a -> (a -> M' b) -> M' b 如果我希望结果是一个新的单子类型,或者我应该像以前一样使用相同的单子,但在同一个单子框中有b,这个定义会对我有帮助吗?正如我在评论中提到的,我认为这样的操作不能安全地定义为一般单子(例如,M=IO,M'=Maybe) 但是,如果M可安全转换为M',则该绑定可定义为: convert :: M1 a -> M2 a ... (>>=*) ::

如果我这样定义“bind”函数:

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

如果我希望结果是一个新的单子类型,或者我应该像以前一样使用相同的单子,但在同一个单子框中有b,这个定义会对我有帮助吗?

正如我在评论中提到的,我认为这样的操作不能安全地定义为一般单子(例如,
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
(可能是→ []),
listtomabe
([]→ 可能),
stToIO
(ST RealWorld→ IO)。。。请注意,任何单子都没有通用的
convert
方法。

正如我在评论中提到的,我不认为可以安全地为普通单子定义此类操作(例如
M=IO
M'=可能

但是,如果M可安全转换为M',则该绑定可定义为:

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

(>>=*) :: M1 a -> (a -> M2 b) -> M2 b
x >>=* f = convert x >>= f
反过来说

convert x = x >>=* return

一些这样的安全转换方法是
maybeToList
(可能是→ []),
listtomabe
([]→ 可能),
stToIO
(ST RealWorld→ IO)。。。请注意,对于任何单子,都没有通用的
转换方法。

该定义不仅没有帮助,而且会严重混淆代码的未来读者,因为它会打破对它使用的所有期望

例如,M和M'都应该是单子吗?如果是,那么它们是如何定义的?请记住:
>=
的定义是Monad定义的一部分,并且在任何地方都用于定义其他使用Monad的函数-除了
返回
失败
本身之外的每个函数

还有,你可以选择你使用的M和M,还是电脑?如果是的话,你怎么选择?它是否适用于任何两个Monad实例,或者是否存在您想要的Monad的某个子集,或者M的选择是否决定了M'的选择


可以制作一个像你所写的那样的函数,但它肯定比
>=
复杂得多,如果试图把你的函数塞进
>=
的衣服里,那将是误导性的、残酷的,而且可能是灾难性的。

这个定义不仅没有帮助,但它将严重地混淆代码的未来读者,因为它将打破所有对它使用的期望

例如,M和M'都应该是单子吗?如果是,那么它们是如何定义的?请记住:
>=
的定义是Monad定义的一部分,并且在任何地方都用于定义其他使用Monad的函数-除了
返回
失败
本身之外的每个函数

还有,你可以选择你使用的M和M,还是电脑?如果是的话,你怎么选择?它是否适用于任何两个Monad实例,或者是否存在您想要的Monad的某个子集,或者M的选择是否决定了M'的选择


可以制作一个像您所写的那样的函数,但它肯定比
>=
复杂得多,而且会产生误导,很残忍,尝试将函数塞进
>=
的衣服中可能会带来灾难性后果。

您可能需要查看来自Oleg的此示例:

您可能需要查看来自Oleg的此示例:

这可能是一件复杂的事情,但在某些情况下是可行的。基本上,如果它们是您可以在内部看到的单子(例如
可能
或您编写的单子),那么您可以定义这样的操作

(在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
的哪些实例,使用bind操作符控制哪些类型可以排序。当然,您经常需要
Comp s=s
,但您也可以使用它来定义各种疯狂的事情

例如,您的monad中可能有一些操作,这些操作绝对不能被任何其他操作所遵循。想静态地执行吗?定义一个空的数据类型
数据终端
,并且不提供
组合终端t
的实例

这种方法不适合从(比如)
转换到
IO
,但它可以用来携带一些关于您正在做什么的类型级别的数据

如果您确实想更改monad,可以将上面的类定义修改为

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

我使用了前一种风格来达到有用的目的,尽管我认为后一种想法在某些情况下也可能有用。

这可能是一件复杂的事情,但在某些情况下是可行的。基本上,如果它们是您可以在内部看到的单子(例如
可能
或您编写的单子),那么您可以定义这样的操作

(在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
的哪些实例,使用bind操作符控制哪些类型可以排序。当然,您经常需要
Comp s=s
,但您也可以使用它来定义各种疯狂的事情

例如,您的monad中可能有一些操作,这些操作绝对不能被任何其他操作所遵循。想静态地执行吗?定义一个空的数据类型
数据终端
,并且不提供
组合终端t
的实例

这种方法不适合(比如)从
转换到
IO
,但它可以