Tony Han

Happy hacking

Page 2


125 days contributing on GitHub

如果本文有个中文标题,大概会是这样:在 GitHub 上连续 125 天有 contribution 记录是怎样一种体验?

先来简单说说这 125 天里一些自己认为值得一提的事,可能这样大家才可能比较感兴趣地往下看。

  • 在 elixir-lang/ecto(类似于 ActiveRecord) 有 22 次提交,排名第8。
  • 完成了 Qiniu SDK for Elixir 的主要开发工作。
  • 其他零碎的,比如 elixir-lang/elixir、elixir-lang/plug、grpc/grpc 等等。

这 125 天大致可以分为三个阶段。

开始

基本从年假前几天到年假结束,只是出于学习 Elixir 这门语言的原因,需要写点书上 demo 代码。根据我以往的习惯,会把这些代码扔到 GitHub 上,因为这样可能会增加一点 GitHub 的经验值?。当然,这次也是这么做了,不过坚持的还不错,整个年假每天都有看点书、写点代码。

年假过去后 Elixir 的基础学的差不多了,按照之前的习惯,GitHub 的 streak 就又要断了。不过幸好这次并没有,不然就不会有 125 天了。

投入

学了一个东西,总要用一下的,不然只是看个书,copy 一些书上的代码,其实跟没学差不多。于是我开始想要做点什么呢,干脆做个网站吧,毕竟 Elixir 还是很适合做 web 的,而且又有 Phoenix 这个比较像 Rails 的框架。于是就花点时间做了一个到现在也没部署、没公开的小网站。这个网站其实不是重点,重点是,做的过程中,我发现 Elixir 生态系统还不太完善。比如,Ecto 有一些可以改进或者 fix 的地方,以及没有能够上传文件到 qiniu 的 Elixir lib。可能这些困难会导致放弃或者想办法绕过去,但我标榜自己是个 hacker 嘛,作为一个 hacker,是应该能够通过更优雅或者更出乎意料的方式解决这些问题的?。

于是我去给 Ecto 贡献代码。虽然我是 Elixir 新手,只能做一些比较相对简单的事情,不过还是学到很多 Elixir...

Continue reading →


译:理解 Elixir 的宏-1

(原作者这一系列讲 Elixir macro 的文章写的非常好,我的认识还远远不够,所以在这里翻译一下,这是第一篇。翻译水平有限,见谅)

原文链接: http://www.theerlangelist.com/2014/06/understanding-elixir-macros-part-1.html

这是讨论宏(macro)的一系列文章的第一篇。我原本打算在之后要出版的 <Elixir in Action> 一书中来探讨这个问题,但又决定不这么做了。因为这个问题跟这本书的主题不太切合,这本书更关注于底层 VM 和 OTP 的关键部分。

于是我决定在这里探讨宏。个人而言,我觉得宏的问题很有意思,在这个系列里,我会试着解释它们是如何工作的,并对于如何写宏介绍一些基本的技巧和建议。尽管我确信写宏并不怎么难,但它确实需要一些比对一般 Elixir 代码,更高程度的认知,因此我认为了解 Elixir 编译器的一些内部细节是很有帮助的。知道幕后的事物是怎样运转的,能够让你对于元编程代码的思考更加容易。

这是一篇中等难度的文章,如果你对 Elixir 和 Erlang 比较熟悉,但对宏还是有一些疑惑,那么你找对地方了。如果你刚接触这两种语言,那最好还是先从其他的开始,比如 Getting started guide 或者干脆找本书来看看。

元编程

很可能你已经对 Elixir 的元编程有一些熟悉了,基本上就是,我们有一些代码,这些代码能结合一些输入来生成其他代码。

借助宏,我们能够写出像 Plug 里一样的结构

get "/hello" do
  send_resp(conn, 200, "world")
end

match _ do
  send_resp(conn, 404, "oops")
end

或者这个 ExActor 里的例子

defmodule SumServer do
  use ExActor.GenServer

  defcall sum(x, y), do: reply(x+y)
end

...

Continue reading →


How does Plug.Builder work?

(本文涉及的源码需要有一些对macro的了解才比较容易看懂)

上回说到,我们可以利用Plug,搭配Cowboy这个web server来写一个简单的web app。而实际中我们不可能把所有的处理逻辑都放在一个Plug中,代码既不容易维护,也不能重复利用Plug。

正如Plug的名字(插头)一样,我们可以把一个个的Plug连起来,组成一个功能强大的Plug pipeline。就像你到国外旅行,电源适配器接口不对应,就可以买个转换器(其实就是一个插头),一头插着你的电源,另一头插在酒店的插座上。 再比如Plug实现了Plug.Logger,把它“插在”在你的整个Plug的pipeline中,就帮你加入了日志的功能。

