This post was posted by Yison on the ScalaCool team blog.

If you see an open source library with a logo of four cats and five arrows, you might feel a little uncomfortable – is that something you can use in engineering code?

That’s right, it’s a functional library designed by Ty Elevel, a bunch of functional programming guys that have thrown their weight into the Scala ecosystem, and it’s Cats, short for Category.

In the water Drop system, we use Cats on a large scale to solve some business problems and benefit from it. But this is clearly not what the Scala standard library supports, so the purpose of this series is to systematically introduce the library to a wider audience.

We started with Scalaz, which is the predecessor of Cats. Due to the grammar problems, we made a lot of jokes, and then we replaced it with Cats.

Of course, many people who know Cats already know that there are excellent learning materials about Cats:

  • Herding Cats, also the author of Learning Scalaz.
  • Scala with Cats Book, from underscore. IO

But obviously, if you have no previous experience with functional programming, you may feel sleepy when you read these materials for the first time. Therefore, the series wanted to explore some basic questions about functional programming in a friendly way before introducing Cats. Such as:

  • What is functional programming
  • What are the characteristics of functional programming
  • What are the advantages of functional programming

Functional programming?

So what is functional programming? In fact, there is no definitive definition of this point, and this article does not attempt to give an answer.

However, we want to discuss what “functional programming thinking” is, and how it differs from what we know as “imperative programming.”

This is a very good question on Zhihu.

What is functional programming thinking? – zhihu

This article recommends the following answers:

Nameoverflow: Functional programming is concerned with the mapping of data, imperative programming is concerned with the steps to solve the problem.

More mathematical versions: @Parker Liu Functional programming is concerned with relationships between types (algebraic structures), imperative programming is concerned with steps to solve a problem.

conclusion

The idea of functional programming is to combine the relationships between types (algebraic structures) to construct your program using mathematical constructionism.

doubt

To dissect this conclusion, functional programming is intuitively very simple. There is only one thing we need to do, and that is compose.

But at this point, we must be confused again, and the following question immediately follows:

  • Is it really that simple?
  • What exactly is a “combination”?
  • Can it be theoretically complete?
  • What is a “relationship”?

Let’s start with a basic question — what are processes and data?

Process and data

Those of you who have read SICP have no doubt noticed that this is the subject of the first and second chapters of this book.

Simply put, data is something we want to operate on, and procedures are descriptions of rules for operating on that data. They form the basic elements of programming.

In a functional programming language like Lisp, procedures, like data, are first-level states, which means:

  • You can call it a variable
  • Can be supplied to the procedure as a parameter
  • Can be returned as a result of the procedure
  • Can be included in other data structures

For example, we can define a procedure that takes one parameter and returns another, which may seem odd. How familiar is it to define a procedure that takes one number and returns another?

In functional programming, we find that the line between “process” and “data” is sometimes blurred. That is, sometimes we can think of them as one thing.

To return to our conclusion: “Functional programming cares about relationships between types (algebraic structures).”

Thus, the first problem that functional programming addresses is the need for high enough abstraction to provide types (algebraic structures) for various data and procedures.

This is also a question to be answered in our follow-up study of Cats, and how it helps us achieve this point.

How do YOU organize your business well with ADT in Scala

Turing completeness and Lambda calculus

Secondly, let’s discuss again, what is “relationship”?

It is certainly doubtful that functional programming thinking must be theoretically complete if it can describe all programs by “composition” alone.

Yan College many friends know what is known as Turing complete. It sounds very high, but it is a very simple concept, which means that anything you can do with a Turing machine can solve any computable problem.

Another solution that supports solving all computable problems is the “Lambda calculus.” This theory is the cornerstone of a functional programming language like Lisp.

Lambda in functional programming can be thought of as a relationship between two types, an input type and an output type. Lambda calculus is a calculation that gives a lambda expression a value of the input type and yields a value of the output type. This calculation satisfies the \ alpha-equivalence and \beta – specifications.

