iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 1
1

前言

這系列的文章算是我給自己的筆記,從學習 Functional programming 以來已經也有了兩年了吧!過程中跌跌撞撞,很常發現在跟別人介紹 Functional programming 是什麼時,自己的知識卻非常稀少,甚至自我懷疑過好幾次。嘗試學習 Haskell → 放棄 → 學習 Rx → 似懂非懂 → 買 scala 的書來看 → 看到懷疑人生 → 舉辦工作坊 → 別開玩笑了你根本無法解釋 Monad → 看影片學習 Category Theory → 挑戰鐵人賽。這段學習的過程不能說很順利,但至少我知道我有在進步,這樣就夠了。

希望來看這系列文的大家,不管是什麼背景,都能夠學到些東西,讓我們一起成長,我不敢說這些內容百分之百正確(尤其是這麼嚴謹的數學),但已經是在我理解的範圍內盡可能的負責。如果你發現有誤,希望能夠多多指正,才有機會產生出更優質的內容讓大家一起學習。

最後希望 Kotlin 鐵人陣大家都能完賽!讓我們開始吧!

Intro

在這系列文章中將會介紹下列幾個概念

  • Kotlin functional programming 基本語法
  • 基本概念,包含 pure function、immutability 等等
  • Category theory 入門
  • 各種 Monad
  • 實戰運用

不會介紹:

  • Kotlin 程式語言基礎
  • RxJava 原理與 Best practice

Function type

Functional Programming 中的主角 - Function, 在 Kotlin 中是一個 first class citizen,這是什麼意思呢?對於我,甚至是大部分的人,第一個學習的語言應該會是“物件導向”的程式語言。這類的語言都有共同的特性:建立物件並將其指派到一個變數中,如下方程式碼所示

class Point(val x: Int, val y: Int)

fun main() {
    val a: Point = Point(0, 0)
    val b: Point = Point(0, 1)
    val c: Int = 4
}

上方可以看到變數 a 跟 b 的型別都是 Point ,這是一個物件型別。而 c 呢,則是一個 primitive type 。這兩種型別對於一個熟悉 Java 的人來說是常識。與 Java 不一樣的是, Kotlin 有另外一個型別 - Function type ,一個用來表示函數的型別。相信函數這概念大家都在學校中學過,基本上可以把他想像成一個集合對應到另外一個集合,如下圖:

https://user-images.githubusercontent.com/7949400/92629319-b9254b00-f300-11ea-8665-49657d368ab4.png

左邊是輸入,右邊則是輸出,兩邊的型別都整數。在 Kotlin 中相對應的 function type 會是 (Int) -> Int ,非常直覺。那如果是一個建立 Point 的 function type 會是怎樣呢?可能會像是 (Int, Int) -> Point ,輸入兩個整數,回傳一個 Point。 以下列出 function type 的幾個範例:

val add: (Int) -> Int = fun(x) { x + 1 }
val minus: (Int) -> Int = {x -> x - 1 }               //lambda,通常會使用這形式
val printFun: (String) -> Unit = { x -> print(x)}     //也可以沒有回傳值,這時候會是 Unit
val helloWorld: (Unit) -> Unit = { print("Hello world")}   //輸入也可以是 Unit 
val helloWorld2: () -> Unit = { print("Hello world")}      //當輸入是 Unit 時可以省略
val createPoint: (Int, Int) -> Point = { x, y -> Point(x, y)}   //多個輸入參數 
val square: (Int) -> Int = { it*it }                          //如果只有一個參數的話可以簡化成 it
val square2 = { x: Int -> x*x }.                               //如果型別可以推斷的出來的話,function type 可以省略

另外值得一提的是 lambda 表示式,在上方的範例中,除了第一個之外,其餘都是以 lambda 的形式來實做 function 的內容,還有lambda 也經常被稱作匿名函式。

Higher order function

有了前面的觀念後就可以來介紹 higher order function 了,他的定義非常簡單,跟大家所熟悉的 function 沒什麼兩樣,就只差在輸入或是輸出的型別,只要其中一個是 Function Type ,就可以被稱做是 higher order function。

// 輸入 logFun 是 Function Type
fun addOne(a: Int, logFun: (String) -> Unit): Int {
    val result = a + 1
    // 使用 invoke () 來執行 function
    logFun.invoke(result.toString())
    return result
}

// 回傳值是 Function Type
fun getCountDownListener(): (Int) -> Unit {
    return { count: Int -> textView.text = count.toString }
}

Extension function

這是一個很強大的工具,有了這個功能,可以讓你在任何"型別"上新增自定義的函式。在實務上,會有不屬於自己專案的類別,例如 Android 的 Canvas 、 View 等等。在 google 官方的開源專案中就有許多這樣的 extension function :

fun Canvas.withTranslation(
    x: Float = 0.0f,
    y: Float = 0.0f,
    block: Canvas.() -> Unit
) {
    val checkpoint = save()
    translate(x, y)
    try {
        block()
    } finally {
        restoreToCount(checkpoint)
    }
}

//link: https://android.googlesource.com/platform/frameworks/support/+/android-room-release/core/ktx/src/main/java/androidx/core/graphics/Canvas.kt

在上面我特別標註了"型別",還記得我們一開始介紹甚麼嗎?沒錯!Function type 也可以擁有 extension function :

fun ((Int) -> Int).double() = { a: Int -> this(a) * 2}

fun main() {
    val addThree = { a: Int -> a + 3}
    println(addThree(4))
    println(addThree.double()(4))
}

// 猜猜看會印出甚麼吧!

Infix function

使用 infix 關鍵字可以讓你在呼叫 function 時省略 (),使用的好的話會語意更加清晰,同時也是在寫 DSL 時重要的技術之一,看看下面這個例子

fun main() {
    println(4 add 5)
}

// 請注意 infix function 一定同時會是 extension funcion
infix fun Int.add(another: Int) = this + another

Function reference

有時候我們會想要將一般的 function 當作 lambda 傳給 higher order function , function reference 可以幫你做到這點,使用方法是前面加兩個冒號,同時要注意型態要一致才能做這樣的轉換:

fun main() {
    printEquation(4, ::divideTwo)
}

fun printEquation(input: Int, equation: (Int) -> Int) {
    println(equation(input))
}

fun divideTwo(a: Int) = a / 2

關於 Kotlin 還有非常多其他的語法沒辦法全部介紹完,這邊只能介紹一些之後會很常用到的部分,如果讀者有興趣可以前往官網學習:https://kotlinlang.org/docs/reference/functions.html


下一篇
Function composition and lazy execution
系列文
Functional Programming in Kotlin16

1 則留言

0
hannahpun
iT邦新手 5 級 ‧ 2020-09-11 23:29:36

看到 "別開玩笑了你根本無法解釋 Monad"就笑了 XD 真的無敵難解釋的

QQQ 今天還看到一句話,“一但你了解了 Monad ,就失去了跟別人解釋它的能力”

之前也有聽過別人說,"Monad 就是 Monad ,你一旦用別的東西去比喻它就錯了" XD 記得那個討論串好像是在討論一張 Monad is not a box 的圖吧 XD

我要留言

立即登入留言