Plug.Builder的目的就是为了让你方便地写出Plug pipeline,我们还是先来看文档里的例子? :D

defmodule MyApp do
  use Plug.Builder

  plug Plug.Logger
  plug :hello, upper: true

  def hello(conn, opts) do
    body = if opts[:upper], do: "WORLD", else: "world"
    send_resp(conn, 200, body)
  end
end

MyApp就是一个组合了几个Plug的Plug pipeline,按顺序有Plug.Loggerhellosend_resp是从Conn引入的,plug这个macro显然是从Plug.Builder引入的,那具体是如何引入,以及为什么这样就可以定义一个Plug pipeline,秘密都在use Plug.Builder这行代码中。

Define plugs one by one

当以一个Module为参数调用了use这个macro(是的,use也是一个macro)时,其实最终只是调用了那个Module的__use__ macro而已。是的,还是macro ╮(╯▽╰)╭

我们来看Plug.Builder的__use_...

Continue reading →


Should we do what users need or what we require them to need?

到底我们应该做用户需要的东西,还是要做我们要求用户需要的东西?

看这个句式,很显然,我的观点是后者。因为对于这个句式,作者自己的观点都是后边那个。

我们常常说,一个好的产品要满足用户的需求。这句话说的没错,一个连用户需求都满足不了的产品,不管做的好不好,一定活不下去,一个活不下去的产品也不能算是一个好的产品。所以你看小米,人家现在活得多滋润,就是因为人家抓住了用户的需求,并且极大地满足了用户需求,确实是一个好产品。

当然,我不是小米的粉,讲小米只是为了铺垫一下。(好像谁都看得出来)

如果目标只是定在了“好”产品这一级,那苹果现在也不会这么牛了。当年诺基亚也挺牛的,满世界都是它新品的广告牌,N 系列的拿出去,又实用,又漂亮,又能满足用户装逼的需求,当锤子使也挺好用的。而现在,不也就做着做着就做死了么。就是因为有一群坚信“用户根本不知道自己需要什么”的人觉得,用户要什么我就给什么,太没意思的,一点都满足不了我的控制欲,而且他们相信自己比用户有更高的品味、更好的审美,以及更加确定的相信什么才是更美的、人们更加需要的,他们不想只是遵守游戏规则,而是想要自己制定游戏规则。所以才有了进步。

本文的主角也不是苹果,苹果在这里也只是一个论据。另一个论据是 [stackoverflow.com](stackoverflow.com) ,还更像是本文的主角。

我自认为自己,像大多数自以为是的程序员一样,坚信自己有独特的品味、不凡的追求。写文字必须是 Markdown ,Word 是什么,好像没听过,可能是来自远古的东西吧。只用 Google,把 baidu 当做 ping 的web版,哪怕要“跨过山和大海”。看的书、用的系统、甚至出来“混”的名号,都必须是英文的,即便我们和外国人讲英文除了寒暄几句就不知道讲什么了。但当我从开始一点点用 stackoverflow 之后,还是有一种被雷击中的感觉。

stackoverflow 是一个技术的问答社区,里边有对于各种基本乃至更高级问题的各种答案,有谁说过离了它可能都没办法写代码了。在 stackoverflow...

Continue reading →


How does Plug work with Cowboy?

Plug的文档里有个通过Plug写应用程序的简单例子:

defmodule MyPlug do
  import Plug.Conn

  def init(options) do
     initialize options

    options
  end

  def call(conn, _opts) do
    conn
    |> put_resp_content_type("text/plain")
    |> send_resp(200, "Hello world")
  end
end
 Run the server
$ iex -S mix
iex> c "path/to/file.ex"
[MyPlug]
iex> {:ok, _} = Plug.Adapters.Cowboy.http MyPlug, []
{:ok, PID<...>}

本文就来简单聊一下这寥寥几行代码是如何起作用的。

让我们从最后看起,Cowboy是Erlang写的一个Small, fast, modular HTTP server,有点类似于ruby里的thin的角色。Plug则实现了Cowboy的Adapter,使我们可以方便的写出一个web app。

Plug.Adapters.Cowboy.http MyPlug, [],其实Cowboy只是拼装了一些参数,并用它来调:cowboystart_http函数。最后拼好的这些参数是:

[MyPlug.HTTP,                 ref
100,                          default value for acceptors
[port: 4000],                 default value for options
[env: [
  dispatch: :cowboy_router.compile(dispatches),  important!
  compress: false],           default value
...

Continue reading →