android 函數式編程
by Anup Cowkur
通過安納普·考庫(Anup Cowkur)
Android開發人員的函數式編程-第1部分 (Functional Programming for Android Developers — Part 1)
Lately, I’ve been spending a lot of time learning Elixir, an awesome functional programming language that is friendly to beginners.
最近,我花了很多時間學習Elixir ,這是一種很棒的函數式編程語言,對初學者很友好。
This got me thinking: why not use some of the concepts and techniques from the functional world in Android programming?
這讓我開始思考:為什么不在Android編程中使用功能界的一些概念和技術?
When most people hear the term Functional Programming, they think of Hacker News posts yammering on about Monads, Higher Order Functions and Abstract Data Types. It seems to be a mystical universe far removed from the toils of the daily programmer, reserved only for mightiest hackers descended from the the realm of Númenor.
當大多數人聽到“ 函數式編程 ”一詞時,他們會想到Hacker News上有關Monad,高階函數和抽象數據類型的文章。 這似乎是一個神秘的世界,它遠離日常程序員的工作,只保留給Númenor領域的最強大的黑客。
Well, screw that! I’m here to tell you that you too can learn it. You too can use it. You too can create beautiful apps with it. Apps that have elegant, readable codebases and have fewer errors.
好吧, 擰緊 ! 我在這里告訴您您也可以學習它。 您也可以使用它。 您也可以使用它創建漂亮的應用程序。 具有精美易讀的代碼庫且錯誤更少的應用。
Welcome to Functional Programming (FP) for Android developers. In this series, we’re gonna learn the fundamentals of FP and how we can use them in good old Java and new awesome Kotlin. The idea is to keep the concepts grounded in practicality and avoid as much academic jargon as possible.
歡迎使用面向Android開發人員的函數式編程(FP)。 在本系列中,我們將學習FP的基礎知識,以及如何在老式Java和新的Kotlin中使用它們。 這樣做的目的是使概念扎根于實用性,并盡可能避免使用學術術語。
FP is a huge subject. We’re gonna learn only the concepts and techniques that are useful to writing Android code. We might visit a few concepts that we can’t directly use for the sake of completeness but I’ll try to keep the material as relevant as possible.
FP是一個巨大的主題。 我們將僅學習對編寫Android代碼有用的概念和技術。 為了完整起見,我們可能會訪問一些無法直接使用的概念,但我將嘗試使材料盡可能相關。
Ready? Let’s go.
準備? 我們走吧。
什么是函數式編程,為什么要使用它? (What is Functional Programming and why should I use it?)
Good question. The term Functional programming is an umbrella for a range of programming concepts which the moniker doesn’t quite do justice to. At it’s core, It’s a style of programming that treats programs as evaluation of mathematical functions and avoids mutable state and side effects (we’ll talk about these soon enough).
好問題。 “ 函數式編程 ”一詞是一系列編程概念的統稱,綽號并非如此。 從本質上講,這是一種編程風格,將程序視為對數學函數的評估,并避免了可變的狀態和副作用 (我們將盡快討論這些問題)。
At it’s core, FP emphasizes :
FP的核心是:
Declarative code — Programmers should worry about the what and let the compiler and runtime worry about the how.
聲明性代碼 -程序員應該擔心什么,而讓編譯器和運行時擔心如何。
Explicitness — Code should be as obvious as possible. In particular, Side effects are to be isolated to avoid surprises. Data flow and error handling are explicitly defined and constructs like GOTO statements and Exceptions are avoided since they can put your application in unexpected states.
顯式 -代碼應盡可能明顯。 特別是副作用 應當隔離以避免意外。 明確定義了數據流和錯誤處理,并避免了GOTO語句和異常之類的構造,因為它們會使您的應用程序處于意外狀態。
Concurrency — Most functional code is concurrent by default because of a concept known as functional purity. The general agreement seems to be that this trait in particular is causing functional programming to rise in popularity since CPU cores aren’t getting faster every year like they used to (see Moore’s law) and we have to make our programs more concurrent to take advantage of multi-core architectures.
并發性 -由于稱為功能純度的概念,大多數功能代碼默認情況下是并發的。 普遍的共識似乎是,特別是這種特性正在導致函數式編程的普及,因為CPU內核并沒有像以前那樣每年都在變得更快(請參閱摩爾定律 ),并且我們必須使程序更并發才能利用多核體系結構。
Higher Order Functions — Functions are first class members just like all the other language primitives. You can pass functions around just like you would a string or an int.
高階函數 -就像所有其他語言原語一樣,函數是一等成員。 您可以像傳遞字符串或整數一樣傳遞函數。
Immutability — Variables are not to be modified once they’re initialized. Once a thing is created, it is that thing forever. If you want it to change, you create a new thing. This is another aspect of explicitness and avoiding side effects. If you know that a thing cannot change, you have much more confidence about its state when you use it.
不變性 -變量一旦初始化就不能修改。 一旦創建了事物,它便永遠存在。 如果您要更改它,那么可以創建一個新東西。 這是明確性和避免副作用的另一方面。 如果您知道某事物無法更改,那么使用它時,您對它的狀態就會更有信心。
Declarative, Explicit and Concurrent code that is easier to reason about and is designed to avoid surprises? I hope I’ve piqued your interest.
聲明性,顯式和并發代碼更易于推理并旨在避免意外? 希望我引起了您的興趣。
In this first part of the series, let’s start with some of the most fundamental concepts in FP : Purity, Side effects and Ordering.
在本系列的第一部分中,讓我們從FP中最基本的概念開始: 純度 , 副作用和有序性 。
純功能 (Pure functions)
A function is pure if its output depends only on its input and has no side effects (we’ll talk about the side effects bit right after this). Let’s see an example, shall we?
如果函數的輸出僅取決于其輸入并且沒有副作用 (在此之后再討論副作用),則該函數為純函數。 讓我們看一個例子,好嗎?
Consider this simple function that adds two numbers. It reads one number from a file and the other number is passed in as a parameter.
考慮這個簡單的函數,將兩個數字相加。 它從文件中讀取一個數字,另一個數字作為參數傳遞。
Java
Java
int add(int x) { int y = readNumFromFile(); return x + y;}
Kotlin
Kotlin
fun add(x: Int): Int { val y: Int = readNumFromFile() return x + y}
This function’s output is not dependent solely on its input. Depending on what readNumFromFile() returns, it can have different outputs for the same value of x. This function is said to be impure.
該函數的輸出不僅僅取決于其輸入。 根據readNumFromFile()返回的內容,對于x的相同值,它可以具有不同的輸出。 據說此功能不純凈 。
Let’s convert it into a pure function.
讓我們將其轉換為純函數。
Java
Java
int add(int x, int y) { return x + y;}
Kotlin
Kotlin
fun add(x: Int, y: Int): Int { return x + y}
Now the function’s output is only dependent on its inputs. For a given x and y, The function will always return the same output. This function is now said to be pure. Mathematical functions also operate in the same way. A mathematical functions output only depends on its inputs — This is why functional programming is much closer to math than the usual programming style we are used to.
現在,函數的輸出僅取決于其輸入。 對于給定的x和y,該函數將始終返回相同的輸出。 現在據說該功能是純函數。 數學函數也以相同的方式運行。 數學函數的輸出僅取決于其輸入-這就是為什么函數編程比我們習慣的常規編程風格更接近數學的原因。
P.S. An empty input is still an input. If a function takes no inputs and returns the same constant every time, it’s still pure.
PS空輸入仍然是輸入。 如果一個函數不接受任何輸入并且每次都返回相同的常量,那么它仍然是純凈的。
P.P.S. The property of always returning the same output for a given input is also known as referential transparency and you might see it used when talking about pure functions.
PPS對于給定輸入始終返回相同輸出的屬性也稱為引用透明性 ,在談論純函數時,您可能會看到它。
副作用 (Side effects)
Let’s explore this concept with the same addition function example. We’ll modify the addition function to also write the result to a file.
讓我們通過相同的加法函數示例來探索這個概念。 我們將修改加法功能以將結果也寫入文件。
Java
Java
int add(int x, int y) { int result = x + y; writeResultToFile(result); return result;}
Kotlin
Kotlin
fun add(x: Int, y: Int): Int { val result = x + y writeResultToFile(result) return result}
This function is now writing the result of the computation to a file. i.e. it is now modifying the state of the outside world. This function is now said to have a side effect and is no longer a pure function.
現在,此功能將計算結果寫入文件。 即它現在正在修改外部世界的狀態。 現在據說該功能具有副作用 ,不再是純功能。
Any code that modifies the state of the outside world — changes a variable, writes to a file, writes to a DB, deletes something etc — is said to have a side effect.
可以修改外部環境狀態的任何代碼(更改變量,寫入文件,寫入DB,刪除某些內容等)都具有副作用。
Functions that have side effects are avoided in FP because they are no longer pure and depend on historical context. The context of the code is not self contained. This makes them much harder to reason about.
FP中避免了具有副作用的功能,因為它們不再是純粹的,而是取決于歷史背景 。 代碼的上下文不是自包含的。 這使得他們很難推理。
Let’s say you are writing a piece of code that depends on a cache. Now the output of your code depends on whether someone wrote to the cache, what was written in it, when it was written, if the data is valid etc. You can’t understand what your program is doing unless you understand all the possible states of the cache it depends on. If you extend this to include all the other things your app depends on — network, database, files, user input and so on, it becomes very hard to know what exactly is going on and to fit it all into your head at once.
假設您正在編寫一段取決于緩存的代碼。 現在,代碼的輸出取決于是否有人向緩存中寫入了內容,寫入了內容,寫入的時間,數據是否有效等。除非您了解所有可能的狀態,否則您將無法理解程序在做什么取決于它的緩存。 如果將其擴展到包括應用程序所依賴的所有其他內容(網絡,數據庫,文件,用戶輸入等),將很難知道到底發生了什么并立即將其全部放入您的腦海。
Does this means we don’t use network, databases and caches then? Of course not. At the end of the execution, you want the app to do something. In the case of Android apps, it usually means updating the UI so that the user can actually get something useful from our app.
這是否意味著我們那時不使用網絡,數據庫和緩存? 當然不是。 在執行結束時,您希望應用程序執行某些操作。 對于Android應用,通常意味著更新UI,以便用戶實際上可以從我們的應用中獲得有用的信息。
FP’s greatest idea is not to completely forego side effects but to contain and isolate them. Instead of having our app littered with functions that have side effects, we push side effects to the edges of our system so they have as little impact as possible, making our app easier to reason about. We’ll talk about this in detail when we explore a functional architecture for our apps later in the series.
FP的最大想法不是完全放棄副作用,而是要遏制和隔離它們。 我們不會在應用程序中散布有副作用的功能,而是將副作用推到系統的邊緣,以使它們的影響盡可能小,從而使我們的應用程序更易于推理。 在本系列后面的內容中,我們將為我們的應用程序探索功能架構時,將對此進行詳細討論。
訂購方式 (Ordering)
If we have a bunch of pure functions that have no side effects, then the order in which they are executed becomes irrelevant.
如果我們有一堆沒有副作用的純函數,那么執行它們的順序就無關緊要了。
Let’s say we have a function that calls 3 pure functions internally:
假設我們有一個內部調用3個純函數的函數:
Java
Java
void doThings() { doThing1(); doThing2(); doThing3();}
Kotlin
Kotlin
fun doThings() { doThing1() doThing2() doThing3()}
We know for sure that these functions don’t depend on each other (since the output of one is not the input of another) and we also know that they won’t change anything in the system (since they are pure). This makes the order in which they are executed completely interchangeable.
我們確定這些函數不會相互依賴(因為一個函數的輸出不是另一個函數的輸入),并且我們也知道它們不會改變系統中的任何內容(因為它們是純函數)。 這使得它們執行的順序完全可以互換。
The order of execution can be re-shuffled and optimized for independent pure functions. Note that if the input of doThing2() were the result of doThing1() then these would have to be executed in order, but doThing3() could still be re-ordered to execute before doThing1().
可以重新調整執行順序,并針對獨立的純函數進行優化。 需要注意的是,如果doThing2()的輸入是doThing1的結果(),則這些必須以執行,但doThing3()仍然可以重新排序doThing1之前執行()。
What does this ordering property get us though? Concurrency, that’s what! We can run these functions on 3 separate CPU cores without worrying about screwing anything up!
這個訂購屬性讓我們得到什么? 并發,就是這樣! 我們可以在3個獨立的CPU內核上運行這些功能,而不必擔心搞砸了!
In many cases, compilers in advanced pure functional languages like Haskell can tell by formally analyzing your code whether it’s concurrent or not, and can stop you from shooting yourself in the foot with deadlocks, race conditions and the like. These compilers can theoretically also auto-parallelize your code (this doesn’t actually exist in any compiler I know of at the moment but research is ongoing).
在許多情況下,使用Haskell這樣的高級純函數語言的編譯器可以通過形式化分析代碼來判斷代碼是否是并發的,并且可以阻止您死于死鎖,競爭條件等。 從理論上講,這些編譯器還可以自動并行化您的代碼(目前我所知道的任何編譯器中實際上都不存在此代碼,但研究仍在進行中)。
Even if your compiler is not looking at this stuff, as a programmer, it’s great to be able to tell whether your code is concurrent just by looking at the function signatures and avoid nasty threading bugs trying to parallelize imperative code which might be full of hidden side effects.
即使您的編譯器沒有考慮這些問題,作為程序員,也能夠僅通過查看函數簽名來判斷代碼是否是并發的,并且避免討厭的線程錯誤試圖并行化命令性代碼(可能充滿了隱藏的內容),這是很好的。副作用。
摘要 (Summary)
I hope this first part has intrigued you about FP. Pure, Side effect free functions make it much easier to reason about code and are the first step to achieving concurrency.
我希望第一部分對FP感興趣。 純的,無副作用的功能使代碼推理變得更加容易,并且是實現并發的第一步。
Before we get to concurrency though, we have to learn about immutability. We’ll do just that in Part 2 of this series and see how pure functions and immutability can help us write simple and easy to understand concurrent code without resorting to locks and mutexes.
在開始并發之前,我們必須了解不變性 。 我們將在本系列的第2部分中做到這一點,看看純函數和不變性如何能夠幫助我們編寫簡單易懂的并發代碼,而無需借助鎖和互斥體。
繼續閱讀 (Read next)
Functional Programming for Android developers — Part 2If you haven’t read part 1, please read it here:medium.com
適用于Android開發人員的函數式編程—第2部分 如果您尚未閱讀第1部分,請在此處閱讀: medium.com
If you liked this, click the ? below. I notice each one and I’m grateful for every one of them.
如果喜歡此,請單擊“?”。 下面。 我注意到每個人,我感謝每個人。
For more musings about programming, follow me so you’ll get notified when I write new posts.
有關編程的更多信息,請關注我,以便在我撰寫新文章時得到通知。
翻譯自: https://www.freecodecamp.org/news/functional-programming-for-android-developers-part-1-a58d40d6e742/
android 函數式編程