哨兵

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

  在上一篇创建了我们的第一个Monad, Identity<T>. 我们确定了类型要变成Monad, 它必须有一个type constructor(Identity<T>), 和两个方法,Bind与ToIdentity

//a function Bind, allows us to compose Identity returning functions

public static Identity<B> Bind<A,B>(this Identity<A>a, Func<A,Identity<B>func>)
{
return func(a.Value);
}

public static Identity<T>ToIdentity<T>(tis T Value)
{
return new Identity<T>(value);
}

我也提到了在C#里 Bind有个不同的名字,SelectMany,它是为IEnumerable<T>定义的扩展方法, 如你所知,IEnumerable<T>也是一个Monad,实际上它是C#里Monad的代表.

        今天我们来看下如何为Identity<T>实现SelectMany, 并且去掉繁琐的lambda表达式

Linq要求我们写一个函数来结合Bind和To****的功能,SelectMany,SelectMany签名必须如下:

Identity<C>SelectMany<A,B,C>(this Identity<A>a,Func<A,Identity<B>>func,Func<A,B,C>select)

它看起来像Bind函数,只是多了一个select参数,它以A,B为参数,返回C。 并且有一个不同的返回类型Identity<C>,替代Identity<B>. 如果你的amplified实现了SelectMany方法,在执行Linq的"from x in y"表达式时会转换为SelectMany的调用.

让我们将SelectMany实现为一个扩展方法:

public static Identity<C>SelectMany<A,B,C>(this Identity<A>a,Func<A,Identity<B>>func, Func<A,B,C>select)
{
return ???
}

现在要根据参数类型来写出实现,首先我们知道要返回Identity<C>, 只有select Func可以返回C,我们可以调用ToIdentity将C转换为Identity<C>

public static Identity<C>SelectMany<A,B,C>(this Identity<A>a,Func<A,Identity<B>>func, Func<A,B,C>select)
{
return select(???).ToIdentity();
}

传什么给select呢, 第一个参数是A,我们可以调用a.Value得到A, 第二个参数B,我们可以通过Bind函数得到B

public static Identity<C>SelectMany<A,B,C>(this Identity<A>a,Func<A,Identity<B>>func, Func<A,B,C>select)
{
return (a.Value,a.Bind(func).Value).ToIdentity();
}

让我们展开Bind函数,在这里Bind并没有多大用

public static Identity<C>SelectMany<A,B,C>(this Identity<A>a,Func<A,Identity<B>>func, Func<A,B,C>select)
{
return select(a.Value,func(a.Value).Value).ToIdentity();
}

我们已经为Identity<T>实现了SelectMany. 现在我们可以用Linq语法替换我们上一篇的lambda表达式:

var result="Hello World!".ToIdentity().Bind(a=>

                                       7.ToIdentity().Bind(b=>

 (new DateTime(2010,1,11)).ToIdentity().Bind(c=>

(a+", "+b.ToString()+", "+c.ToShortDateString())

                                           .ToIdentity())));

var result=from a in "Hello World".ToIdentity()

                 from b in 7.ToIdentity()

                 from c in (new DateTime(2010,1,11)).ToIdentity()

                 select a+", "+b.ToString()+", "+c.ToShortDateString();

Console.WriteLine(result.Value);

是不是清晰很多?通过新视角看Linq和Monad, 我们可以把"from x in y"看做是Monad式的赋值,将将右侧的amplified value 赋值给左侧的unamplified type。

实际上你可以对任何WhatEver<T>使用Linq语法,而不仅仅是IEnumerable<T>。如果你想使用其他linq 语法如where,let,join等,你必须实现对应的方法, 他们都可以使用Bind创建

posted on 2016-06-27 23:30  哨兵  阅读(341)  评论(0编辑  收藏  举报