在上一篇创建了我们的第一个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创建

Linq怎么支持Monad的更多相关文章

  1. 通过orderby关键字,LINQ可以实现升序和降序排序。LINQ还支持次要排序。

    通过orderby关键字,LINQ可以实现升序和降序排序.LINQ还支持次要排序. LINQ默认的排序是升序排序,如果你想使用降序排序,就要使用descending关键字. static void M ...

  2. Rafy 中的 Linq 查询支持(根据聚合子条件查询聚合父)

    为了提高开发者的易用性,Rafy 领域实体框架在很早开始就已经支持使用 Linq 语法来查询实体了.但是只支持了一些简单的.常用的条件查询,支持的力度很有限.特别是遇到对聚合对象的查询时,就不能再使用 ...

  3. C#8.0: 在 LINQ 中支持异步的 IAsyncEnumerable

    C# 8.0中,提供了一种新的IAsyncEnumerable<T>接口,在对集合进行迭代时,支持异步操作.比如在读取文本中的多行字符串时,如果读取每行字符串的时候使用同步方法,那么会导致 ...

  4. Monad 系列

    本系列是在学习Monad时在网上找到的一个老外的博客,作者是MikeHadlow,地址是mikehadlow.blogspot.com,  可惜国内访问不了.这个系列对Monad讲解的浅显易懂,而且有 ...

  5. db4o发布7.2,出现.NET 3.5版本,支持LINQ

    db4o发布7.2,出现.NET 3.5版本,支持LINQ   Db4Object刚刚发布了db4o的7.2beta,除了以前支持如下的平台:.NET 1.1,.NET 2.0,Mono外,现在还支持 ...

  6. C# 函数式编程:LINQ

    一直以来,我以为 LINQ 是专门用来对不同数据源进行查询的工具,直到我看了这篇十多年前的文章,才发现 LINQ 的功能远不止 Query.这篇文章的内容比较高级,主要写了用 C# 3.0 推出的 L ...

  7. Entity Framework 6 Recipes 2nd Edition(11-11)译 -> 在LINQ中调用数据库函数

    11-11. 在LINQ中调用数据库函数 问题 相要在一个LINQ 查询中调用数据库函数. 解决方案 假设有一个任命(Appointment )实体模型,如Figure 11-11.所示, 我们想要查 ...

  8. LinqToDB 源码分析——轻谈Linq查询

    LinqToDB框架最大的优势应该是实现了对Linq的支持.如果少了这一个功能相信他在使用上的快感会少了一个层次.本来笔者想要直接讲解LinqToDB框架是如何实现对Linq的支持.写到一半的时候却发 ...

  9. C#基础:LINQ 查询函数整理

    1.LINQ 函数   1.1.查询结果过滤 :where() Enumerable.Where() 是LINQ 中使用最多的函数,大多数都要针对集合对象进行过滤,因此Where()在LINQ 的操作 ...

随机推荐

  1. JSP_内置对象_out

    out对象是JspWriter类的实例,是向客户端输出内容的常用对象,常用方法如下: void println() 向客户端打印字符串 void clear() 清除缓冲区的内容,如果在flush之后 ...

  2. JAVA中实现根据文件路径下载文件

    import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; ...

  3. RabbitMQ出现服务启动几秒退出问题

    最近在学习rebbitmq, 1.首先安装了otp_win64_20.3, 2.erlang安装完成需要配置erlang环境变量: 这个是新建的 文档是:ERLANG_HOME D:\develop\ ...

  4. 【剑指Offer】16、合并两个排序的链表

      题目描述:   输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则.   解题思路:   首先需要判断几个特殊情况,即判断输入的两个指针是否为空.如果第一个 ...

  5. MAC下redis的安装和配置

    1.下载 打开官网:https://redis.io/ 选择下载你要的版本压缩包 2.安装 打开终端,cd - 将下载的压缩包拷贝到local目录下:sudo cp Downloads/redis-4 ...

  6. rsync增量同步标志位详细解释

    rsync非常强大,下面是我用rsync做目录备份时用到的参数: rsync -ruPi -plEt /home/op/photo/ /remote_backup/photo/ --dry-run - ...

  7. python第十一周:RabbitMQ、Redis

    Rabbit Mq消息队列 RabbitMQ能为你做些什么? 消息系统允许软件.应用相互连接和扩展.这些应用可以相互链接起来组成一个更大的应用,或者将用户设备和数据进行连接.消息系统通过将消息的发送和 ...

  8. firebird数据库建模

    用ERS数据库建模,真的很方便.可是8.0版本只支持interbase和ib2007.没有建模工具,查找修改数据库等确实是裹足难走.新的建模工具不适应.找来找去,新版10.0出来了.雷锋帮助把使用权也 ...

  9. HDU - 1043 - Eight / POJ - 1077 - Eight

    先上题目: Eight Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tota ...

  10. [bzoj2938][Poi2000]病毒_AC自动机

    病毒 bzoj-2938 Poi-2000 题目大意:给你n个01串,问是否存在一个无限长的01串使得这个01的任意子串都不等于给出的01串. 注释:All_length<=30,000 想法: ...