我有一般性问题和更具体的案例问题。
一般如何组合不同的monad? monad运算符的某些组合是否允许轻松组合?或者是否必须编写特殊方法来组合每对可能的monad?
作为一个具体的例子,我写了一个Maybe monad。如何使用IEnumerable<IMaybe<T>>
?除了手动挖掘到LINQ扩展中的Maybe monad(例如:if(maybe.HasValue)
... select
子句中)之外,是否存在将两者与其各自的Bind等组合的“monadic”方式monad操作?
否则,如果我必须编写特定的组合方法,这样的方法是正确的吗?
public static IEnumerable<B> SelectMany<A, B>(this IEnumerable<A> sequence, Func<A, IMaybe<B>> func)
{
return from item in sequence
let result = func(item)
where result.HasValue
select result.Value;
}
public static IEnumerable<C> SelectMany<A, B, C>(this IEnumerable<A> sequence, Func<A, IMaybe<B>> func, Func<A, B, C> selector)
{
return from item in sequence
let value = item
let maybe = func(item)
where maybe.HasValue
select selector(value, maybe.Value);
}
答案 0 :(得分:0)
很棒的问题!
monad转换器是一种向任意基础monad添加一些功能,同时保留monad-ness的类型。可悲的是,Monad变换器在C#中难以表达,因为它们必须使用更高级的类型。所以,在Haskell工作,
class MonadTrans (t :: (* -> *) -> (* -> *)) where
lift :: Monad m => m a -> t m a
transform :: Monad m :- Monad (t m)
让我们一行一行地回顾一下。第一行声明monad转换器是类型t
,它接受类* -> *
的参数(即,期望一个参数的类型)并将其转换为另一种类型{{1} }。当你意识到所有monad都有类* -> *
时,你可以看到* -> *
将monad变成其他monad的意图。
下一行说明所有monad变换器都必须支持t
操作,该操作需要一个任意monad lift
并将提升<变速器进入变换器的世界m
最后,t m
方法表示对于任何monad transform
,m
也必须是monad。我正在使用the constraints
package中的蕴涵运算符t m
。
通过一个例子,这将更有意义。这是一个monad变换器,它将:-
- 添加到任意基础monad Maybe
。 m
运算符允许我们中止计算。
nothing
为了使newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
nothing :: Monad m => MaybeT m a
nothing = MaybeT (return Nothing)
成为monad变换器,只要它的参数是monad,它就必须是monad。
MaybeT
现在编写instance Monad m => Monad (MaybeT m) where
return = MaybeT . return . Just
MaybeT m >>= f = MaybeT $ m >>= maybe (return Nothing) (runMaybeT . f)
实现。 MonadTrans
的实现将基本monad的返回值包装在lift
中。 Just
的实施是无趣的;它只是告诉GHC的约束求解器,只要它的参数是transform
就确实是一个monad。
MaybeT
现在我们可以编写一个monadic计算,使用instance MonadTrans MaybeT where
lift = MaybeT . fmap Just
transform = Sub Dict
来添加失败,例如MaybeT
monad。 State
允许我们使用标准的lift
方法State
和get
,但如果我们需要使计算失败,我们也可以访问put
。 (我考虑使用你的nothing
(又名IEnumerable
)的例子,但是对于已经支持它的monad添加失败有一些不妥之处。)
[]
使monad变换器真正有用的是它们的可堆叠性。这允许你用许多小monad组成具有许多功能的大monad,每个monad具有一个功能。例如,给定的应用程序可能需要执行IO,读取配置变量并抛出异常;这将使用类似
的类型进行编码example :: MaybeT (State Int) ()
example = do
x <- lift get
if x < 0
then nothing
else lift $ put (x - 1)
the mtl
package中有一些工具可以帮助您抽象出给定堆栈中monad变换器的精确集合和顺序,从而允许您忽略对type Application = ExceptT AppError (ReaderT AppConfig IO)
的调用。
答案 1 :(得分:-1)
在这种特定情况下,您可以在IEnumerable<T>
中实现MayBe<T>
,因此它返回0或1值。