本日內容取自 Kotlin Conf 2023 - Kotlin & Functional Programming: pick the best, skip the rest by Urs Peter這個影片,裡面講到如何從指令式程式設計 (Imperative programming) 到利用 scope funtion 達到表達式程式設計最後再到 functional programming,對於 Monad 解釋也很清楚,是個非常好的影片,推薦大家去看
如果是告訴電腦如何一步一步的執行,我們會稱為指令式程式設計,程式碼是說明如何作 (How to do)。如此的程式碼有比較大的機會使用可變變數,可變集合,較長的變數範圍,在許多地方 return , 這些都違反我們前幾天說的原則,如以下程式碼:
fun devsToFile(fileName: String): Result {
    val client RestClient()
    client.username = "reader" 
    client.secret = System.getenv("pwd")
    client.url="https://..." 
    client.initAccessToken()
    try{
    //檔案處理
        val file File(fileName)
        file.createNewFile()
        file.setWritable(true)
        //取得外部資料
        val devs = client.getAll<Developer>()              
        require (devs.isNotEmpty()) { 
            val msg = "No devs found" 
            LOG.error(msg)
            msg
        }
        //列印到檔案
        devs.forEach(file.appendText(it.toCSV())} 
        return Result (devs. size, file.length())
    }
    finally {
        client.close()
    }
}
以上的例子其實有點故意,是我應該還是會先確定取得外部資料再來開 FileStream.但因為沒有 scope function 比較難限制開發者跳來跳去的思維
那如果利用Kotlin 的 scope function ,就可限制變數的範圍(scope)並讓流程更易讀,這個就會稱為表達式程式設計,改寫如下例
fun devsToFile(fileName: String): Result =
    //利用 apply 限縮可變空間 
    RestClient().apply{
        username = "reader"
        secret = System.getenv("pwd")
        url = "https://..."
        initAccessToken()
    }.use { client -> //使用 use 在結束自動 close
        client.getAll-Developer>().let { devs ->
            require(devs.isNotEmpty()) {
                "No devs found".also { LOG.error(it) }
            }
            File(fileName).run {
                createNewFile()
                setWritable(true)
                devs.forEach(appendText (it.toCSV())}
                Result(devs.size, length())
            }
        }
    }
我們利用了幾個 Kotlin Scope function
我們再把改造前後的程式放在一起比較,左邊的看起來比較容易閱讀並且安全呢!

當我們想改變想的是轉成 CSV 的格式。例如文字要不要加上 ""。我們可以把 (Developer)->String 抽成函數參數,而能接受函數參數的函數叫作 High-order function, 如下我們把
(Developer)->String 變成參數傳入。這個 function 就更有彈性能夠複用
fun devsToFile(fileName: String, toLine:(Developer) -> String ): Result =
    //利用 apply 限縮可變空間 
    RestClient().apply{
        username = "reader"
        secret = System.getenv("pwd")
        url = "https://..."
        initAccessToken()
    }.use { client -> //使用 use 在結束自動 close
        client.getAll-Developer>().let { devs ->
            require(devs.isNotEmpty()) {
                "No devs found".also { LOG.error(it) }
            }
            File(fileName).run {
                createNewFile()
                setWritable(true)
                devs.forEach(appendText (toLine(it))}
                Result(devs.size, length())
            }
        }
    }
今天來推這首美式校園風的 Allergy