What have you found for these years?

2011-11-06

人類、機器、與程式設計者

人類、機器、與程式設計者——談取捨與設計原則

似乎大多數的設計,都是在一連串的元素中選擇。有的元素可以同時被選擇,
有的則互斥導致無法同時被選擇。就像選了白色還是可以選圓形,但選了白色
就不能選黑色了。這是一個顯而易見的例子,然而,在大部份的情況下,元素
與元素之間是否互斥,卻不是這麼明顯。

一個很簡單的例子即是程式裡時間與空間的效能抉擇。一個爛的程式當然能夠
又肥又慢,而一個好的程式當然能夠又小又快。可是這種最佳化卻有著極限在,
到了一定程度後,要再更快就得犧牲體積,但那又不是說要犧牲就真的能犧牲
換到的,很多時候企圖要去換,體積犧牲是犧牲了,速度上卻也不見得真的
比較快。

我想,這種微妙平衡的拿捏,可能是一個永遠沒有解的問題。因為在不同的
情況下,我們本來就會有不同的需求。因此沒有給定一個特定的情境的話,
我們本來就沒辦法評估這樣的拿捏究竟是好或壞。不幸的是,情境這種事不僅
很難說清楚,還會不斷根據情況不斷改變。比方說,我們的硬體愈來愈好,
於是軟體可以更大量地「濫用」資源來換取其他以前不敢奢求的東西——
例如圖形介面。

為什麼我們需要圖形介面?

在談這件事前,先來看看軟體的使用對象有哪些。這邊大略粗分成三種,也就
是本文標題的三種——人類、機器、與程式設計者。設計給這三種東西使用的
軟體,會有非常不同的設計準則。

- - -

電腦(機器)畢竟是人在用的,人類擅長與人類溝通,不擅長跟機器溝通。
這也是為什麼我們要疊床架屋,不斷在現有架構上再架一層抽象層(同步口譯),
使我們不必真的去學對方的語言來溝通。

人類懶惰、容易犯錯、又善變。如果我們溝通的對象死板又不允許任何錯誤,
那溝通起來會異常辛苦。所以設計給人類使用的東西,最好:

* 懂得舉一反三,不同的話可以有同樣的意思。例如我們因為懶惰可能會想用縮寫,
但這不代表用全名是錯的。

* 懂得容錯或是揣測原意,因為我們容易打錯字或是講錯話,說出來的話自己沒
意會到講錯了,重複講一次才意識到自己說的不是那個意思,很常見。

* 軟體本身改變的成本必須盡量低。因為我們善變,又不見得能一開始就真的
表達出自己的本意。

換句話說,除了軟體本身外,重點在於使用介面 (UI) 和使用經驗 (UX)

- - -

然而給機器用的軟體就不同了。與人類相反,機器勤勞、穩定、又不易改變(通常
也沒有學習能力)。因此機器與機器的溝通重點則在於協定 (protocol), 一個穩定
不會改變的介面。簡單地說,如果程式都沒有問題了,也沒有改變需求的話,就
不要去動他 :P

另一方面,要達到這種境界,同時也需要嚴謹的定義,不然我們要怎麼確定他真的
沒有問題了?或許可以很簡單地用 UNIX philosophy (雖然似乎跟 philosophy
無關) 來描述這件事:

Write programs that do one thing and do it well.
Write programs to work together.
Write programs to handle text streams,
because that is a universal interface.

但也不只是這樣。機器終究是給人用的,人用錯誤的方式操作機器終究會在某處
變成機器的錯誤,因此我們還是需要容許這部分的錯誤,問題在於我們要在哪一個
環節點(整個系統架構中的哪一個程式?)處理這個錯誤?

- - -

那麼給程式設計者使用的軟體呢?也就是 library 或是 framework 之類的軟體。
由於錯誤地使用 library 或是 framework 幾乎可以肯定一定是某個 bug, 而在使用
這些軟體 (library and/or framework) 時,目標軟體還沒被製作出來,因此我們
需要盡早知道這些錯誤的使用,才能避免 bug 流入最終軟體裡。

也就是說,這些軟體本身需要極為嚴謹,不容許任何錯誤。一個簡單的想法是,
我們必須用比較強的表達能力,去表達比較弱的東西,而不是反過來。因此我們
需要極為嚴謹的東西去做能夠容許錯誤的東西。

