android 函数式编程_Android开发人员的函数式编程-第1部分

壤驷凯
2023-12-01

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.

现在,函数的输出仅取决于其输入。 对于给定的xy,该函数将始终返回相同的输出。 现在据说该功能是函数。 数学函数也以相同的方式运行。 数学函数的输出仅取决于其输入-这就是为什么函数编程比我们习惯的常规编程风格更接近数学的原因。

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部分中做到这一点,看看纯函数和不变性如何能够帮助我们编写简单易懂的并发代码,而无需借助锁和互斥体。

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 函数式编程

 类似资料: