---
# System prepended metadata

title: 'CH 10 First-class function: part 1'

---

# CH 10 First-class function: part 1

## First class function

### New skill

• Learn how to make first-class functions and values.
  
• Learn to wrap syntax using higher-order functions.
  - Build complex calculations using chains of operations.
  - Discover a way to operate on deeply nested data.
  - Fontrol the ordering and repetition of actions to eliminate timing bugs.
  
• Apply two refactorings that use first-class and higher-order functions.

<!--
- 学习如何制作一流函数版本的语法。
- 学习使用高阶函数包装语法。
- 应用两个使用一流函数和高阶函数的重构方法。
-->


> These skills will be available once we learn about first-class values.
<!-- 
我们就可以学习一种功能性的迭代方式。我们可以利用运算链建立复杂的计算。我们可以发现一种对深度嵌套数据进行操作的方法。我们还可以学习控制动作的顺序和重复，以消除时间上的错误。我们将用两个架构来结束这个游戏，这两个架构可以让我们构造我们的服务。一旦我们学习了第一类值，这些技能就可以使用了。
-->

### First-class value

First-class values are anything that can be stored in a variable, passed as an argument, and returned from functions. A first-class value can be manipulated by code.

<!-- 一级值是任何可以存储在变量中、作为参数传递、并从函数中返回的东西。一级值可以被代码所操纵。 -->

- Many parts of a language are not first-class. We can wrap those parts in functions that do the same thing to make them first-class.
- Some languages have first-class functions that let you treat functions as first-class values. First-class functions are necessary for doing this level of functional programming.
- Higher-order functions are functions that take other functions as arguments (or that return a function).

<!-- - 一种语言的许多部分都不是一流的。我们可以将这些部分包裹在做同样事情的函数中，使它们成为一流的。
- 有些语言有一流的函数，让你把函数当作一流的值。一级函数对于进行这种层次的函数式编程是必要的。
- 高阶函数是以其他函数为参数的函数（或返回一个函数）。高阶函数让我们对不同的行为进行抽象。 -->



<!-- 在这一章中，我们将学习一种代码气味和两种重构方法，它们可以帮助我们消除重复，找到更好的抽象。我们将在本章和本书的整个第二部分中应用这些新技能。 -->


<!-- - 函数名中的隐式参数是一种代码气味，其中函数之间的差异在函数名中被命名。我们可以应用表达式隐式参数来使参数成为第一类，而不是函数名中不可访问的部分。
- 我们可以应用称为用回调替换主体的重构来抽象出行为。它创建了一个一流的函数参数，代表两个函数之间的行为差异。 -->

<!-- 代码气味：函数名称中的隐性参数 -->
- Code smell: Implicit argument in function name
  - Implicit argument in function name is a code smell where the difference between functions is named in the function name. We can apply express implicit argument to make the argument first-class instead of an inaccessible part of the function name.

<!-- 重构。表达隐性参数 -->
- Refactoring: Express implicit argument

<!-- 重构。用回调取代主体 -->
- Refactoring: Replace body with callback
  - Apply the refactoring called replace body with callback to abstract over behavior. It creates a first-class function argument that represents the behavioral difference between two functions.

## Code smell: Implicit argument in function name

There are two characteristics to the implicit argument in function name smell:
1. Very similar function implementations
2. Name of function indicates the difference in implementation
The function name difference is an implicit argument

<!-- 隐含参数在函数名称中的气味有两个特点。
1. 非常相似的函数实现
2. 函数的名称表明了实现的差异
函数名称差异是一个隐性参数 -->

## Refactoring: Express implicit argument

### Steps:

1. Identify the implicit argument in the name of the function.
2. Add explicit argument.
3. Use new argument in body in place of hard-coded value.
4. Update the calling code.


<!-- 这就是express这个词的意思。使隐式参数显性化。以下是具体步骤。 -->

<!-- 1. 确定函数名称中的隐性参数。
2. 添加显性参数。
3. 在主体中使用新的参数来代替硬编码的值。
4. 更新调用代码。
让我们看看我们如何重构setPriceByName()，它只能设置价格，变成setFieldByName()，它可以设置项目的任何字段。 -->