所以程式設計者其實是比較接近機器而不是接近人 (?)

而其實這個原則也同樣可以用在某些純機器使用的軟體中,正如同上面所提到的,
整個系統不單只是一、二層而已,我們也需要一些不容錯的軟體來協助製作容錯的
軟體。參考 fail-fast.

- - -

以前我在設計軟體時,總是試著拒絕不正確的操作。理由非常簡單,因為不正確
就是不正確,而且直接拒絕不正確的操作,對於程式而言比較容易撰寫。畢竟我們
需要考慮的條件比較少,核心程式可以假設輸入永遠都是正確的。

同時,因為我正是開發者,我希望盡早看到錯誤,在比較簡單的環境下注意到錯誤,
才比較容易找到出問題的原因,並將之修好。尤其我讀到 C++ 的 template 有所謂的
two-phase lookup, 更是讓我覺得盡早報錯是一件非常重要的事情。

不過死死板板一板一眼的軟體,應該是沒幾個人類會喜歡用。因為那強迫我們必須在
腦袋清楚的情況下使用。當然危急到人類性命安全的軟體等等除外,那還真的必須
一板一眼地強迫使用者想清楚。但一般工具性軟體,實在不會想要這麼聚精會神地使用。

另一個教訓是,我以前寫的一些東西,別人往往不太會用,因為其他人可能沒那麼清楚
設計這套軟體的脈絡。他們可能不知道這軟體要幹嘛,甚至也不知道他們自己要幹嘛...

- - -

總結來說,給人類使用的軟體,需要謹記人類懶惰、容易犯錯、又善變。不把這些事
放在心上的話,不容易設計出「好用的」軟體。而給程式開發者的軟體,也就是 library
與 framework 等等,則需要嚴謹,不能太過於容錯。給機器使用的軟體,如 OS 或
database 等等,則大抵上介於人類與開發者之間。

而這些東西,其實或多或少可以說是 Rails 教我的。

Rails 是個 framework, 但是他不嚴謹,有大量的容錯,大量的模糊地帶,違反我認為
framework 該有的性質。但是但是,程式設計者,難道真的不是人類嗎?其實我們也想
偷懶一下,隨隨便便,模模糊糊地描述我們要的東西。

我覺得這正是 Rails 帶給程式界很重要的一個精神。他之所以紅翻天,到處都有 clone,
成為許許多多其他 framework/library 的 inspiration, 終究是有其很重要的理由的。
儘管我不斷批評不斷抱怨其莫名其妙之處,但說穿了應該只是性格不合,或是說目的
不同吧。

就像很多人不能接受 Ruby 的隨便程度,而我則是不能接受 Rails 的隨便程度。

也就是說,雖然我覺得 framework 必須是嚴謹的,但不嚴謹也是能走出一片天。
應該是沒有絕對的法則吧,只有一般情況下的說法。重點在於,不同的設計準則,
會做出不同風貌的東西。

- - -

我後來設計的東西,如 rest-graph, rest-core, rest-more, rib, shere, 都努力地
把「人類」的需求考慮進去。東西畢竟是人要用的,過度強求「一致性」反而對人類
而言不方便,那可能或多或少就有點本末倒置了吧。

我想 Ruby 也是如此。Ruby 可是具有豐富的「不一致」呀...

對於這一點,我想我還是得感謝 Rails 讓我體會到這些。

2011-11-05

[ANN] shere 0.9.0 released

Shere

by Lin Jen-Shin (godfat)

DESCRIPTION:

Share the directory here with Nginx!

CHANGES:

shere 0.9.0 – 2011-11-04

Birthday!

INSTALLATION:

gem install shere

SYNOPSIS:

shere
shere --help
shere -p 8080 ~/public
sudo shere -u nobody

[ANN] rib 1.0.0 released

Rib

by Lin Jen-Shin (godfat)

DESCRIPTION:

Ruby-Interactive-ruBy – Yet another interactive Ruby shell

CHANGES:

Rib 1.0.0 – 2011-11-05

Bugs fixes

  • [more/color] Fixed a bug for displaying 1/0. Fixed #4, thanks @bootleq

Rib 0.9.9 – 2011-10-26

Bugs fixes

  • [more/color] Fixed Windows coloring support.
  • [more/color] Properly reset ANSI sequence.

Enhancement

  • [commands] Extract commands description under __END__ in the commands. Please read rib-rest-core as an example.
  • [rib] Always show original errors if anything is wrong.

INSTALLATION:

gem install rib

SYNOPSIS:

Screenshot

[ANN] rest-more 0.7.2.1 released

rest-more

by Cardinal Blue http://cardinalblue.com

DESCRIPTION:

Various REST clients such as Facebook and Twitter built with [rest-core][]

CHANGES:

rest-more 0.7.2.1 – 2011-11-05

Bugs fixes

  • [Facebook::RailsUtil] Fixed a missed change which should be made.

rest-more 0.7.2 – 2011-11-05

Incompatible changes

  • [Flurry] renamed api_key to apiKey to better match the original name from flurry. Also renamed access_code to apiAccessCode.
  • [Facebook::RailsUtil] Some module_functions are changed to controller’s private methods. You aren’t using it if you don’t know what does this mean.

Enhancement

  • Added rib-rest-core command line tool, extracted from rest-core.
  • [RailsUtilUtil] Introduced this to ease the pain writing RailsUtil for various clients.
  • [Bing] Added Bing client and its RailsUtil.
  • [Github] Added RailsUtil for Github client.
  • [Linkedin] Added RailsUtil for Linkedin client.
  • [Mixi] Added RailsUtil for Mixi client.
  • [Twitter] Added RailsUtil for Twitter client.

INSTALLATION:

gem install rest-more

Or if you want development version, put this in Gemfile:

gem 'rest-more', :git => 'git://github.com/cardinalblue/rest-more.git',
                 :submodules => true

SYNOPSIS:

require 'rest-more'

RestCore::Twitter.new.statuses('_cardinalblue') # get user tweets
RestCore::Github.new.get('users/cardinalblue')  # get user info

linkedin = RestCore::Linkedin.new(:consumer_key    => '...',
                                  :consumer_secret => '...')
linkedin.authorize_url!   # copy and paste the URL in browser to authorize
linkedin.authorize!('..') # paste your code from browser
linkedin.me               # get current user info

RestCore::Facebook.new.get('4') # get user info

See example for more complex examples.

[ANN] rest-core 0.7.2 released

rest-core

by Cardinal Blue http://cardinalblue.com

DESCRIPTION:

Modular Ruby clients interface for REST APIs

CHANGES:

rest-core 0.7.2 – 2011-11-04

  • Moved rib-rest-core to rest-more
  • Moved RestCore::Config to rest-more
  • Renamed RestCore::Vendor to RestCore::ParseQuery

INSTALLATION:

gem install rest-core

Or if you want development version, put this in Gemfile:

gem 'rest-core', :git => 'git://github.com/cardinalblue/rest-core.git',
                 :submodules => true

If you just want to use Facebook or Twitter clients, please take a look at rest-more which has a lot of clients built with rest-core.

2011-11-04

How to run Rails 2 on Ruby 1.9.3

I was complaining this on twitter, and ihower answered me.
(thank you so much! it saved me so much time)

Basically, just applied this pull request as a monkey patch in your
application, or you can see how I monkey patched this in rest-more:

# monkey patch from https://github.com/rails/rails/pull/3473
class MissingSourceFile < LoadError #:nodoc:
  REGEXPS = [
    [/^no such file to load -- (.+)$/i, 1],
    [/^Missing \w+ (file\s*)?([^\s]+.rb)$/i, 2],
    [/^Missing API definition file in (.+)$/i, 1],
    [/^cannot load such file -- (.+)$/i, 1]
  ]
end

This patch is so simple, simple enough that this shouldn't
break anything (except for some crazy hacks), and I don't think
Rails team should refuse to merge this simply because Rails 2
is considered frozen.



I am really so tired of Rails, and I don't feel Rails 3 is much better
regarding this kind of hacks. I still saw a lot of this error:

ActionView::Template::Error: A is not missing constant B!

This error didn't make any sense and it never pointed to the real place
where the constant was misspelled. Yes, misspelled.

I swear I would never use Rails in my "personal" projects.

Items which always makes me feel angry:
(still growing, and did I miss anything?)

* IE
* Rails
* Facebook

All texts are licensed under CC Attribution 3.0