Mooring floating purpose in this paper, starting from Jane books: www.jianshu.com/u/204b8aaab…

This article is participating in “Java Theme Month – Java Development in Action”, see the activity link for details

version The date of note
1.0 2019.8.11 The article first
1.1 2021.5.21 Modify the title:A brief introduction to functional programming-> Talk about code: functional programming

1. Introduction

One sunny afternoon, I was looking at my daily code for refactoring migrations when I came across this code:

    private void getTopicsDiskSizeForSomeBroker(int brokerID, AdminClient admin, Map<String, Long> topicsSizeMap) throws ExecutionException, InterruptedException {
        DescribeLogDirsResult ret = admin.describeLogDirs(Collections.singletonList(brokerID));
        Map<Integer, Map<String, DescribeLogDirsResponse.LogDirInfo>> tmp = ret.all().get();
        for (Map.Entry<Integer, Map<String, DescribeLogDirsResponse.LogDirInfo>> entry : tmp.entrySet()) {
            Map<String, DescribeLogDirsResponse.LogDirInfo> tmp1 = entry.getValue();
            for (Map.Entry<String, DescribeLogDirsResponse.LogDirInfo> entry1 : tmp1.entrySet()) {
                DescribeLogDirsResponse.LogDirInfo info = entry1.getValue();
                Map<TopicPartition, DescribeLogDirsResponse.ReplicaInfo> replicaInfoMap = info.replicaInfos;
                for (Map.Entry<TopicPartition, DescribeLogDirsResponse.ReplicaInfo> replicas : replicaInfoMap.entrySet()) {
                    String topic = replicas.getKey().topic();
                    Long topicSize = topicsSizeMap.get(topic);
                    if (topicSize == null) {
                        topicsSizeMap.put(topic, replicas.getValue().size);
                    } else {
                        topicsSizeMap.put(topic, replicas.getValue().size + topicSize);
                    }
                }
            }
        }
    }
Copy the code

I feel bad after reading this code!

First there are the rocket-like three nested for loops, and then there are those variable declarations. In order to iterate over them, we have to declare it again…

2. The use of the Stream

    public List<KafkaTopicInfoBO> getTopicDiskSize(a) {
        return getTopicPartitionReplicaInfo().entrySet().stream()
                .map(e -> new KafkaTopicInfoBO(e.getKey().topic(), e.getValue().size))
                .collect(Collectors.toList());

    }

    protected Map<TopicPartition, DescribeLogDirsResponse.ReplicaInfo> getTopicPartitionReplicaInfo() {
        Properties globalConf = zkConfigService.getProperties(ZkPathUtils.GLOBAL_CONFIG);
        Properties adminConfig = new Properties();
        adminConfig.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, globalConf.getProperty((ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG)));
        AdminClient adminClient = AdminClient.create(adminConfig);
        List<String> brokerIds = zkConfigService.getChildByPath(kafkaIdsPath);
        return  brokerIds.stream()
                .map(Integer::valueOf)
                .map(Collections::singletonList)
                .map(adminClient::describeLogDirs)
                .map(DescribeLogDirsResult::all)
                .map(mapKafkaFuture -> {
                    try {
                        return mapKafkaFuture.get();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                })
                .map(Map::values)
                .flatMap(Collection::stream)
                .map(Map::values)
                .flatMap(Collection::stream)
                .map(e -> e.replicaInfos)
                .map(Map::entrySet)
                .flatMap(Collection::stream)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }
Copy the code

That looks a little better. But for those unfamiliar with functional programming, the above code is a little difficult to understand.

Next, let’s talk a little bit about functional programming.

3. What is functional programming

3.1 Understand in one sentence

It’s like algebra from math

f(x)=5x^2+4x+3
g(x)=2f(x)+5=10x^2+8x+11
h(x)=f(x)+g(x)=15x^2+12x+14
Copy the code

Functional programming defines the relationship between input data and output data. Mathematical expressions are used to map the relationship between input data and output data.

3.2 Intuition: Use code examples

public class Quint{
    public static void main (String args[]){
        for (int i=0; i<25; i++){ System.out.println(i*i); }}}Copy the code
