半年 Scala 小感——Monad 篇

上一次聊了聊 Scala 里的代数数据类型(ADT),这一次继续聊聊 Scala 里我比较喜欢的地方:实用的 Monad。

早在我刚开始学习函数式编程的时候,我就曾试图弄明白 Monad 这个概念,然而看过很多文章后依然一头雾水。究其原因,可能是我当初使用的语言 Clojure 里,Monad 并不是很常用,所以没有办法通过一些十分简单的例子来理解这个十分抽象的概念。在当时研究 Continuation 的时候,我甚至计划 啃下 Continuation Monad 这个硬骨头,然而十分惭愧的是最后也不了了之了(最近准备用 Scala 再试试)。

然而这几个月开始学习和使用 Scala,并在实际代码中看到、用到几个很基础的 Monad 之后,我突然茅塞顿开,有点明白 Monad 了。

Read on →

半年 Scala 小感——ADT 篇

作为一个 Scala Shop,我们的主力编程语言自然非它莫属了。入职小半年,我在工作中也用到了不少 Scala,对其了解也逐渐深入,现在是时候聊聊我对这个语言的感觉了。

必须声明的是,我还是个 Scala 菜鸟,对 Scala 的很多特性,尤其是类型系统了解还不够深入,所以这里只谈我了解的部分。这一次我想说的是代数数据类型(Algebraic Data Type)。

代数数据类型(ADT) 在函数式编程,尤其是静态类型的 FP 语言中很常见,它是一种组合已有类型形成新类型的方式。组合方式有两种:Product 或者 Sum。前者几乎所有编程语言都有,就是记录、Struct 或者 Class 等,它的取值范围是所有字段取值范围的笛卡尔积;后者一般在 ML、Haskell、Scala、F# 等静态类型函数式语言里才有,也叫 Tagged Union,每一种可能的取值都叫一个 variant, 它的取值范围是所有 variant 的并集。比如最典型的 Option[T] 类型,取值要么是 None,要么是 Some[T]

第一次听说 Sum Type 这个概念是在学 Standard ML 的时候,当时看到了就眼前一亮。配合模式匹配,代码写起来太简洁且富有表达力了。而真正让我更加认同 Sum Type 的是 Scott Wlaschin 在他的 Functional Design Patterns 里说的一句话(顺带一提,强烈推荐仔细看看这个 slides,全是干货):

“Make illegal states unrepresentable”

Read on →

再聊监控

去年我曾写过一个系列文章 介绍在上一家公司搭建的监控系统,那个方案基于 Ganglia 和 Nagios,效果很不错,对我们监控性能状况,及时发现问题起了不小的作用。

不巧的是,在新公司入职不久,我又开始和同事一起研究起监控系统来。作为技术上比较『追新』的公司,我们也把视线放到了比较新的一些方案上了。目前我们的监控系统已经上线运行了一些时间,效果也很不错。在此对用到的主要子系统做一个介绍,希望能对有兴趣或者正在研究监控方案的朋友们带来一些帮助。

绝对的核心 Riemann

Riemann 是一个分布式监控系统,但它做的事情其实非常简单,就是接收事件,按照配置的 stream 对事件进行处理并转发到 InfluxDB、Graphite 或者产生告警、发邮件等。官网的一张架构图很好地解释了这一点:

Riemann 是用 Clojure 实现的,作为一个 Clojure 拥趸,这一点极大地吸引了我。

Read on →

Space Apps 黑客马拉松

上个月 11、12 号,我和两个同事一起参加了 NASA 在全球各地组织的 Space Apps 黑客马拉松奥克兰站的活动。虽然最后我们很遗憾地没有拿到任何奖项,但整个过程还是充满了乐趣,我也学习到了很多,尤其是对待此类活动的心态问题,相信下次会做得更好。

我们选择的项目名叫 River Flow,是一个数据可视化的项目,目标是以一种简单、符合直觉的方式呈现美国 USGC 公开的水质监控数据,供有着不同需求的大众来使用。

我们的想法是抓取 USGC 的数据存储到我们自己的数据库中,然后提供一个 API 来查询具体某个地理位置的河流水质数据。前端以动画的形式呈现这些数据,例如用颜色深浅代表水的深度,用简单的粒子系统来模拟水的流动,且根据水的流速来决定动画快慢,再用线条宽度来表示河流宽度等。其他的数据则通过不同的颜色及辅助的标记来呈现。最后在界面下面放上一个可以拖动的时间轴用于观察随着时间的变化,且支持类似『播放』的模式,以动画的形式表现数据随着时间的变化。

Read on →

撸了个玩具级 Lisp 解释器

相信 10 个 Lisper 中有 9 个会写个自己的 Lisp 解释器玩吧,我当然也不能免俗了。去年 12 月初还在国内的时候就开了个坑写了这么个简单的 toylisp,两天时间就弄出来一个具备词法作用域的核心 Lisp,但是没有宏,所以功能有限,只具备核心的几个函数和 special form:lambdadefcarcdreq?condquote 等,还有几个基本的算术函数。像 defuniflet 等可以宏来实现的操作符就没有定义。

最近的闲暇时间又将其重新拾起来了,这次将宏实现出来了,并加入了一个小小的核心库供解释器启动时自动加载。几个基本的 Lisp 解释器和配套功能算是有了,现在可以拿出来说说了。

Read on →

异国他乡的新生活

一月七日,我从北京出发来到了奥克兰,开始了异国他乡的新生活。我的目标是留在这里,为自己的小家创造一个好的生活环境。当然,现在一切都还是起步,未来还有太多不确定性,但我一定要尽自己的全力去尝试一下,哪怕最后失败了,我也不会有遗憾了。

