What have you found for these years?

2010-05-06

async thin (open-uri vs em-http) (2)

em-http would be 10 times faster than open-uri
(actually, it's max([time(requests)]) vs sum([time(requests)])

url = 'http://graph.facebook.com/spellbook'
times = 10
require 'open-uri'
require 'em-http'
require 'async-rack'

run Builder.new{
use ContentType
map('/async'){
run lambda{ |env|
multi = EM::MultiRequest.new
times.times{
multi.add(EM::HttpRequest.new(url).get)
}
multi.callback{
env['async.callback'].call [200, {}, [
multi.responses[:succeeded].first.response
]]
}
throw :async
}
}
map('/sync'){
run lambda{ |env|
s = nil
times.times{ s = open(url).read }
[200, {}, [s]]
}
}
}

...燒掉了

剛剛碰好大一聲,整個螢幕就黑掉了。那一瞬間有點傻掉,
不過其實沒有非常緊張,不知道是因為碰上真正問題時通常會冷靜,
還是真的覺得應該不是螢幕燒掉?

低頭看了一下延長線,燈全暗的,再看一下電腦,看起來沒事。
想想應該是這條延長線燒掉吧?不過這條看起來沒那麼糟啊?
再看看桌上另一條延長線,再看看 hinet 的光纖機有沒有在發亮?
...我看應該是這條延長線燒掉才對吧?畢竟他很老了...

稍微靠近一點,有一點焦臭味,應該沒錯吧...

總之最不能燒掉的是硬碟,其他勉強還說得過去吧...
音樂還在播,所以喇叭應該沒事,其他系統則完全是另一條線路,
應該都沒事。拿比較新的那條延長線到其他插座試了一下,
應該是正常的。接下來就是拿他來取代燒掉的那條,
拿走時稍微聞了一下,噁...

電腦接上,螢幕接上,看起來確實是沒事。
不過我螢幕的 LED 燈看來真的是壞了。
原本就怪怪的了,會閃,現在就完全熄滅...
這該不會其實就是燒掉前的徵兆吧?我卻覺得只是壞掉了?
大概也無從證實了

接上硬碟,乍看之下正常,不過還是稍微掃描一下..
最近看要不要弄個 1.5T, 來做兩份備份。
有錢一點還要異地備份,其他地方也放一顆...

倒是燒掉那條延長線,上面還有保險絲哩。
但轉開來一看,我是沒看過保險絲啦,但看起來完全沒事 @@

-

早覺得不能這樣插這麼多東西 ><
偏偏插座就只有這樣幾個,有啥辦法 :/

async thin (or em'ed rainbows)

async thin:
~> ab -n 100 -c 10 http://127.0.0.1:9292/
Concurrency Level: 10
Time taken for tests: 10.939 seconds
Complete requests: 100

use ContentType
run lambda{ |env|
Thread.new{
sleep(1)
env['async.callback'].call [200, {}, ["OK\n\n"]]
}
throw :async

# sleep(1)
# [200, {}, ["OK\n\n"]]
}

sync thin:
~> ab -n 100 -c 10 http://127.0.0.1:9292/
Concurrency Level: 10
Time taken for tests: 100.295 seconds
Complete requests: 100
use ContentType
run lambda{ |env|
# Thread.new{
# sleep(1)
# env['async.callback'].call [200, {}, ["OK\n\n"]]
# }
# throw :async

sleep(1)
[200, {}, ["OK\n\n"]]
}

[ANN] rest-graph 1.0.0 released

= rest-graph 1.0.0
by Lin Jen-Shin (aka godfat-真常)
godfat (XD) godfat.org

== LINKS:

* github
* rubygems
* rdoc

== DESCRIPTION:

super simple facebook open graph api client

== FEATURES:

* simple graph api call
* simple fql call
* utility to extract access_token and check sig in cookies

== SYNOPSIS:

require 'rest-graph'

# every option is optional!!
rg = RestGraph.new(:access_token => '...',
:graph_server => 'https://graph.facebook.com/',
:fql_server => 'https://api.facebook.com/',
:accept => 'text/javascript',
:lang => 'en-us', # this affect search
:auto_decode => true, # decode by json
:app_id => '123',
:secret => '1829')
rg.get('me')
rg.post('feed/me', :message => 'bread!')

# for rack
lambda{ |env|
rg = RestGraph.new(:app_id => '123', :secret => '1829')
rg.parse_rack_env!(env) # auto save access_token if sig checked
rg.access_token # => ...
rg.data['uid'] # => facebook uid
}

# for fully blown cookies hash
rg = RestGraph.new(:app_id => '123', :secret => '1829')
rg.parse_cookies!(cookies) # auto save access_token if sig checked
rg.access_token # => ...
rg.data['uid'] # => facebook uid

# for fbs in cookies
app_id = '456'
rg = RestGraph.new(:app_id => '123', :secret => '1829')
fbs = cookies["fbs_#{rg.app_id}"]
rg.parse_fbs!(fbs) # auto save access_token if sig checked
rg.access_token # => ...
rg.data['uid'] # => facebook uid

# fql query
rg.fql('SELECT name FROM page WHERE page_id="123"')

== REQUIREMENTS:

* tested with MRI 1.8.7 and 1.9.1
* gem install rest-client
* gem install json (optional)
* gem install json_pure (optional)
* gem install rack (optional, to parse access_token in HTTP_COOKIE)

== INSTALL:

> gem install rest-graph
# or if you want rails plugin and bleeding edge
> script/plugin install git://github.com/godfat/rest-graph.git

== LICENSE:

Apache License 2.0

Copyright (c) 2010, Lin Jen-Shin (aka godfat 真常)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

篠房六郎短篇集

總算一鼓作氣看完了。每次要看時都覺得很疲倦了,
然後才看到第一頁滿滿的字,講的話又常常拐彎不知道拐到哪了,
內容又往往有相當程度的黑暗,就覺得實在太累看不下去.....

最後又有個後記說「所謂漫畫,只要把一堆衝擊性強又搞不懂在說啥的
圖像,塞進去就八九不離十了,...」讓我有種受騙感。而這篇什麼
「乖小孩的製作方法」也確實是相當不知所云。雖然不覺得算很差,
但相較篠房六郎的其他篇,不知道能不能說畢竟是出道作呢?

第二篇「生化武器 鈴木」,我很草率看過去了。或許可以說是我看過裡
最覺得無趣的一篇。不過後記上面寫到,這篇還是成年前畫的...
就覺得這樣說來還是相當厲害啊。只是這篇的風格覺得跟其他的真的
差很多。太過寫實了,哈哈哈。

最後一篇又是「空談師」後記說是「空談師」的前身。但我感覺不太出來
兩者有什麼關係。題材是一樣的,但內容好像沒關聯。相較起來,這前身
實在好懂太多了。後來的空談師實在是相當不知道他想說什麼...
所以或許我比較喜歡這篇吧(笑)。這篇可能也是少數講得很具體的。
不過令我吃驚的是,這篇雖然是這本短篇集裡,最後畫完的,
但卻居然是大學畢業前畫的啊。以大學生而言,這太超水準了 @@
但這也是在說,他大學時就在商業雜誌上畫漫畫了。而且還是從進大學後
才開始畫的?真是這樣也覺得實在有點恐怖。再之前應該是學美術的吧?
不管是不是都有夠厲害就是了 orz

-

剛看完時其實心情很差。不過他後記寫得倒是頗歡樂的––相較漫畫本身。
看完到寫這篇中間的時間拉太長了,我想不起來那時的心情。
如果用理智回想的話,大概就是好多的茫然吧。我想...
活在自己片段不完備的幻想裡,或許也是一種方式吧。
那麼篠房六郎的漫畫會是很好用的工具(笑)

-

練習不要打太多重複的字和重複的詞和贅字和贅詞 (metajoke)
不過我真沒想到,一眨眼就好多天沒寫什麼了。某方面而言或許也是好事

2010-05-02

scala type system (1) covariance and contravariance

covariance and contravariance
那麼當我們說以下,又是什麼意思?

Animal animal = new Cat();

這其實就是 covariant, 意思是當我們要的是某一個 type (Animal) 時,
我們能夠給他一個相等於這個 type (Animal) 的東西,或是比之更小的
type (Cat). 就是假設這邊我們可以寫:

A a = new B();

則可以得知 B <= A

反過來說,contravariant 就是當我們要某一個 type, 假設是 Cat,
我們必須給他一個大於等於 Cat 的 type, 就是 Cat 或 Animal.
這邊比較抽象一點,因為在 Java 裡面,可能看不到這樣的東西。
我不確定有沒有,因為沒有很熟悉 Java. 但在 C++ 裡,好像也沒有?
Scala 裡則很明確告訴你有這樣東西。我們可以簡單歸納成,
在 return type 裡,可以存在 covariance, 而在 parameter type
裡,則可以存在 contravariance.

在上面的例子就是 new Cat() 本身可以當成一種 function,
是一個 Unit -> Cat 的 function. 而前面寫 Animal animal =
則是表示我們要一個 Animal.

在 Ruby 裡,可以很明確看成是一種 function, 因為我們會寫成這樣:

animal = Cat.new

在 Scala 裡,如果 Cat 是一種 case class 的話,也可以寫成:

val animal:Animal = Cat()

我會建議盡量用這種形式來寫,避免使用 new operator,
這樣才比較像是 functional programming. Python 也是直接用 Cat()

-

那麼哪裡會有 contravariance 呢?在這之前,先把其他 covariance 的例子看完。
另外還有兩個例子,其中一個是具有 covariant return type 的 method,
是否能夠具備 override 原本 method 的資格?考慮以下:

Animal popup(){ return new Dog(); }
Cat popup(){ return new Cat(); }

第二個 method 是否能夠 override 第一個 method?
在 C++ 和 Java 1.5+ 的答案是可以的。在 Java 1.4- 則不行。
可以這樣做的原因也非常簡單,就跟 Animal animal = new Cat() 的
理由完全相同。考慮以下:

class Ground{ Animal popup(); }
class Grass extends Ground{ Cat popup(); }

Animal animal = ground.popup();

無論那個 ground 的實際 class 是 Ground 或是 Grass,
反正 popup() return 回來的一定是 T <= Animal,
那麼是否真的是 Animal 有關係嗎?因此 overriding method
的 return type 可以是 covariant. 而這樣有個好處,
當你很清楚你在使用 grass 時,就可以獲得 cat 的能力,
而不會受限於 Ground 對 Cat 一無所知。

Cat cat = grass.popup();

-

第三個 covariance 的例子是,如果我們有一個 list of animal 的
pointer/reference/variable, 他可以指向 list of cat 嗎?

ArrayList<Animal> animals = new ArrayList<Cat>();
animals.add(new Cat());
animals.get(0).sleep();

可乎?在 Java 裡不行,但在 Scala 裡面可以。先不談為什麼可以,
先看為什麼這樣做沒關係?在 ArrayList<Animal> 裡,type parameter 是 Animal,
意味著在 add 時我們是用 Animal 去接受 Cat, 這邊是 covariant,
就算在 Java 裡也是可以。然而 get 時,這時得到的也是 Animal,
因為 animals 的知識僅止於 ArrayList<Animal>.
那麼我們當然可以把 Cat 當成 Animal 來用,不是嗎?那為什麼要禁止:

ArrayList<Animal> animals = new ArrayList<Cat>();

呢?那又要怎麼讓這樣的作法也同樣符合 covariance 的規則?
那就是如果 Cat 是 Animal 的 subclass, 那 ArrayList<Cat> 也
應該要是 ArrayList<Animal> 的 subclass.
這在 Scala 裡,是這樣定義:

class List[+T]

如果這邊只寫 T, 那表示這是 invariant 關係,跟 Java 一模一樣。
但如果這邊是寫 +T, 那就表示如果有一個 type 叫 U,
而 T 跟 U 的關係是 T < U, 則可以得到 List[T] < List[U]
Cat < Animal, 因此 List[Cat] < List[Animal],
也因此以下是合法的:

val animals: List[Animal] = List[Cat]()

反過來說,contravariance 就是 -T 了。
如果有 Something[-T], 且 T < U, 則:
Something[U] < Something[T]
這個乍看之下很不合理,且聽我娓娓道來....

-

先整理一下有哪些地方會發生 covariance.
1. LHS, left hand side (等號左邊)
2. overriding method return type
3. type parameter (generic)

contravariance 比較少見,大概只有上面的 2~3 這兩個例子,
只是 2 要把 return type 改成 parameter type, 也就是:

2. overriding method parameter type
3. type parameter (generic)

然而 2 就算在 Scala 裡面也是不能用的,我不清楚原因,
或許作者覺得加這個下去會使得程式變得過分複雜,
也使得 overloading (ad-hoc polymorphism) 變得比較弱。

考慮以下的例子:

AnimalUtil:
Int mood(Cat cat){ return cat.life() + cat.mana(); }

DerivedAnimalUtil:
Int mood(Animal animal){ return animal.life(); }

別在意名稱,一時想不到要叫什麼,隨便亂打的。問題是下面的 method
是否可以 override 上面的 method? 考慮我們什麼時候會需要
virtual function:

AnimalUtil util = new DerivedAnimalUtil();
util.mood(cat);

這邊無論如何,因為 AnimalUtil 的 mood 的 parameter type 是 Cat,
一定得傳一個 cat 進去。那麼就算 mood 實際上是呼叫到 DerivedAnimalUtil 的
mood, 其 parameter type 是 Animal, 那麼就只是在做這件事而已:

Animal animal = cat;

然後在 mood 裡面就把 cat 當成 animal 來用。
因此我們可以說,對於 parameter type, 我們可以利用 contravariance.
也就是說,隨著 T 變小,return type 可以一起變小,
而 parameter type 則可以變大。這也是為什麼 Scala 裡的
Function1 的定義是 Function1[-T, +R].

至於為什麼 parameter type 不能是 covariant 的?
考慮以下例子,把上面的例子反過來:

AnimalUtil:
Int mood(Animal animal){ return animal.life(); }

DerivedAnimalUtil:
Int mood(Cat cat){ return cat.life() + cat.mana(); }

可否讓下面的 method 去 override 上面的 method?

AnimalUtil util = new DerivedAnimalUtil();
util.mood(new Dog()); // BOOM!!

由於 AnimalUtil 是吃 Animal, 把 Dog 傳進去當然合理了。
但 DerivedAnimalUtil 卻是要吃 Cat, 這樣就爆炸了。

-

在 Scala 裡,這種 parameter type 是 contravariant 的 overriding,
實際上是不能用的。因此在 Scala 裡,真正會看到 contravariance 的地方,
只有上面提到的 3. type parameter (generic), 也就是上面提到的
Function1[-T, +R]

這個狀況大概可以用這個例子表達:

val f: Function1[Null, Any] = (any: Any) => null
f(null) // => null

就是說我們要的 function 是 Null -> Any,
但是實際上指到的 function 卻是 Any -> Null, 這是合法的。
先假設左邊的 type 是 T = Null -> Any
再假設右邊的 type 是 U = Any -> Null
那麼如果我們能寫 T = U, 則表示 U <= T

假設以下縮寫:
T 的 parameter type 是 PT (Null)
U 的 parameter type 是 PU (Any)
T 的 return type 是 RT (Any)
U 的 return type 是 RU (Null)

再根據 return type 是 covariant, parameter type 是 contravariant,
則可推論:

U <= T
PU >= PT
RU <= RT

代回這邊的例子,則得到:

Function1[Any, Null] <= Function1[Null, Any]
Any >= Null
Null <= Any

可以發現確實是對的。也就是說,Function1[Any, Null] 是
Function1[Null, Any] 的 subclass.
parameter type 變小了,而 return type 則變大了。
正好就可以看出是 Function1[-T, +R]

f 是一個只能吃一個 null 的 function, 他會回傳 any
而實際上的 function 是吃 any, 但 null 當然屬於 any,
所以沒問題。而回傳了 null 回去,null 也是一種 any, 也沒問題。
因此實際呼叫:

f(null)

就會給一個 null, 對兩邊而言,都是完全 type check 的。
無論我們要吃什麼 argument, 都有可能會吃到 null,
簡單地說就是這麼一回事了。在 Java 裡這件事叫做例外,
在 Scala 裡,這件事則可以被一般的 typing rule 說明,不需要例外。

雖然說在 Scala 裡是這樣哩...

null.isInstanceOf[String] // => false

也許現實並沒辦法做到這麼理想吧? XD
倒是可以這樣做:

null.asInstanceOf[String] // a String, but the value is null

-

[全劇終]

往後延長

真的很糟,感覺真的是睡前才覺得比較輕鬆啊。
然後就捨不得睡,想說看點娛樂性的東西...
就好像只要不去睡,就會無中生有一些時間出來。
這段時間都是賺到的...

但時間當然不會無中生有,所以就變成整個往後延長一截...
整個生活節奏就是往後不斷拉長,像是一天有 30 小時,
然後一週其實只有 5 天。

但其他時間也沒有真的在做什麼正事耶。

google analytic

2009-04 ~ 2010-05

godfat.org
browsers:
1. Firefox 46%
2. Safari 24%
3. Chrome 17%
4. Internet Explorer 9% (woot!)

OS:
1. Windows 48%
2. Macintosh 42%
3. Linux 9%
4. iPhone 1%

sources:
1. (direct) 40%
2. google (organic) 9%
3. github.com (referral) 7%
4. blogger.com (referral) 6%
5. andaudio.com (referral) 5%

keywords:
1. godfat 55%
2. 飽和脂肪星 8%
3. 飽和脂肪 6%
4. "godfat" 3%
5. github godfat 3%

blogger.godfat.org
New Visitor: 55%
Returning Visitor: 45%

Loyalty
1 time: 55%
200+ times: 15%

browsers:
1. Firefox 47%
2. Internet Explorer 26%
3. Safari 12%
4. Chrome 11%
5. Mozilla 4%
6. Opera 1%

OS:
1. Windows 68%
2. Macintosh 22%
3. Linux 10%
4. FreeBSD 0.1%
5. iPhone 0.1%
6. iPod 0.03%
7. Android 0.03%
8. Nintendo Wii 0.01%
9. SunOS 0.01%

screen resolution:
1. 1280x800 25%
2. 1024x768 20%
3. 1280x1024 15%
4. 1680x1050 9%
5. 1920x1080 3%

sources:
1. google (organic) 60%
2. (direct) 13%
3. blogger.com (referral) 10%
4. friends.roodo.com (referral) 4%
5. google.com.tw (referral) 3%

keywords:
1. llvm 3%
2. 星之一角 3%
3. git rebase 3%
4. godfat 2%
5. git gui 2%

小結:
受歡迎的內容似乎還是程式為主,而且以 git 和 llvm 為多。
但我 llvm 沒講過什麼耶.. 也只有一兩篇而已。
其次應該是遊戲的,一堆 pet society 的搜尋...
其他一堆零碎的。
總而言之就是有點意外,但不會超過太多這樣吧。
偶爾會有一兩篇超受歡迎,大概就是被轉貼哪裡之類的?

最有趣的還是 browser 啦!哈哈。

scala type system (0) unit, top, bottom, and null

這算是我最喜歡 scala 的部份,所以想試著解釋這些東西。
以下假設讀者有基本的 object-oriented programming 概念,
或是有基本的 Java/C++ 的 OOP 經驗。怎麼樣算是基本的 OOP 概念?
只要知道 virtual function, dynamic binding 就行了。
嗯,dynamic dispatch 似乎也是類似的意思。
總之大概就是這些概念,名詞混亂也不是一天兩天的事了,別在意...

-

先看最簡單的 top (⊤)
簡單地說,就是 Java 裡的 Object, Ruby 裡的 BasicObject (1.9),
Object (1.8). 在 Scala 裡則是 Any.
在 C++ 中,基本上沒有這種東西,真要說的話,void* 勉強算是
pointer 裡面的 top. 然而 void 本身跟 top 無關,卻是 unit.
這邊先不談 unit, 因為他的概念雖然簡單,有些時候卻容易混淆。

之所以叫做 top, 因為他是整個 type system 中最上層的 class (type)[0],
也是最「大」的一個 type[1], 也就是說如果能夠比較大小的話,
top 比任何的 type 都大,因為他假設最少,可能的值最多。
任何一個值(value)都屬於(classified by) top.

在 Ruby 中,也把繼承(subclass, extends, inheritance, whatever)
寫成 class Derived < Base; 當然這並不見得是在表達大小,
然而 Fixnum < Object 卻真的是 true, 表示 Fixnum 是 Object 的
subclass, 也是在說 Fixnum.ancestors 中,Fixnum 會在 Object 的
前面。以下是 Fixnum 的線性繼承體系,最上層(top)就是 BasicObject.
=> [Fixnum, Integer, Numeric, Comparable, Object, Kernel, BasicObject]

而在 Java 裡,當我們說:

Object obj = new String();

意思是 String 是一種 Object, 如果我們所需要的 value 是一種
Object 的話,事實上並不需要關心實際的 value 是什麼,只要他能夠
被 Object classified, 也就是說所有 Object 該有的性質,
String 都有的話,那就不需要關心實際上確實是什麼東西。
同時這表示如果違反 LSP, 就是違反了這樣的「性質」。
使得實際上在 type system 中比較大的 type, 並不是真的比較大。
很可能他們本身並沒有實際的關聯,不是上下關係,而是左右關係。

-

接下來看 unit, 也就是一個什麼東西都沒有的 singleton pattern
中的 singleton. 在 Haskell 和 Scala 中,都寫成 ()
以 C++ 來看,大概可以寫成:

struct Unit{
  static Unit const& unit() const{
    static Unit unit; return unit;
  }
};

這樣的東西看起來一點意義都沒有,不過如果沒有他,type system
就會變得不完整。比方說 function 一定是一個會回傳某個值的東西,
但有時候我們在具有 side-effect 的 function 中,並不在乎
回傳的東西是什麼,因為我們已經在 side-effect 中達到目的了。

printf("Hello, World!");

當然 printf 可以有回傳,例如一個整數表示實際上 output 了幾個
character. 但大部份的情況下並不在乎這種事。這時候在 C/C++/Java
中往往會把 return type 寫作 void, 表示這個 function 沒有
return value. 但這樣使得 function 本身存在一種特例,並不是一件好事。
我們會希望一個系統能盡量簡單,盡量一致,減少例外,才會一個漂亮好看的系統。
也不需要「背」太多例外。

因此可以把 void 看成是 unit. 這點就是讓人混淆之處了,
void 是虛無的意思,但是 unit 卻是某個東西的意思。
在用到 unit 的 programming language 中,我們不會用 void
表示沒有 return value, 而是用 unit. 因為得到 unit
就表示我們得到了一個沒有用的東西,也就是說,我們關心的事,
在這個 function call 裡的 side-effect 就已經處理了。

而在 C/C++/Java 裡,我們有時候也會在 return type 是 void 的
function 中寫 return; 這又表示什麼??我剛學的時候,
真的覺得很混淆,不是說沒有 return, 那這 return 是 return 什麼?
如果這邊不是用 void, 而是用 unit 的話,那就很清楚了。
事實上 return; 就是 return unit; 的意思。
可以把 return; 想成是 return Unit::unit(); 的縮寫。

當我們有一個 type 的洞在那裡,不知道要填什麼進去時,就選 unit 吧。

-

here comes the dragon. 最抽象難懂的 bottom (⊥).
相對於 top, bottom 是最小的 type, 位於繼承體系的最下端,
是所有 class 的 subclass, 他的 value(instance) 會擁有所有
其他 class 的所有東西––而這樣的東西並不存在。

因此在 Scala 中,bottom 叫做 Nothing.
我們會用 Nothing 來表達一個 function 真的不會 "return",
因為 bottom 並沒有一個值,因此不可能找到一個值真的去 return.
這跟 void 是完全不一樣的,void 只是說我們對那個 return value
不感興趣,但他還是會 return, 程式還是正常在執行。
那什麼時候真的沒有一個 return value?

1. exception 發生了。這時我們絕不會說這個 function 真的
"return" 了,因為這是某種形式的 goto. 這邊不多討論 exception.

2. 這個 function block 住,永遠結束不了。
例如裡面有無限迴圈,或是無窮遞迴。

在這兩種情況下,我們都會說這個 function 實際上還是 return 了
一個東西,而這個東西其實是 "bottom", 所有 type 的 subtype.

考慮一個 Java method:

String toString(){ ... }

因為 halting problem, 我們無法驗證 toString 到底會不會
terminate, 因此實際上這個 String 是有可能永遠不會 terminate,
也就是說他有可能會回傳 "bottom". 那要怎麼讓這個式子合法?

答案就是因為 Nothing 是所有 class 的 subclass,
那 bottom 這個不存在的 value, 當然也是 String 的一種 instance.
就像是這邊 toString 其實是回傳 SuperString 的 instance,
都是 String 的 subclass 的 instance.

同時因為 Nothing 是所有 class 的 subclass,
也意味著所有的 function/method 都有可能永遠不會 terminate,
或是遭遇 exception, 也就是都有可能回傳 bottom.
bottom 之所以抽象難懂,是因為他不存在,不是一個具體的東西。
反之 unit 則是具有唯一的值,是非常具體的。

另一方面,在 Haskell 中有 undefined, 很類似 bottom,
只要 runtime 一看到 undefined, 程式就會壞掉。
所以可以用 undefined 來表示 bottom, 反正 bottom
也表示著永遠不會真的碰到的東西。

-

事實上在 Java/Scala 中,除了上述的 bottom 外,
還有一個可能會在任意 function/method 中回傳的值,
那就是 null. 在 Java 中,其實這樣寫是很怪的:

Cat cat = null;

為什麼 Cat 可以指向 null? 在單純的 OOP 中無法解釋,
這只能當做是一種例外。但如果 null 的 type 是 Null,
而且他也是所有 class 的 subclass 呢?那麼 null 本身
當然也是任何 class 的 instance, 也可能會被任何
function return 出來––除了 return type 是 bottom 的以外。

Nothing this_is_a_function_never_returns();

由於這邊也不允許 null 被 return, 所以 Null 並不是真的
所有 class 的 subclass, 他在 Nothing 之上,
其他所有的 class 之下。以 Scala 為例,大概是長這樣:

  Any
/ | \
A B C
\ | /
Null
|
Nothing

箭頭不方便畫,總之大家都知道方向。這在數學上大概就是
partially ordered set, 簡稱 poset.
提這個其實沒有特別的意義,只是想說如果能在數學找到可以對應的東西,
就能拿在數學上有嚴謹定義,並推導出來的性質來用。我們本來就是透過
尋找相似的東西,來推論出不熟悉的東西,可能會有什麼樣的外在與內在。
就像我們看到人,就會拿過去認識的人的經驗,來判斷這個人有什麼可能性。
而我們怎麼知道我們看到的是人?當然也是根據過去對人的認識來推論的...

扯遠了。所以 Null 有個唯一的值,也就是 null, 跟 unit 一樣是個
singleton. 不一樣的地方在於,unit 就只是 unit, 硬要說,
他只繼承了 top, 是 top 的 subtype, 沒有其他性質了。

Cat cat = null;
cat.sleep();

這邊第二行應該會有 null pointer exception,
但當初 compile 為什麼能過?因為 null 也是 Cat 的 instance,
所以 Cat 擁有的 method, null 當然也有。
而造成的 null pointer exception,
表示 Null override 了 Cat 的 sleep method,
而實作的內容就是很單純地丟出一個 null pointer exception.

我們可以說 null 擁有所有的 method, 但是這所有的 method,
實際上只有一個實作,就是 throw null pointer exception.
在 Ruby 中,這件事非常容易做,就是在 NilClass (nil(null)
的 class) 上實作 method_missing, 在裡面 raise nil pointer
exception 就行了。但在 Ruby 裡不會這樣做,因為他不是
static typing 的語言,因此不會刻意去遵守這樣的規則。

就算 Java 本身並沒有 Null, 也沒有所有 class 的 subclass 的
概念,在 JVM 底層倒是可以考慮用這種想法去實作。儘管我不知道
實際上有沒有 JVM 用這種概念去做。至少 Scala 本身是用這種概念。
或是說這本來就是 high level 的概念,VM 本身不需要知道這種事。

-

下集講 covariant and contravariant,
Scala 的 List[Nothing] 和 Function1[-T, +R]

-

[0]
這邊有時候 class 跟 type 混著講,因為在 OOP 中,
class 也是一種 type. (duck typing 例外),
因此有時候講 subclass 和 subtype 的意思是一樣的。
老實說,在口語中要講得很精確,真的是不容易...

[1]
Ordered Sets

2010-05-01

2010-05-01

今天一整天處於沒力的狀態.. 連要怎麼描述這狀況都懶
關於之前一直想寫 scala type system 的文章,
寫到一半有點不行了。如果現在打一下 guild wars 呢?
好累,而且根本就有無數多該做的事情沒做...

仔細想想,或許原因出於覺得有太多事是應該做而沒做的。
儘管儘管,我也已經有太多太多東西是拒絕去看的了
有某種進退兩難的感覺

今天要是繼續這樣的話,等會可能就關機去睡了...
然後大概半夜就會睡不著了吧 :/

五月了??

剛剛忽然間看到現在是五月一號了,
那一瞬間好像忽然間就過了五個月...
然後才驚覺自己好像一直覺得現在是 2009
儘管我日期寫 2010 已經很久了。

這種感覺真的有點可怕。
而且可以想像以後應該會覺得更恐怖吧....... :(

欸,可能還是不要想這種事比較好
乖乖過好現在這一分鐘就好了...

All texts are licensed under CC Attribution 3.0