(println (take 25 (map (fn [x] (*x x) (range)))))
Copy the code

A quick explanation of the previous Lisp code:

  1. The range function returns an infinite list of integers starting at 0
  2. The list is then passed into the map, and for each element in the list, the anonymous function of the square is called, producing an infinite list of squares
  3. Pass the list into the take function and only return the first 25
  4. Println Prints the input parameters

4. Use Kotlin, which supports better functional programming


    protected fun getTopicPartitionReplicaInfo(a): Map<TopicPartition, DescribeLogDirsResponse.ReplicaInfo> {
        val globalConf = zkConfigService.getProperties(ZkPathUtils.GLOBAL_CONFIG)
        val adminConfig = Properties()
        adminConfig.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, globalConf.getProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG))
        val adminClient = AdminClient.create(adminConfig)
        val brokerIds = zkConfigService.getChildByPath(kafkaIdsPath)
        return brokerIds.stream()
                .mapToInt {
                    Integer.valueOf(it)
                }.let { intStream ->
                    adminClient.describeLogDirs(intStream.boxed().collect(Collectors.toList()))
                }.let { describeLogDirsResult ->
                    describeLogDirsResult.all()
                }.let { mapKafkaFutrue ->
                    mapKafkaFutrue.get()
                }.let { mapStream ->
                    mapStream.values
                }.let {
                    it.stream().map { e -> e.values }.flatMap { e -> e.stream() }.collect(Collectors.toList())
                }.flatMap {
                    it.replicaInfos.entries.toList()
                }.let { it ->
                    it.associateBy({ it.key }, { it.value })
                }
    }
Copy the code

The code looks pretty darn good. But Kotlin’s keywords are easier to write. Let’s look at the signature of the map function and the let function in Kotlin:

     * Returns a stream consisting of the results of applying the given
     * function to the elements of this stream.
     *
     * <p>This is an <a href="package-summary.html#StreamOps">intermediate
     * operation</a>.
     *
     * @param <R> The element type of the new stream
     * @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
     *               <a href="package-summary.html#Statelessness">stateless</a>
     *               function to apply to each element
     * @return the new stream
     */
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
Copy the code
/** * Calls the specified function [block] with `this` value as its argument and returns its result. * * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#let). */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) - >R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)}Copy the code

We can see that a Map in Java is limited to the Stream API, whereas Kotlin’s let is not.

At the same time, we can see that the support for functional programming is clearly better in Kotlin — in Kotlin we can represent functions with a {}, whereas in Java we need interfaces (in Java, objects are first-class citizens).

If you’re interested, you can try Haskell or Lisp(Clojure on the JVM). These are purely functional languages.

Similarly,Kotlin has many other such functions, called scoped functions. Here are some common ones:

  • let
  • run
  • also
  • apply
  • takeIf
  • takeUnless
  • repeat

5. Summary

In The Way to Clean Architecture, there is a summary:

  • Structured programming limits the direct transfer of program control
  • Object – oriented programming limits the indirect transfer of program control
  • Functional programming is a restriction on program assignment operations

If object-oriented programming is abstracting data, functional programming is abstracting behavior.

5.2 Three pieces of Functional programming:

  1. Map
  2. Reduce
  3. Filter

For example, bread and vegetables are mapped to the shredding operation and reduced to burgers.

We can see that Map and Reduce do not care about input data, they are control, not business. Control describes how to do it, while business describes what to do.

In this article, we’ve only seen the figure of a Map — as mentioned above, the Map operates on each element in the flow.

Readers may ask what a let is, but in the code examples in this article, a let operates on the entire stream.

In simple terms, Map && Reduce corresponds to the loops we use every day, and Filter corresponds to If

5.3 Advantages && Disadvantages

  • advantage

    1. stateless
    2. Concurrent no harm
    3. The function is executed in no order
  • disadvantage

    1. Serious Data Replication

5.4 Application Scenarios

  • Python’s decorator pattern
  • Event traceability: Instead of recording the final state, each event is recorded. When needed, events are traced (recalculated) to arrive at the current state. Such as:
    1. Database transaction log
    2. Version controller
    3. The currency