再谈continuation monad

上一篇中我们已经介绍了continuation monad,但是这个monad与Identity,Maybe,IEnumerable monads稍微难于理解,故本篇再次讨论。

首先解决上一篇中最后关于continuation monad的问题,即以下这段代码目前还无法通过编译,

var r = from x in .ToContinuation<int, string>()
from y in .ToContinuation<int, string>()
select x + y; Console.WriteLine(r(z => z.ToString().Replace('', 'a'))); // displays a3

比较前面Identity monad中SelectMany实现了两个版本,可以发现对于continuation monad,我们需要实现其SelectMany的另一个版本,即除了this指定的参数,还有两个输入参数。

对比Identity monad中相应的SelectMany实现,容易知道,新增的参数是一个Func 函数,在我们上面这个例子中,这个Func 函数的lambda表达式体就是 x + y,故不难写出SelectMany的函数签名,如下,用(1)标注

public static K<V, Answer> SelectMany<T, U, V, Answer>(this K<T, Answer> m, Func<T, K<U, Answer>> k, Func<T, U, V> s);                                                                                                 (1)

这里我们也新增了一个泛型参数V,虽然对我们这个例子来说,T,U,V都是int,Answer是string,但是我们考虑了更一般的情况,故用三个泛型参数T,U,V来表示。

为了方便,我们首先把之前的SelectMany版本再次写出来,免得到上一篇文章中找寻,

public static K<U, Answer> SelectMany<T, U, Answer>(this K<T, Answer> m, Func<T, K<U, Answer>> k)
{
return (Func<U, Answer> c) => m((T x) => k(x)(c));
}

那么,如何实现(1)的SelectMany函数?

分析:

返回类型为K<V, Answer>,故函数体可以写成类似如下,

return m.SelectMany<T, V, Answer>(...);

这句代码中的SelectMany记为之前我们已经实现了的重载版本,根据这个重载版本,我们知道括号中...部分为一个输入参数,类型为Func<T, K<V, Answer>>,故此时(1)的函数体如下,

return m.SelectMany<T, V, Answer>((T x) => ...)

现在,这句代码中的...部分的返回类型为K<V, Answer>,此为最终的返回类型,那如何得到这样一个类型值?

首先,要得到V类型,只能通过s获得,而s的参数类型为T, U,T类型值已知,为x,还需要一个U类型值,U类型值只能通过k获得,应用k到x上得到K<U, Answer>,对这个K<U, Answer>应用已实现的SelectMany重载版本,即可去包装化K<U, Answer>,如下所示,

k(x).SelectMany(...)

为了与最终的返回类型K<V, Answer>对接起来,这里k(x).SelectMany(...)中的输入参数必须为Func<U, K<V, Answer>>,此时(1)的函数体可以写成如下,

return m.SelectMany<T, V, Answer>((T x) => k(x).SelectMany<U, V, Answer>(y => ...)

此时,这句代码中...部分的返回类型为K<V, Answer>,而要得到这个类型,此时已经很容易了,将s应用到x和y上,得到类型V的值,将V类型包装成K<V, Answer>即可,而包装可以使用ToContinuation<V, Answer>,故(1)的函数体为,

return m.SelectMany<T, V, Answer>((T x) => k(x).SelectMany<U, V, Answer>(y => s(x, y).ToContinuation<V, Answer>()));

此时,可以运行上面的那个LINQ的测试代码了。

深入理解Continuation Monad

Continuation Monad用于将内部的计算外置。举例说明,

var r = .ToContinuation<int, string>().SelectMany(
x => .ToContinuation<int, string>().SelectMany(
y => (x + y).ToContinuation<int, string>())); Console.WriteLine(r(i => i.ToString()));

很明显,这段测试代码用于将7和6相加,并将和转换为字符串。如果对7和6的和不直接简单的转换为字符串,而是想对和先加1再转换为字符串呢,此时只需要修改最后一句代码中int转换为string的逻辑即可,无需修改第一句程序语句。

Continuation Monad涉及两个类型之间的关系,实现将这两个类型之间的转换的逻辑实现外置,这与F#中的Continuation Computation类似。

我们在实现Continuation Monad的函数时,主要是通过函数参数对类型进行转换,直到获得需要的类型,如下面代码为例,用于将类型T转换为Answer类型(其中this所指参数省略),

public static K<T, Answer> CallCC<T, Answer>(this ...);

K<T, Answer>的类型参考上一篇文章。

K<T, Answer>正是用于将类型T转换为Answer类型的委托,那要得到这个返回类型,this所指的参数类型可以是Func<Func<T, K<U, Answer>>, K<T, Answer>>,这个类型函数的输入为一个从T到K<U, Answer>的Func,这个Func的输入必须为T,因为我们的最终目的是要从T转换为Answer,那自然必须提供一个T才行,当然,这个Func还可以是从T到K<T, Answer>,此时this所指参数类型为Func<Func<T, K<T, Answer>>, K<T, Answer>>。这个Func甚至可以是从T到K<T, U>的函数,可以有很多可能,这里仅列出已经提过的几种情况,

public static K<T, Answer> CallCC<T, U, Answer>(this Func<Func<T, K<U, Answer>>, K<T, Answer>> u);            (1)

public static K<T, Answer> CallCC<T, U, Answer>(this Func<Func<T, K<T, Answer>>, K<T, Answer>> u);            (2)

public static K<U, Answer> CallCC<T, U, Answer>(this Func<Func<T, K<T, U>>, K<U, Answer>> u);               (3)

再如第一种情况,this所指的参数类型Func<Func<T, K<U, Answer>>, K<T, Answer>>,这个类型函数的输出类型为K<T, Answer>正好与我们最终的返回类型相同,然而,如果这个类型函数的输出类型与最终输出类型不同呢?比如是K<U, Answer>,如下所示,

public static K<T, Answer> CallCC<T, U, Answer>(this Func<Func<T, K<U, Answer>>, K<U, Answer>> u);              (4)

这种情况下,对提供的T,仅能得到K<U, Answer>类型,如果不提供从U到T的转换函数,则我们无法实现CallCC。

Continuation Monad函数实现

对第一种情况来说,参数u这个类型函数的输出类型即为我们所要的最终类型,故CallCC实现较为简单,如下

public static K<T, Answer> CallCC<T, U, Answer>(this Func<Func<T, K<U, Answer>>, K<T, Answer>> u)
{
return c => u(x => z => c(x))(c);
}

第二种情况更加简单,可以看成是第一种情况的U为T的特殊情况。

第三种情况,参数u这个类型函数的输出类型也是最终的返回来下,其实现如下,

public static K<U, Answer> CallCC1<T, U, Answer>(this Func<Func<T, K<T, U>>, K<U, Answer>> u)
{
return c => u(t => z => z(t))(c);
}

Continuation Monad函数用法

public void Call()
{
Func<Func<int, K<int, string>>, K<string, char[]>> u = U;
var r = u.CallCC(); Console.WriteLine(r(s => s.ToCharArray()));
} private K<string, char[]> U(Func<int, K<int, string>> k)
{
var i = ;
var m = ;
return k(m)(j => (i + j).ToString()).ToContinuation<string, char[]>();
}

其中,lambda表达式j => (i + j).ToString()中的j其实是m传入的。

C# Monads的实现(二)的更多相关文章

  1. C# Monads的实现(一)

    了解Haskell语言的朋友都知道它是一门纯函数式编程,输出结果只跟输入参数相关,这导致Haskell不能有输入输出函数,因为不同的环境下,输入相同,但输出可能有所不同.Haskell语言中,变量的值 ...

  2. C# 函数式编程及Monads.net库

    函数式编程中,一切皆为函数,这个函数一般不是类级别的,其可以保存在变量中,可以当做参数或返回值,是函数级别的抽象和重用,将函数作为可重用的基本模块,就像面向对象中一切皆为对象,把所有事物抽象为类,面向 ...

  3. 【小程序分享篇 二 】web在线踢人小程序,维持用户只能在一个台电脑持登录状态

    最近离职了, 突然记起来还一个小功能没做, 想想也挺简单,留下代码和思路给同事做个参考. 换工作心里挺忐忑, 对未来也充满了憧憬与担忧.(虽然已是老人, 换了N次工作了,但每次心里都和忐忑). 写写代 ...

  4. 前端开发中SEO的十二条总结

    一. 合理使用title, description, keywords二. 合理使用h1 - h6, h1标签的权重很高, 注意使用频率三. 列表代码使用ul, 重要文字使用strong标签四. 图片 ...

  5. 【疯狂造轮子-iOS】JSON转Model系列之二

    [疯狂造轮子-iOS]JSON转Model系列之二 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇<[疯狂造轮子-iOS]JSON转Model系列之一> ...

  6. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  7. 谈谈一些有趣的CSS题目(十二)-- 你该知道的字体 font-family

    开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...

  8. MIP改造常见问题二十问

    在MIP推出后,我们收到了很多站长的疑问和顾虑.我们将所有疑问和顾虑归纳为以下二十个问题,希望对大家理解 MIP 有帮助. 1.MIP 化后对其他搜索引擎抓取收录以及 SEO 的影响如何? 答:在原页 ...

  9. 如何一步一步用DDD设计一个电商网站(二)—— 项目架构

    阅读目录 前言 六边形架构 终于开始建项目了 DDD中的3个臭皮匠 CQRS(Command Query Responsibility Segregation) 结语 一.前言 上一篇我们讲了DDD的 ...

随机推荐

  1. python——爬虫

    网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用的名字还有蚂蚁.自动索引.模拟程序或者蠕 ...

  2. C# 实验4 数据库

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  3. Theos 工程make package时报错

    错误: /Applications/Xcode.app/Contents/Developer/usr/bin/make package requires you to have a layout/ d ...

  4. Linux网络管理常用命令:net-tools VS iproute2

    Exported from workflowy! net-tools ifconfigifconfig命令:用于接口及地址查看和管理 route netstat arp iproute2 ip lin ...

  5. FaceBook页面加载技术

    1. 技术背景 FaceBook页面加载技术 试想这样一个场景,一个经常访问的网站,每次打开它的页面都要要花费6 秒:同时另外一个网站提供了相似的服务,但响应时间只需3 秒,那么你会如何选择呢?数据表 ...

  6. CSS 效果汇总

    只要决心够, 就能征服痛苦. 把一些常用的 CSS 效果记录下来 1. 利用 z-index :hover 显示层 github 效果地址>> 此效果主要利用 a:hover 来改变 sp ...

  7. 数据库中Schema、Database、User、Table的关系[转]

    数据库的初学者往往会对关系型数据库模式(schema).数据库(database).表(table).用户(user)之间感到迷惘,总感觉他们的关系千丝万缕,但又不知道他们的联系和区别在哪里,对一些问 ...

  8. Python学习笔记——基础篇【第六周】——logging模块

    常用模块之logging 用于便捷记录日志且线程安全的模块 import logging logging.basicConfig(filename='log.log', format='%(ascti ...

  9. background-size (设置背景图片的大小)

    设置背景图片的大小,以长度值或百分比显示(数值包括 长度length和百分比percentage),还可以通过cover和contain来对图片进行伸缩. 语法:background-size: au ...

  10. old linkedin profile

    employments: software engineer intern: Karl Storz Imaging, 2015-06 to 2015-09. software engineer int ...