创建我们第一个Monad
上一篇中介绍了如何使用amplified type, 如IEnumerable<T>,如果我们能找到组合amplified type函数的方法,就会更容易写出强大的程序. 我们已经说了很多次函数组合, 听起来又干又硬。函数组合其实就是简单编程,当我们写像下面这样的代码时:
var customer=customerData.GetById(customerId); var order=customer.CreateOrder();
我就是在使用函数组合,在这个例子里组合了GetById和CreateOrder函数, 所有的程序都是一个函数组合,显示的或者隐式的.
作为程序员我们知道怎么样组合参数和返回值都是unamplified type的函数,这很自然.
这里有两个简单的函数. 他们有相同的函数签名,我会使用函数委托和lambda表达式 ,而不是直接写静态方法:
Func<int,int>add2=x=>x+2; Func<int,int>mult2=x=>x*2;
组合这两个函数很简单:
Func<int,int>add2mult2=x=>mult2(add2(x));
我们使用新函数:
var r2=add2mult2(5);
Console.Out.WriteLine("r2={0}",r2);
结果输出r2=14
我们一直在做这样的操作,但是如果我们要组合返回amplified type的函数呢。首先我们需要一个amplified type来组合,在这个例子里我们选择最简单的amplified type. 它仅封装了一个值,我们定义它为Identity:
public class Identity<T>
{
public T Value {get;set;} public Identity(T value)
{
Value=value;
}
}
现在我们创建两个返回amplified type的函数
Func<int,Identity<int>>add2=x=>new Identity(x+2); Func<int,Identity<int>>mult2=x=>new Identity(x*2);
如果我们用上面同样的方式组合新函数,编译会失败:
Func<int,Identity<int>>add2mult2=x=>mult2(add2(x));
因为add2返回Identity<int>类型,而multi2的参数是int,类型不匹配,所以编译失败。我们当然可以使用Value属性,
Func<int,Identity<int>>add2mult2=x=>mult2(add2(x).Value)
这样可以,但问题是们要组合Identity<T>的任何函数时都要写x.Value.不要忘了Identity<T>是我们可以想到的最简单的amplified type,实际上它一点用也没有。我们如果要使用有用的amplified type,如IEnumerable<T>,Task<T>或者IMightThrowAnException<T>,我们就又回到了要添加很多样板代码的老路上。我们需要去掉x.Value,怎样去做呢?
我们要得到的是一个函数,它组合了add2和mult2, 我们定义这个函数为Bind。看看上面编译失败的例子,我们就知道Bind的签名了。它需要接收add2的返回值,一个Identity<T>类型的值和mult2,一个签名Func<int, Identity<int>>的函数. 为了使Bind更有用,我们定义它为泛型函数,并且是扩展方法:
//a function 'Bind',allows us to compose Identity returning functions public static Identity<B> Bind<A,B>(this Identity<A>, Func<A, Identity<B>>func)
{
func(a.Value);
}
当然Bind函数体简单的调用了a.Value并且将结果传给了func. 我们用Bind封装了x.Value. 这样的好处是这种处理amplified type的机制依赖于amplified type封装的类型。Bind的签名是唯一的.
现在我们可以组合add2和mult2了
Func<int,Identity<int>>add2Mult2=x=>add2(x).Bind(mult2);
我们可以使用我们新组合生成的函数:
var r1=add2mult2(5);
Console.Out.WriteLine("r1.Value={0}",r1.Value);
输出r1=14
让我们创建另一个有用的扩展方法, ToIdentity, 为了创建一个新的Identity:
public static Identity<T>ToIdentity<T>(T value)
{
return new Identity<T>(value);
}
现在我们可以用各种Identity值写复杂的表达式:
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())));
Console.WriteLine(result.Value);
输出Hello World!, 7, 11/01/2010
这里我们接收一个string ,"Hello World!",把它转换成一个Identity<string>.将整数7转换为Identity<int>, 日期转换为Identity<DateTime>.我们用Bind函数巧妙的访问封装的值, 连接它们并返回一个Identity<string>.
上面的code不是你见过的最优美的, 但是再看一下,我们接收了不同类型的amplified type, 不用访问Value 属性就可以对它们进行操作, 这是关键. 记住两个事实:首先在未来我们可以去掉lambda表达式,其次Identity<T>可能是最简单的amplified type了. 我们可以实现任意复杂度的功能.
Identity<T>是一个Monad,Monad要求有一个type constructor(Identity<T>自己 )和连个方法:我们的Bind和ToIdentity, 这就是全部。你会发现Bind和ToIdentity在不同语言里有不同的名字。ToIdentity在Haskell中 被叫做return,Bind在C#里被叫做SelectMany, 稍后会看到。他们叫什么名字并不重要,重要的是签名正确:
Bind: Func<Identity<A>,Func<A,Identity<B>>,Identity<B>>
ToIdentity:Func<T,Identity<T>>
Bind是我们写Monad功能的地方, 并且是唯一的地方,这是非常强大的抽象,也是Monad最有魅力的地方.
下一篇我们会看到怎样把Bind重命名为SelectMany, 并与ToIdentity组合使用,使我们可以在Linq中使用Identity<T> Monad
创建我们第一个Monad的更多相关文章
- 如何通过命令行创建和设置一个MySQL用户
我想要在MySQL服务器上创建一个新的用户帐号,并且赋予他适当的权限和资源限制.如何通过命令行的方式来创建并且设置一个MySQL用户呢? 要访问一个MySQL服务器,你需要使用一个用户帐号登录其中方可 ...
- 一个Monad的不严谨介绍
一个单子(Monad)说白了不过就是自函子范畴上的一个幺半群而已,这有什么难以理解的?* 之前了解了下Monad,后来一段时间没碰,最近研究Parser用到Monad时发现又不懂了.现在重新折腾,趁着 ...
- 创建你的一个composer包
如何创建自己的一个composer包,这个其实很好解决的!只要你了解composer相关的知识便不难做到. 首先,你还不知道什么是composer的话,请先学习下composer的相关知识.简单的说, ...
- 字节码编程,Byte-buddy篇一《基于Byte Buddy语法创建的第一个HelloWorld》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 相对于小傅哥之前编写的字节码编程: ASM.Javassist 系列,Byte Bu ...
- jQuery的DOM操作实例(3)——创建节点&&编写一个弹窗
一.原生JavaScript编写弹窗 二.jQuery编写弹窗 知识点归纳总结: 在原生JavaScript中,创建一个节点: var oDiv=document.createElement(&quo ...
- weblogic管理2 - 创建并启动一个managed server
创建一个managed server. 1. 进入网页console管理页面,如:http://10.100.25.14:7001/console , 先点击->服务器 (红色标记框) ...
- weblogic管理1——创建 和 删除一个domain
说明本文环境 WLS_HOME=/home/weblogic/Oracle/Middleware创建一个domian 第一种方法通过console 创建>[weblogic@11g Mid ...
- C#创建、安装一个Windows服务
关于WIndows服务的介绍,之前写过一篇: http://blog.csdn.net/yysyangyangyangshan/article/details/7295739.可能这里对如何写一个服务 ...
- range() 函数创建并返回一个包含指定范围的元素的数组
语法 range(first,second,step) 参数 描述 first 必需.规定数组元素的最小值. second 必需.规定数组元素的最大值. step 可选.规定元素之间的步进制.默认是 ...
随机推荐
- slf4j日志只输出到控制台,没输出到日志文件
最近使用SLF4J遇到了一个比较头疼的坑,日志输出到控制台没有问题,但是始终没有输出到日志文件.无论怎麽修改日志配置,始终是老样子. 有一种绝望,是各种百度.google却还是解决不了问题..直到我在 ...
- Memcached 之分布式算法原理
memcached并不像mongodb一样可以配置多个节点,并且节点之间可以”自动分配数据“,即相互通信,所以我们在做memcache分布式集群的时候要有一个算法来保证当一台memcache服务器宕机 ...
- LINUX命令行如何查看memcache运行状态
(附加)如何查看memcache服务器端版本: ./memcached -h memcache的运行状态可以方便的用 stats 命令显示. 首先用telnet 127.0.0.1 11211这样 ...
- LeetCode -- 1038. Binary Search Tree to Greater Sum Tree
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode ...
- php第二节课
基础语法 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...
- 利用vue-gird-layout 制作可定制桌面 (一)
安装 vue-gird-layout https://github.com/jbaysolutions/vue-grid-layout 先跑一遍demo 运行起来. # install with np ...
- [Ynoi2011]D2T1
题目大意: 给定一个数列$a$,有以下几种询问: 1. 给定$x$,在序列末尾插入$x$.2. 给定$l,r$,输出$\sum\limits_{i=l}^r a_i$.3. 给定$x$,将数列中的所有 ...
- 当样式中存在!important时无法使用show()或hide() 2017-06-11 22:25 15人阅读 评论(0) 收藏
如果使用!important在你的样式中,比如display: none !important,此时就不能用show()了 但是我在查阅资料时发现有这样的解释, If using !important ...
- Ecshop模板中html_options用法详解
程序部分 <?php $smarty->assign('status_list', $_LANG['cs']); // 订单状态 $smarty->display("ind ...
- 10.Spring注解+MyBatis+Servlet
1.创建sql脚本 create table account ( id ) unsigned not null auto_increment primary key, account_number ) ...