2014/10/27

functional object in scala

通常認為 functional object 的特性,就是使用了 immutable object 的特性。

immutable vs mutable object

immutable objects 比 mutables obejcts 多了幾個優點, 但有一個缺點。

優點是

  1. immutable objects 比 mutable objects 容易理解,因為他們不會隨著時間改變。

  2. 可任意傳送 immutable objects,但如果是 mutable table,則可能需要複製一份,以防止被其他程式碼修改。

  3. 因為不能修改 immutable object 的內容,所以兩個 threads 不能同時存取一個 immutable object。這在 multi-thread 環境會很好用。

  4. immutable objects 可安全地用在 hash table key 裡面,才不會發生這種情況:當 mutable object 用在 HashSet,下次使用 HashSet 時,物件可能就不見了。

缺點是

  1. immutable objects 需要較大的一塊記憶體空間,進行物件複製。

製作一個 Rational Number Immutable Object

// primary constructor,需要兩個 整數,分別是分子與分母
class Rational(n: Int, d: Int) {

    // scala 會直接將 class body 裡面,不屬於任何 field/method 的程式碼
    // 直接放到 primary constructor

    // scala 限制更多, 只能讓 primary constructor,
    // 在第一行呼叫 superclass constructor

    // require 語法是 preconditions
    // 因為有理數的分母不能為 0,必須在建立物件時,加上欄位檢查
    require(d != 0)

    // 66/42 可約分成  11/7
    // 需要計算 gcd(66,42)   greatest common divisor
    private val g = gcd(n.abs, d.abs)

    // 外部可直接使用 r.numer, r.denom 兩個欄位的數值
    val numer = n / g
    val denom = d / g

    // auxiliary constructors
    // 5/1  原本要寫 Rational(5,1)   可省略成  Rational(5)
    // 任何 auxiliary constructor 都可以在第一行 呼叫其他 constructor
    def this(n: Int) = this(n, 1)

    // 有理數的加法,為了保持 immutable object 的特性
    // 運算後,回傳新的 Rational object
    // 定義 operator method
    def +(that: Rational): Rational =
        new Rational(
            numer * that.denom + that.numer * denom,
            denom * that.denom)

    def +(i: Int): Rational =
        new Rational(numer + i * denom, denom)

    def -(that: Rational): Rational =
        new Rational(
            numer * that.denom - that.numer * denom,
            denom * that.denom)

    def -(i: Int): Rational =
        new Rational(numer - i * denom, denom)

    def *(that: Rational): Rational =
        new Rational(numer * that.numer, denom * that.denom)

    // Rational 要處理 r*2 的運算,必須要對 * 作 overloaded
    def *(i: Int): Rational =
        new Rational(numer * i, denom)

    def /(that: Rational): Rational =
        new Rational(numer * that.denom, denom * that.numer)

    def /(i: Int): Rational =
        new Rational(numer, denom * i)

    // 列印物件時,會自動呼叫 toString,否則預設列印出物件的 reference 位址
    // 這裡是覆寫 override 上層的 toString
    override def toString = numer + "/" + denom

    // gcd 最大公因數 greatest common divisor
    private def gcd(a: Int, b: Int): Int =
        if (b == 0) a else gcd(b, a % b)
}

identifier in scala