1. Middle Earth

相信很多人都是通过《魔戒》和《霍比特人》这两部电影开始了解新西兰这个国度,当然前些日子很火的《爸爸去哪儿》第二季节目的新西兰站也让人对这个国度有了一些近距离的了解。虽然在电影里早已见过了新西兰的美丽景色,但飞机降落那一刻,看到蓝天白云下连绵不断的绿色田园,中间点缀着星星点点的房子,我还是陶醉了。对我来说,这就是 Middle Earth 了。

Read on →

苹果令人担忧的软件质量

前年中秋换手机时买了个 iPhone 5,去年八月则入坑买了 rMBP 13,从此我成为了一个苹果用户(果粉肯定算不上)。总的来说,iOS 和 OS X 的使用体验没得说,绝对是我用过的所有系统里最好用的。但总有那么些不大不小的软件问题一直困扰着我,随着 iOS 和 OS X 的不断演进,这些问题似乎越来越多。苹果作为一个一流企业,我认为其软件系统的质量理应更好,如果长此以往不加改进的话,我不禁为其担心……

下面数数我遇到的这些问题吧。

Read on →

我的 2014

2012,2013 年我都没有写过年终总结,因为我发现 2011 年时展望的那些事情我都没怎么完成……执行力,这是我现在很大的一个软肋,希望 2015 年能跳出自己的『心理舒适区』,逼迫自己做点事情吧。

技术上无甚作为的一年

2014 年,技术上我基本上没什么大的突破。工作上是各种打杂的事情,一会写写核心引擎,一会做做运维,始终没能扎根在一个点上深入。当然期间还是收获了不少一手的高负载系统调优和运维经验。

因为工作的繁忙,我的业余时间也几乎没有多少时间放到技术上。一张图就可以看出来:

Read on →

解决恶心的 Nf_conntrack: Table Full 问题

相信有一定 Linux 服务器运维经验的人肯定见过这个问题:dmesg 或者 /var/log/messages 里大片地输出类似这样的日志:

1
2
Dec  8 11:22:29 product08 kernel: nf_conntrack: table full, dropping packet.
Dec  8 11:22:29 product08 kernel: nf_conntrack: table full, dropping packet.

同时服务器上的各种网络服务耗时大幅上升,各种 timed out,各种丢包,完全无法正常提供服务。

随着我们流量的提升,最近又开始被这个问题虐了。几个月前第一次遇到此问题的时候,我们使用了提高 nf_conntrack table size 的方法解决的,事实证明这是个治标不治本的方法,流量上来了,改得再大最后还是会爆掉。况且 table 太大,占用的内存也会很多。

今天再次研究了一下这个问题,换了另一种更好的方案,尝试解决掉它。简单地说,方案就是在 iptables raw 表里增加规则,将无需跟踪状态的包标记为 NOTRACK,这样就无需耗费 nf_conntrack table entry 来记录其状态了。

Read on →

玩玩 core.logic

我第一次接触逻辑编程是在 《七周七语言》中看到的对 Prolog 的介绍。当时一知半解,但感受到了逻辑编程在解决某类问题时的威力。之后开始玩 Clojure,Prolog 也被我放到一边没有深入学习。

最近在公司做一个项目的时候发现要解决的问题似乎很适合逻辑编程(我直接用 Java 也没太费功夫就实现了,但不管咋样,玩玩呗),遂决定拿来练习一下。当然我不再打算学习 Prolog 了,而是直接使用 Clojure 的逻辑编程库 core.logic

废话不多说,开始吧。

1. 问题描述

这个是航空行业的一个问题,但我相信大家都能明白。简单地说就是要根据一组航班号确定其代表的出发地、到达地和中转地,例如航班号是 CZ323 和 CZ305,那么行程是从北京到广州转机到奥克兰。乍一看似乎很简单,但是要考虑一个特殊情况,且听我道来。

每个航班都会有一个唯一的航班号,形如 MU779,其中 MU 是航空公司的代码, 779 是航班号。每个航班的出发到达地通常是固定的(为简化问题,不考虑航班排期变化的情况),比如 MU779 是 SHA-AKL 的(上海到奥克兰)。

恶心的是,航班可能有经停。所谓经停,就是航班在中间的某个城市降落,目的地是这个城市的乘客直接下来,其他乘客也先下去等会,到时间了再跟新来的乘客一起再回到飞机上继续飞到最终的目的地(就像火车中间停多个站一样)。比如CZ323 这个航班就是 PEK-CAN-PNH(从北京经停广州飞往金边)。对于这样的航班,光给我一个航班号是无法确定乘客从哪上,要去哪的,有可能是PEK-CAN, PEK-PNH,也可能是CAN-PNH。

如果是直达行程,我们就无能为力了,但如果是有转机的情况(转机就是到一个目的地之后换另一个航班),是能根据后面的航班作出推断的,因为转机肯定要在一个城市的,我不可能飞到广州然后到深圳转另一个航班(一般的机票检索系统不会这么干,也几乎没有这样的运价)。比如现在我知道乘客坐的是 CZ323 加上 CZ305 这两个航班,我只需看看 CZ305 航班的出发城市,就知道 CZ323 这段是到达的什么地方。CZ305 是 CAN-AKL 的,没有经停的航班,那反过来就可以推断 CZ323 这段是 PEK-CAN 的。简单的说就是前一段的到达地和下一段的出发地要一致

问题描述完毕,我们要做的就是,给出一组航班号,推断出可能的行程(航班数据是给定的)。

Read on →