With regard to Turing completeness and Lambda calculus, we can continue this discussion in a future article if we have the opportunity.

Combinatory-oriented programming

Let’s talk a little bit more about the core, the so-called combination.

“Combinatory-oriented programming” was a concept proposed by @Ajoo of JavaEye ten years ago.

First, we can use a philosophical analogy to illustrate the difference between object-oriented programming and combinatory-oriented programming.

The former is induction and the latter is deduction.

In other words, when we design programs in object-oriented languages like Java, we usually take a summary approach, whereas functional programming languages advocate “composition”, which is closer to mathematical thinking, it is a kind of derivation.

As a result, functional programming is more concerned with combinations of highly abstract type relationships and then transformations of those relationships, called Transformer.

This leads us to the second key question: how do Cats provide enough Transformer to help us achieve combinations of relationships?

For example,

For those of you who are new to these concepts, they are a bit abstract, so let’s give a practical example to help you understand them better.

Suppose we now want to design a sweepstakes participation process involving the following logic:

  • Get event prize data
  • Judge the start, start, end of the activity, whether the prize is stolen, etc

Imperative style

import org.joda.time.DateTime
import scala.concurrent.Future

case class Activity(id: Long, start: DateTime, end: DateTime)
case class Prize(id: Long, name: String, count: Int)

val activity = syncGetActivity()
val prizes = syncGetPrizes(activity.id)

if (activity.start.isBefore(DateTime.now())) {
  println("activity not starts")}else if (activity.end.isBefore(DateTime.now())) {
  println("activity ends")}else if (prizes.map(_.count).sum < 1) {
  println("activity has no prizes")}else {
  println("activity is running")}Copy the code

Functional style

import org.joda.time.DateTime
import scala.concurrent.Future

case class Activity(id: Long, start: DateTime, end: DateTime)
case class Prize(id: Long, name: String, count: Int)

sealed trait ActivityStatus {
  val activity: Activity
  val prizes: Seq[Prize]}case class ActivityNotStarts(activity: Activity, prizes: Seq[Prize]) extends ActivityStatus
case class ActivityEnds(activity: Activity, prizes: Seq[Prize]) extends ActivityStatus
case class ActivityPrizeEmpty(activity: Activity, prizes: Seq[Prize]) extends ActivityStatus
case class ActivityRunning(activity: Activity, prizes: Seq[Prize]) extends ActivityStatus

def getActivityStatus() :Future[ActivityStatus] = {
  for {
    activity <- asyncGetActivity()
    prizes <- asyncGetPrizes(activity.id)
  } yield (activity, prizes) match {
    case (a, pzs) if a.start.isBefore(DateTime.now()) => ActivityNotStarts(a, pzs)
    case (a, pzs) if a.end.isBefore(DateTime.now()) => ActivityNotStarts(a, pzs)
    case (a, pzs) if pzs.map(_.count).sum < 1= >ActivityPrizeEmpty(a, pzs)
    case (a, pzs) => ActivityRunning(a, pzs)
  }
}
Copy the code

Above, we can see that functional styles tend to be abstracted at a higher level of business, which is intuitively a design of describe what rather than how to do.

It is worth mentioning that asyncGetActivity is a process that asynchronously retrieves Activity data from a database. Its type is a Future[Activity] of higher order type, which is the abstraction of the process we mentioned earlier.

By combining asyncGetActivity and AsyncgetPeaces, we finally get the result of an object of type ActivityStatus.

conclusion

Scala is an “object-oriented” and “functional” programming language that allows you to write very different code styles. A lot of people use it as better Java, but with Cats, a functional library, we can design our programs with a more functional programming mindset, which makes Scala more powerful.

Through this article, we get an intuitive feel for functional programming. Of course, you may still be in a fog, but don’t worry, we’ll talk more about that in a future post. In the next article, we’ll look at the benefits of functional programming.