有四種

  1. alphanumeric identifier
    或 a letter 開始, 後面可以是 letters/digits/,$ 也算是一個 character,但這是保留給 scala compiler 使用,建議不要使用 $ , 以免跟 compiler 產生的 id 有衝突。

  2. operator identifier
    包含 1~多 個 operator characters (ex: + : ? ~ # )
    ex: + ++ ::: <?> :->

  3. mixed ifentifier
    包含 a alphanumberic identifier, 後面是 , 然後是 operator identifier。
    ex: unary
    + myvar_=

  4. literal identifier
    用 (...) 包含在裡面的任意 string
    ex: x <clinit> yield
    yield 是 java Thread class 的 static method,但在 scala 不能寫 Thread.yeild(),因為 yield 是 scala 的保留字,所以要改寫成 Thread.yield()。

implicit conversion

上面的 Rational 可支援 r2,但是無法支援 2r 的計算,為了解決這個問題,scala 讓我們建立一個 implicit conversion,自動轉換 integer 為 rational number。

implicit conversion 定義有 scope 的限制,如果要在 scala interpreter 裡面使用,則必須在 interpreter 裡面執行 implicit conversion 的定義。如果把 implicit method 定義放在 Rational class 裡面,則對 inpterpreter 來說,是沒有作用的。

implicit def intToRational(x: Int) = new Rational(x)

測試

scala> val r = new Rational(2,3)
r: Rational = 2/3

scala> 2 * r
res0: Rational = 4/3

雖然 implicit conversion 很好用,但可能會造成程式可讀性降低。

scala 的程式特性是簡潔,但在簡化程式碼的時候,同時要考慮到 readable 與 understandable 的要求,才不至於寫出難以維護的程式碼。

2014/10/20

迷你書 阿里巴巴的技術力量

因應阿里巴巴在美國上市的重大消息,InfoQ 將一些對阿里巴巴工程師的專訪,集結成一本迷你書 阿里巴巴上市背後的技術力量,我們可以從這些互動的 FAQ 訪問中,了解到這個 公司的文化與態度。

阿里巴巴的目標,是要幫助大陸的中小企業,讓他們也能很快地使用網路平台進行銷售與交易,平台使用的關鍵問題在使用者,而且是關於賣方與買方雙方面的問題。

甚至連技術深度導向的核心業務優化團隊,同樣也要面對使用者,開發者必須要將自己的優化成果推廣出去,系統夠好也要有人使用,除非你開發的東西已經超過目前全人類的想像,已經開發到了未來。

在政治集權的市場中,建立互信金流機制

在阿里巴巴上市現場,記者提出對 trust 的疑問,阿里巴巴如何說服美國投資者,面對中共政權,如何讓投資者相信,這是一個可以被信任的企業。



馬雲說明,阿里巴巴為了提供一個讓中小企業可以使用的 ecosystem,必須跟中共政權對話,建立互信基礎,這是必要的,政府跟企業必須合作,才能在非常糟糕的信用卡市場中,完成一個網路交易平台。 Every trust takes time to build.

架構師的歷程

要成為一個架構師,更重要的學習是來自於實踐,所謂一個行業的專家指的並不是他能力有多強,是指他碰到過了這個行業裡面所有的問題,同時他解決了,他就能成為專家。

架構師不僅僅要了解技術,同時要進行業務分析,更全面地了解問題與方法,才能更容易去掌握問題的全貌。

淘寶的架構變革

  1. 2003/5 ~ 2004/5 LAMP
  2. 2004/2 ~ 2008/3 weblogic -> JBoss,開發了 TFS, iSearch, TDBM, CDN
  3. 2007/10 ~ 2009/11 系統走向產品化、服務化,支援大型團隊的並行開發,逐步模組化、中心化、可快速擴充、提昇可用性。非核心資料庫由 Oracle 移至 MySQL,建立訊息系統與服務框架,淘寶開放平台(TOP)上線
  4. 2009/8 ~ now 逐步提供系統自動化,減少操作失誤機率

資料庫的演進

阿里巴巴一開始是使用 Oracle,發展至今,已經慢慢地藉由拆分的方法,移轉到 MySQL,至於 NoSQL 則是依照業務的需求而使用,因為阿里巴巴的核心業務比較複雜,並不適合直接改用 NoSQL,而是在適當的業務場景中,採用 NoSQL。

當資料庫性能不足時,就會考慮使用 Cache,目前有集中式與分散式兩種 Cache 解決方案,也有使用 memory cache。數據資料由集中式演進為分散式,可跨多個 IDC 進行容錯備份,數據資料異地同步。

拆分資料庫有專責單位,最重要的任務是,如何在拆分的過程中,達成不間斷服務,平順地過渡到新架構上。第一次拆分最大的 table,花了兩年的時間,現在進步到2~3個月。

企業訊息系統

訊息系統對阿里巴巴來說,是非常重要的,但是阿里巴巴沒有直接採用既有的訊息系統,反而選擇以 KafKa 的概念,重新實作一個,最重要的原因是「系統維護」,因為阿里巴巴是使用 Java 開發語言,而 KafKa 是用 Scala 開發的。

訊息系統最重要的是要維護資料的一致性,目前的規模是每天處理百億個訊息,系統 Loading 在 4~5 左右。

前端性能優化的重點

  1. 減少 http request
  2. 減少 redirect
  3. preload 資源
  4. 盡量減少 cookie 的大小
  5. delay loading
  6. asynchronous ajax,減少 DOM 節點數,加快render與 first byte時間
  7. CDN 加速
  8. delay render
  9. pre-resolve DNS
  10. js 不放在 header,避免 blocking concurrent render tasks

系統必須根據網站的特點進行優化,每個網站的轉換率、流量與使用者的特性不同,必須根據實際的數據,判斷優化是否對轉換率及流量是有幫助的,並不是無止境的優化。

JVM 團隊

核心系統開發部一開始,是在集團內部尋找需求,優化後,得到十倍的效能提昇,漸漸地累積出優化成果,內部其他團隊就會自動找開發部協助解決效能問題。

優化的方式,並不是要求應用程式修改程式碼,而是直接針對 JVM 進行修改與調整。但如果是計算密集的應用,就要用調整演算法的方式處理。

因為有優化 JVM 的經驗的工程師不多,因此這個單位大多都是由應屆畢業生進行專業培養。

這個單位的 KPI 不單純只看優化的結果,重要的是要對優化結果,找到適當的應用,把成果推廣出去,但有時候會因為各種原因,而用不上你修改後的東西。

Open Source

雖然阿里巴巴將自身開發的一些軟體以 Open Source 的方式發布出來,但對於阿里雲飛天平台,企業認為這是阿里巴巴的核心業務與價值,因此沒有開源。

開源對阿里巴巴來說,並不是一個核心企業價值,阿里巴巴是個需要賺錢的公司,這些開源專案,都是因為跟某些原始專案的團隊搭配上的問題,才刻意 fork 並調整的專案,而這些修改,都是因應阿里巴巴公司規模成長業務需要而去做的。

2014/10/19

洞 - 路易斯.薩其爾

洞是一本很奇特的兒童文學小說,故事的大綱,可以到 洞 wiki page 查閱,奇特的地方是,小朋友跟我讀過後的感受有些不同,這是一部1999年得獎的作品,我相信作品會得獎,一定有它特別的道理。

小朋友讀完後,認為這是一部故事情節很刺激的小說,主角史丹利.葉納慈特殊的倒楣家族歷史,雖然故事的前半段,有點不知所云,主角被意外送入了綠湖營,但情節從最後一個洞那裡開始,就狠緊張。

我還特別問了他一個問題,那些洋蔥跟水是從哪裡來的,他很快就回答出來,是山姆種的,還說了「一吻奪命」凱蒂.巴婁跟山姆的關係,重要的是,他了解故事情節有些部份是回顧過去發生過的事情,這個回答確定他是看得懂這個故事的。

在我剛讀完的時候,我的第一個直覺是,這麼特殊的情節,有情殺案、偷竊、集中營,為什麼這樣的故事會得獎。唯一的亮點,應該址有史丹利跟零蛋兩個人發展出來的友情,最後互相幫助並存活下來的過程,這個部份也是整個故事中,比較正面的情節,其他部份都是在描寫黑暗面。

故事的脈絡,是簡單而且直接的,沒有什麼隱藏的內情,我想這也是小朋友能看得懂的一個重要的原因。簡單的脈絡中,包含了綠湖營裡面的團體生活,少年之間如何互相鬥爭,藉以獲取最大的生存利基。另外是凱蒂.巴婁跟山姆之間發生的故事,還有左若尼夫人的詛咒,這三個部份巧妙地融合在故事當中。

書本最後的折頁中,另外介紹了三本得獎的作品,其中第一本讓我驚訝了一下,因為書名是「嗑藥」,很明顯就是一個在討論青少年在青春期過程中,為了種種原因誤入歧途,選擇用藥品麻醉自己,最後發現自己該換一個方式與角度看世界的故事,而這樣的故事也可以取得兒童文學獎,我得考慮一下,才能想清楚該不該買來給小朋友看。

2014/10/6

Classes and Objects in Scala

Scala 特地將 object 從 class 中取出來,讓 object 直接作為 singleton object 的保留字,原本在 Java 中需要透過 Singleton Design Pattern 才能達成的工作,Scala 內建了 singleton object,我們不需要再去了解 Design Pattern,只需要專注在開發 object 內容的工作上。

static method

在 object 中定義可以使用的 method,實際上用起來就像是在 Java 呼叫 static method 一樣。通常我們會在撰寫 Utility 程式中,用到 singleton 與 static method 的想法去實做,畢竟使用公用程式還要先產生物件,會覺得非常麻煩。

object StringUtil {
    def trimHead(s:String) = s.dropWhile(_ == ' ')

    def main(args: Array[String]):Unit = {
        println(StringUtil.trimHead(args(0)))
    }
}

測試

>scala testscala.StringUtil "   test  22 "
test  22

companion object

如果在同一個 scala 檔案中,包含了同樣名字的 class 與 object 兩個定義,在 scala 就稱為 companion object。

class StringUtil {
    val s = "     I am a string in class StringUtil"
}

object StringUtil {
    def trimHead(s: String) = s.dropWhile(_ == ' ')
    def getClassString(): String = {
        val su = new StringUtil()
        trimHead(su.s)
    }

    def main(args: Array[String]): Unit = {
        println(StringUtil.trimHead(args(0)))
        println(StringUtil.getClassString())
    }
}

如果使用 StringUtil.xxx ,實際上是呼叫 singleton object StringUtil,如果是 val su = new StringUtil(),實際上是產生了 StringUtil class 的 instance。

因此 su 是沒辦法呼叫 trimHead 這個定義在 object 的 method,而 StringUtil.s 也同樣是沒有辦法使用的。

仔細看一下 companion object,其實就跟定義一個 Java class,裡面寫上幾個 static method/field 完全一樣,scala 明確地用 object 與 class 兩個關鍵字,將 class/object 內部的靜態與一般區塊分開。

對於programmer來說,有了更簡潔的語法,更簡短的程式碼,又不需要了解 Singleton Design Pattern,整體來說,有什麼道理,不該繼續往 scala 前進呢?

Scala IDE in Eclipse 4.4 Luna

另外再提供一個資訊,目前 Scala IDE 正式版只能在 Eclipse 4.3 Kepler 上運作,如果是使用 Eclipse 4.4 Luna,則我們得要改用開發版的 Scala IDE。

雖然Scala IDE 4.0.0 Milestone 3在網頁上說可以用在 Eclipse Luna,不過實際上測試後是不行的,必須要使用Scala IDE Lithium (4.0), Nightly版本。