<!-- 这个重构应用于这段代码，用一个通用的函数取代了四个现有的函数，谁知道因为有了通用的setFieldByName()，有多少个函数不用写了。
我们所做的是使字段名成为一个一流的值。以前，字段名除了作为函数名的一部分隐含地暴露给API客户外，从未暴露给客户。现在，它是一个值（在本例中是一个字符串），可以作为一个参数传递，也可以存储在一个变量或数组中。这就是我们所说的第一类。我们可以使用整个语言来处理它。而使事物成为第一类是本章的主题。
你可能认为像这样使用字符串是不安全的。我们将在几页中讨论这个问题。现在，请随它去吧! -->

![](https://i.imgur.com/ZIS8mmo.png)

![](https://i.imgur.com/IlbhOXG.png)


```ruby
# Usage example
employee = { id: 1, name: "John Doe", salary: 5000 }
department = { id: 2, name: "IT", multiplier: 1.2 }
# Before refactoring
def calculate_salary_for_employee_in_department(employee_id, department_id)
  # Calculate salary based on employee and department information
  # ...
end

calculate_salary_for_employee_in_department(employee[:id], department[:id])


# After refactoring
def calculate_salary(employee, department)
  # Calculate salary based on employee and department information
  # ...
end

# After refactoring
calculate_salary(employee, department)
```

### Examples of things you can do with a first-class value

1. Assign it to a variable.
2. Pass it as an argument to a function.
3. Return it from a function.
4. Store it in an array or object.

### Will field names as strings lead to more bugs?

```javascript
var validItemFields = ['price', 'quantity', 'shipping', 'tax'];

function setFieldByName(cart, name, field, value {
  if(!validItemFields.includes(field))
    throw `Not a valid item field: '${field}'.`;
  var item = cart[name];
  var newItem = objectSet(item, field, value);
  var newCart = objectSet(cart, name, newItem);
  return newCart;
}
```

### Will first-class fields make the API hard to change?

```javascript
var validItemFields = ['price', 'quantity', 'shipping', 'tax', 'number']
var translations = { 'quantity': 'number' };

function setFieldByName(cart, name, field, value) {
  if(!validItemFields.includes(field))
    throw "Not a valid item field: '" + field + "'.";
  if(translations.hasOwnProperty(field))
    field = translations[field];
  var item = cart[name];
  var newItem = objectSet(item, field, value);
  var newCart = objectSet(cart, name, newItem);
  return newCart;
}
```

<!-- before Replace body with callback -->
### For loop example: Eating and cleaning up

1. Wrap code in functions.
2. Rename to be more generic.
3. Express implicit argument.
4. Extract function.
5. Express implicit argument.

![](https://i.imgur.com/8gW3oIq.png)

![](https://i.imgur.com/7prkjd4.png)

## Refactoring: Replace body with callback

1. Identify the before, body, and after sections.
2. Extract the whole thing into a function.
3. Extract the body section into a function passed as an argument to that function.


![](https://i.imgur.com/09ukdZo.png)

![](https://i.imgur.com/IqhWYCd.png)

![](https://i.imgur.com/lRG6CCU.png)


```ruby
# Usage example
employee = { id: 1, name: "John Doe", salary: 5000 }
department = { id: 2, name: "IT", multiplier: 1.2 }

# Before refactoring
def calculate_salary_for_employee_in_department(employee_id, department_id)
  # Calculate salary based on employee and department information
  employee = get_employee(employee_id)
  department = get_department(department_id)
  salary = employee[:salary] * department[:multiplier]
  salary
end

calculate_salary_for_employee_in_department(employee[:id], department[:id])

# After refactoring
def calculate_salary(employee, department, salary_calculator)
  # Before
  # ...
  
  # Body
  salary_calculator.call(employee, department)

  # After
  # ...
end

# Proc
salary_calculator = Proc.new { |employee, department| employee[:salary] * department[:multiplier] }
# lambda
salary_calculator = ->(employee, department) { employee[:salary] * department[:multiplier] }

calculate_salary(employee, department, salary_calculator)

# Another refactoring
def salary_calculator(employee, department)
  employee[:salary] * department[:multiplier]
end

calculate_salary(employee, department, method(:salary_calculator))
```

## Conclusion

This chapter introduced us to the ideas
- First-class values
- First-class functions
- Higher-order functions
 
> We are going to be exploring the potential of these ideas in the next chapters. After the distinction between actions, calculations, and data, the idea of higher-order functions opens up a new level of functional programming power. The second part of this book is all about that power.
