Haskell 单子;绑定";函数问题
如果我这样定义“bind”函数: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 ... (>>=*) ::
(>>=) :: 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
,但它可以