(reading-nix-language)=


# Nix 언어 기초

```{contributors}
:authors: fricklerhandwerk
:editors: infinisil
```

Nix 언어는 *derivation*, 즉 기존 파일의 내용을 사용해 새 파일을 도출하는 방법을 정확하게 기술한 것을 편리하게 만들고 조합하도록 설계되었습니다. Nix 언어는 도메인 특화, 순수 함수형, 지연 평가, 동적 타입 프로그래밍 언어입니다.

:::{admonition} Nix 언어의 주요 활용 사례
:class: note


- {term}`Nixpkgs`

  세계에서 가장 크고 가장 최신인 소프트웨어 배포판이며, Nix 언어로 작성되었습니다.

- {term}`NixOS`

  완전히 선언적으로 설정할 수 있으며 Nix와 Nixpkgs를 기반으로 하는 Linux 배포판입니다.

  NixOS의 기반이 되는 모듈식 설정 시스템은 Nix 언어로 작성되었으며, Nixpkgs의 패키지를 사용합니다. NixOS가 제공하는 운영 체제 환경과 서비스는 Nix 언어로 설정합니다.

:::

매우 복잡해 보이는 Nix 언어 표현식을 금방 마주칠 수 있습니다. 다른 모든 프로그래밍 언어와 마찬가지로, 필요한 Nix 언어 코드의 양은 해결하려는 문제의 복잡도와 밀접하게 일치하며, 그 문제와 해법을 얼마나 잘 이해하고 있는지를 반영합니다. 소프트웨어 빌드는 복잡한 작업이며, Nix는 Nix 언어를 통해 이 복잡성을 *드러내는* 동시에 *관리할 수 있게 해 줍니다*.

그러나 Nix 언어 자체에는 이 튜토리얼에서 소개할 몇 가지 기본 개념만 있으며, 이 개념들은 자유롭게 조합할 수 있습니다. 복잡해 보이는 부분은 언어 자체가 아니라 언어를 사용하는 방식에서 비롯됩니다.

## 개요

이 문서는 다른 튜토리얼과 예제를 따라갈 수 있도록 **Nix 언어 읽는 법**을 소개합니다.

실제로 **Nix 언어를 사용하는 것**에는 여러 가지가 포함됩니다:

- 언어: 구문과 의미론
- 라이브러리: `builtins`와 `pkgs.lib`
- 개발자 도구: 테스트, 디버깅, 린팅, 포매팅 등
- 범용 빌드 메커니즘: `stdenv.mkDerivation`, 빌드 헬퍼 등
- 조합 및 설정 메커니즘: `override`, `overrideAttrs`, 오버레이, `callPackage` 등
- 생태계별 패키징 메커니즘: `buildGoModule`, `buildPythonApplication` 등
- NixOS 모듈 시스템: `config`, `option` 등

이 튜토리얼은 가장 중요한 언어 기능만 다루고 라이브러리는 간략히 설명하며, 마지막에는 나머지 구성 요소에 대한 참조 자료와 리소스를 안내합니다.

### 무엇을 배우게 됩니까?

이 튜토리얼을 마치면 일반적인 Nix 언어 코드를 읽고 그 구조를 이해할 수 있습니다. 이 튜토리얼의 목표는 Nix 언어가 익숙한 언어와 어떤 점에서 다를 수 있는지 짚어 보는 것입니다.

따라서 Nix 언어에서 가장 흔하고 특징적인 패턴을 보여 줍니다:

- [이름 할당과 값 접근](names-values)
- [함수](functions) 선언과 호출
- [내장 함수와 라이브러리 함수](libraries)
- 빌드 입력을 얻기 위한 [비순수성](impurities)
- 빌드 작업을 기술하는 [derivation](derivations)

:::{important}
이 튜토리얼은 Nix 언어의 모든 기능을 자세히 설명하지 *않으며*, 구문 규칙의 세부 사항도 다루지 *않습니다*. 예를 들어, 이 튜토리얼은 `if ... then ... else ...`와 같은 흔한 구문은 건너뜁니다.

전체 언어 참조는 [Nix 매뉴얼][manual-language]을 참고하십시오.
:::

[manual-language]: https://nix.dev/manual/nix/stable/language/index.html

### 무엇이 필요합니까?

- 소프트웨어 개발에 대한 이해
- 명령줄 예제를 읽을 수 있도록 Unix 셸에 익숙해야 합니다 <!-- TODO: link to yet-to-be instructions on "how to read command line examples" -->
- 예제를 실행하기 위한 {ref}`Nix 설치 <install-nix>`

### 얼마나 걸립니까?

- 함수형 프로그래밍 경험이 없는 경우: 2시간
- 함수형 프로그래밍에 익숙한 경우: 1시간
- 함수형 프로그래밍에 능숙한 경우: 30분

모든 예제를 실행해 보십시오. 예제를 직접 바꿔 보면서 여러분의 가정을 검증하고 배운 내용을 시험해 보십시오. 예제를 완전히 이해했는지 확인하고 싶다면 자세한 설명을 읽어 보십시오.

### 예제는 어떻게 실행합니까?

- Nix 언어 코드 조각을 *Nix 표현식*이라고 합니다.
- Nix 표현식을 평가하면 *Nix 값*이 만들어집니다.
- *Nix 파일*(파일 확장자 `.nix`)의 내용은 Nix 표현식입니다.

:::{note}
*평가*한다는 것은 언어 규칙에 따라 표현식을 값으로 변환한다는 뜻입니다.
:::

이 튜토리얼에는 Nix 표현식 예제가 많이 들어 있습니다. 각 예제 뒤에는 예상되는 평가 결과가 나옵니다.

다음 예제는 두 수를 더하는 Nix 표현식입니다.

```{code-block} nix
:class: expression
1 + 2
```

```{code-block}
:class: value
3
```

#### 대화형 평가

Nix 표현식을 대화형으로(명령줄에 입력하여) 평가하려면 [`nix repl`]을 사용하십시오.

```shell-session
$ nix repl
Welcome to Nix 2.13.3. Type :? for help.

nix-repl> 1 + 2
3
```

:::{note}
Nix 언어는 지연 평가를 사용하며, `nix repl`은 기본적으로 필요할 때만 값을 계산합니다.

일부 예제는 이해를 돕기 위해 완전히 평가된 데이터 구조를 보여 줍니다. 출력이 예제와 일치하지 않으면 입력 표현식 앞에 `:p`를 붙여 보십시오.

예시:

```shell-session
nix-repl> { a.b.c = 1; }
{ a = { ... }; }

nix-repl> :p { a.b.c = 1; }
{ a = { b = { c = 1; }; }; }
```

[`nix repl`]을 종료하려면 `:q`를 입력하십시오.

:::

[`nix repl`]: https://nix.dev/manual/nix/stable/command-ref/new-cli/nix3-repl.html

#### Nix 파일 평가하기

Nix 파일 안의 표현식을 평가하려면 [`nix-instantiate --eval`][nix-instantiate]을 사용하십시오.

```shell-session
$ echo 1 + 2 > file.nix
$ nix-instantiate --eval file.nix
3
```

:::{dropdown} 자세한 설명

첫 번째 명령은 현재 디렉터리의 `file.nix` 파일에 `1 + 2`를 씁니다. 이제 `file.nix`의 내용은 `1 + 2`이며, 다음 명령으로 확인할 수 있습니다.

```shell-session
$ cat file.nix
1 + 2
```

두 번째 명령은 `file.nix`를 대상으로 `--eval` 옵션을 붙여 `nix-instantiate`를 실행합니다. 이 명령은 파일을 읽고 그 안에 담긴 Nix 표현식을 평가합니다. 결과 값이 출력됩니다.

다른 작업 없이 파일을 평가하기만 하려면 `--eval`이 필요합니다. `--eval`을 생략하면 `nix-instantiate`는 주어진 파일의 표현식이 *derivation*이라는 특수한 값으로 평가되기를 기대합니다. derivation은 이 튜토리얼 마지막 부분의 [](derivations)에서 다룹니다.

:::

:::{note}
파일 이름을 지정하지 않으면 `nix-instantiate --eval`은 `default.nix`에서 읽으려고 시도합니다.

```shell-session
$ echo 1 + 2 > default.nix
$ nix-instantiate --eval
3
```
:::

:::{note}
Nix 언어는 지연 평가를 사용하며, `nix-instantiate`는 기본적으로 필요할 때만 값을 계산합니다.

일부 예제는 이해를 돕기 위해 완전히 평가된 데이터 구조를 보여 줍니다. 출력이 예제와 일치하지 않으면 `nix-instantiate`에 `--strict` 옵션을 추가해 보십시오.

예시:

```shell-session
$ echo "{ a.b.c = 1; }" > file.nix
$ nix-instantiate --eval file.nix
{ a = <CODE>; }
```

```shell-session
$ echo "{ a.b.c = 1; }" > file.nix
$ nix-instantiate --eval --strict file.nix
{ a = { b = { c = 1; }; }; }
```

:::

[nix-instantiate]: https://nix.dev/manual/nix/stable/command-ref/nix-instantiate.html

### 공백에 관한 참고 사항

공백은 필요한 곳에서 [어휘 토큰][lexical tokens]을 구분하는 데 사용됩니다. 그 외에는 아무 의미가 없습니다.

[lexical tokens]: https://en.wikipedia.org/wiki/Lexical_analysis#Lexical_token_and_lexical_tokenization

줄 바꿈, 들여쓰기, 추가 공백은 읽는 사람의 편의를 위한 것입니다.

다음은 모두 동일합니다.

```{code-block} nix
:class: expression
let
 x = 1;
 y = 2;
in x + y
```

```{code-block}
:class: value
3
```

```{code-block} nix
:class: expression
let x=1;y=2;in x+y
```

```{code-block}
:class: value
3
```

(names-values)=
## 이름과 값

Nix 언어의 값은 원시 데이터 타입, 리스트, 속성 집합, 함수일 수 있습니다.

원시 데이터 타입과 리스트의 예시는 [속성 집합](attrset)의 맥락에서 나옵니다. 이 절의 뒷부분에서는 문자열의 특수한 기능인 [문자열 보간](string-interpolation), [파일 시스템 경로](file-system-paths), [들여쓰기된 문자열](indented-strings)을 접하게 됩니다. [함수](functions)는 별도로 다룹니다.

[속성 집합](attrset)과 [`let` 표현식](let)은 값에 이름을 할당하는 데 사용됩니다. 할당은 등호 하나(`=`)로 나타냅니다.

Nix 언어 코드에서 등호(`=`)를 만날 때마다 다음을 기억하십시오.
- 왼쪽에는 할당되는 이름이 있습니다.
- 오른쪽에는 값이 있으며, 값은 세미콜론(`;`)으로 끝을 구분합니다.

(attrset)=
### 속성 집합 `{ ... }`

속성 집합은 이름-값 쌍의 모음이며, 이름은 고유해야 합니다.

다음 예제는 모든 원시 데이터 타입, 리스트, 속성 집합을 보여 줍니다.

:::{note}
JSON에 익숙하다면 Nix 언어를 *함수가 있는 JSON*이라고 생각하십시오.

*함수를 제외한* Nix 언어 데이터 타입은 JSON의 대응 타입과 똑같이 동작하며 생김새도 매우 비슷합니다.
:::


::::{grid} 2

:::{grid-item} **Nix**
```nix
{
  string = "hello";
  integer = 1;
  float = 3.141;
  bool = true;
  null = null;
  list = [ 1 "two" false ];
  attribute-set = {
    a = "hello";
    b = 2;
    c = 2.718;
    d = false;
  }; # comments are supported
}
```
:::

:::{grid-item} **JSON**
```json
{
  "string": "hello",
  "integer": 1,
  "float": 3.141,
  "bool": true,
  "null": null,
  "list": [1, "two", false],
  "object": {
    "a": "hello",
    "b": 1,
    "c": 2.718,
    "d": false
  }
}
```
:::

::::

:::{note}
- [속성 집합 문법](https://nix.dev/manual/nix/stable/language/syntax#attrs-literal): 속성 이름에는 보통 따옴표가 필요하지 않습니다
- [리스트 문법](https://nix.dev/manual/nix/stable/language/syntax#list-literal): 리스트 요소는 공백으로 구분됩니다
:::



(rec-attrset)=
#### 재귀 속성 집합 `rec { ... }`

앞에 `rec`을 붙여 선언한 속성 집합을 때때로 보게 됩니다. 이렇게 하면 집합 안에서 속성에 접근할 수 있습니다.

예시:

```{code-block} nix
:class: expression
rec {
  one = 1;
  two = one + 1;
  three = two + 1;
}
```

```{code-block}
:class: value
{ one = 1; three = 3; two = 2; }
```

:::{note}
속성 집합의 원소는 어떤 순서로든 선언할 수 있으며, 평가 시에 정렬됩니다.
:::

반례:

```{code-block} nix
:class: expression
{
  one = 1;
  two = one + 1;
  three = two + 1;
}
```

```{code-block}
:class: value
error: undefined variable 'one'

       at «string»:3:9:

            2|   one = 1;
            3|   two = one + 1;
             |         ^
            4|   three = two + 1;
```

(let)=
### `let ... in ...`

“`let` 표현식” 또는 “`let` 바인딩”이라고도 합니다

`let` 표현식을 사용하면 값에 이름을 할당하여 반복해서 사용할 수 있습니다.

예시:

```{code-block} nix
:class: expression
let
  a = 1;
in
a + a
```

```{code-block}
:class: value
2
```

:::{dropdown} 자세한 설명

할당은 `let`과 `in` 키워드 사이에 둡니다. 이 예시에서는 `a = 1`을 할당합니다.

`in` 뒤에는 할당이 유효한 표현식, 즉 할당된 이름을 사용할 수 있는 표현식이 옵니다. 이 예시에서 표현식은 `a + a`이며, 여기서 `a`는 `a = 1`을 가리킵니다.

이름을 할당된 값으로 바꾸면 `a + a`는 `2`로 평가됩니다.

:::

이름은 어떤 순서로든 할당할 수 있으며, 할당(`=`)의 오른쪽에 있는 표현식은 할당된 다른 이름을 참조할 수 있습니다.

예시:

```{code-block} nix
:class: expression
let
  b = a + 1;
  a = 1;
in
a + b
```

```{code-block}
:class: value
3
```

:::::{dropdown} 자세한 설명

할당은 `let`과 `in` 키워드 사이에 둡니다. 이 예시에서는 `a = 1`과 `b = a + 1`을 할당합니다.

할당 순서는 중요하지 않습니다. 따라서 할당 순서를 거꾸로 한 다음 예시도 동등합니다:


```{code-block} nix
:class: expression
let
  a = 1;
  b = a + 1;
in
a + b
```

```{code-block}
:class: value
3
```

`b = a + 1`의 `a`는 `a = 1`을 가리킨다는 점에 유의하십시오.

`in` 뒤에는 할당이 유효한 표현식이 옵니다. 이 예시에서 표현식은 `a + b`이며, 여기서 `a`는 `a = 1`을, `b`는 `b = a + 1`을 가리킵니다.

이름을 할당된 값으로 바꾸면 `a + b`는 `3`으로 평가됩니다.

이는 [재귀 속성 집합](rec-attrset)과 비슷합니다. 두 경우 모두 할당 순서는 중요하지 않으며, 왼쪽의 이름을 할당(`=`) 오른쪽의 표현식에서 사용할 수 있습니다.

예시:

::::{grid} 2

:::{grid-item} `let ... in ...`

```{code-block} nix
:class: expression
let
  b = a + 1;
  c = a + b;
  a = 1;
in {  c = c; a = a; b = b; }
```

```{code-block}
:class: value
{ a = 1; b = 2; c = 3; }
```

:::

:::{grid-item} `rec { ... }`

```{code-block} nix
:class: expression
rec {
  b = a + 1;
  c = a + b;
  a = 1;
}
```

```{code-block}
:class: value
{ a = 1; b = 2; c = 3; }
```

:::

::::

차이점은 재귀 속성 집합은 [속성 집합](attrset)으로 평가되지만, `in` 키워드 뒤에는 어떤 표현식이든 올 수 있다는 것입니다.

다음 예시에서는 `let` 표현식을 사용하여 리스트를 만듭니다.

```{code-block} nix
:class: expression
let
  b = a + 1;
  c = a + b;
  a = 1;
in [ a b c ]
```

```{code-block}
:class: value
[ 1 2 3 ]
```

:::::

새로 선언된 이름에는 `let` 표현식 자체 안에 있는 표현식만 접근할 수 있습니다. 이 바인딩은 지역 스코프를 가집니다.

반례:

```{code-block} nix
:class: expression
{
  a = let x = 1; in x;
  b = x;
}
```

```{code-block}
:class: value
error: undefined variable 'x'

       at «string»:3:7:

            2|   a = let x = 1; in x;
            3|   b = x;
             |       ^
            4| }
```

<!-- TODO: exercise - use let to reuse a value in an attribute set -->

### 속성 접근

집합의 속성에는 점(`.`)과 속성 이름으로 접근합니다.

예시:

```{code-block} nix
:class: expression
let
  attrset = { x = 1; };
in
attrset.x
```

```{code-block}
:class: value
1
```

중첩된 속성에 접근하는 방식도 같습니다.

예시:

```{code-block} nix
:class: expression
let
  attrset = { a = { b = { c = 1; }; }; };
in
attrset.a.b.c
```

```{code-block}
:class: value
1
```

점(`.`) 표기법은 속성을 할당할 때도 사용할 수 있습니다.

예시:

```{code-block} nix
:class: expression
{ a.b.c = 1; }
```

```{code-block}
:class: value
{ a = { b = { c = 1; }; }; }
```

(with)=
### `with ...; ...`

`with` 표현식을 사용하면 속성 집합을 반복해서 참조하지 않고도 그 속성에 접근할 수 있습니다.

예시:

```{code-block} nix
:class: expression
let
  a = {
    x = 1;
    y = 2;
    z = 3;
  };
in
with a; [ x y z ]
```

```{code-block}
:class: value
[ 1 2 3 ]
```

다음 표현식은

```{code-block} nix
with a; [ x y z ]
```

다음과 동등합니다.

```{code-block} nix
[ a.x a.y a.z ]
```

`with`로 사용할 수 있게 된 속성은 세미콜론(`;`) 뒤에 오는 표현식의 스코프 안에서만 유효합니다.

반례:

```{code-block} nix
:class: expression
let
  a = {
    x = 1;
    y = 2;
    z = 3;
  };
in
{
  b = with a; [ x y z ];
  c = x;
}
```

```{code-block}
:class: value
error: undefined variable 'x'

       at «string»:10:7:

            9|   b = with a; [ x y z ];
           10|   c = x;
             |       ^
           11| }
```

(inherit)=
### `inherit ...`

`inherit`은 기존 스코프에 있는 이름의 값을 중첩된 스코프의 같은 이름에 할당하는 축약 표현입니다. 같은 이름을 여러 번 반복하지 않도록 편의를 위해 존재합니다.

예시:

```{code-block} nix
:class: expression
let
  x = 1;
  y = 2;
in
{
  inherit x y;
}
```

```{code-block}
:class: value
{ x = 1; y = 2; }
```

다음 코드 조각은

```{code-block} nix
inherit x y;
```
다음과 동등합니다.

```{code-block} nix
x = x; y = y;
```

(inherit-from-attrset)=
### `inherit (...) ...`

특정 속성 집합의 이름을 괄호로 감싸서 그 속성 집합에서 이름을 `inherit`할 수도 있습니다.

예시:

```{code-block} nix
:class: expression
let
  a = { x = 1; y = 2; };
in
{
  inherit (a) x y;
}
```

```{code-block}
:class: value
{ x = 1; y = 2; }
```

다음 코드 조각은

```{code-block} nix
inherit (a) x y;
```

다음과 동등합니다

```{code-block} nix
x = a.x; y = a.y;
```

`inherit`는 `let` 표현식 안에서도 작동합니다.

예시:

```{code-block} nix
:class: expression
let
  a = { x = 1; y = 2; };
  inherit (a) x y;
in [ x y ]
```

```{code-block}
:class: value
[ 1 2 ]
```

:::{dropdown} 자세한 설명

이 예시는 인위적이지만, 더 복잡한 코드에서는 바깥 스코프의 이름을 다시 사용하는 중첩된 [`let` 표현식](let)을 자주 보게 됩니다.

여기서는 상속할 대상으로 사소하지 않은 값을 두기 위해 속성 집합 `a = { x = 1; y = 2; }`를 사용합니다. `let` 표현식은 `( )`를 사용하여 `a`에서 `x`와 `y`를 상속하며, 이는 다음과 같이 작성하는 것과 동등합니다:

```{code-block} nix
let
  x = a.x;
  y = a.y;
in
```

이제 새 내부 스코프에는 `x`와 `y`가 포함되며, 이들은 리스트 `[ x y ]`에서 사용됩니다.

:::

(string-interpolation)=
### 문자열 보간 `${ ... }`

이전에는 “안티쿼테이션(antiquotation)”이라고 불렸습니다.

Nix 표현식의 값은 달러 기호와 중괄호(`${ }`)를 사용하여 문자열에 삽입할 수 있습니다.

예시:

```{code-block} nix
:class: expression
let
  name = "Nix";
in
"hello ${name}"
```

```{code-block}
:class: value
"hello Nix"
```

문자열 또는 문자열로 표현할 수 있는 값만 허용됩니다.

반례:

```{code-block} nix
:class: expression
let
  x = 1;
in
"${x} + ${x} = ${x + x}"
```

```{code-block}
:class: value
error: cannot coerce an integer to a string

       at «string»:4:2:

            3| in
            4| "${x} + ${x} = ${x + x}"
             |  ^
            5|
```

보간된 표현식은 임의로 중첩할 수 있습니다.

(이렇게 하면 읽기 어려워질 수 있습니다. 실제로는 피하십시오.)

예시:

```{code-block} nix
:class: expression
let
  a = "no";
in
"${a + " ${a + " ${a}"}"}"
```

```{code-block}
:class: value
"no no no"
```

:::{dropdown} 자세한 설명
값을 문자열로 표현할 수 있는 Nix 표현식이라면 어떤 것이든 `${ }` 안에서 사용할 수 있습니다.

위 표현식의 `+` 기호는 [문자열 연결 연산자](https://nix.dev/manual/nix/latest/language/operators#string-concatenation)로, 두 문자열을 받아 새 문자열을 만듭니다.

예시의 표현식은 문자열 보간을 임의로 중첩할 수는 있지만 읽기 어려워지기 쉽다는 점을 보여 주기 위해 일부러 헷갈리게 작성되었습니다.

이 표현식은 `a`의 값과, 공백으로 시작하고 그 뒤에 또 다른 보간된 문자열이 오는 문자열을 연결한 결과를 보간한 문자열을 나타냅니다. 두 번째 보간된 문자열 역시 `a`의 값과, 공백으로 시작하고 그 뒤에 `a`의 보간이 오는 또 다른 문자열을 연결한 결과입니다.

예시:
```{code-block} nix
:class: expression
let
  a = "one";
  b = "two";
in
"${a + b}"
```

```{code-block}
:class: value
"onetwo"
```

내장 함수는 [이후 섹션](libraries)에서 다룹니다.
:::

:::{warning}
할당된 이름 앞에 달러 기호(`$`)는 있지만 중괄호(`{ }`)는 없는 문자열을 볼 수도 있습니다:

이러한 문자열은 보간된 문자열이 *아니며*, 보통 셸 스크립트의 변수를 나타냅니다.

이런 경우 주변 Nix 표현식의 이름이 사용된 것은 우연입니다.

예시:

```{code-block} nix
:class: expression
let
  out = "Nix";
in
"echo ${out} > $out"
```

```{code-block}
:class: value
"echo Nix > $out"
```
:::

<!-- TODO: link to escaping rules -->

(indented-strings)=
### 들여쓰기된 문자열

“여러 줄 문자열(multi-line strings)”이라고도 합니다.

Nix 언어는 여러 줄에 걸쳐 있고 공통된 들여쓰기가 있는 문자열을 위한 편의 문법을 제공합니다.

들여쓰기된 문자열은 *두 개의 작은따옴표*(`'' ''`)로 표기합니다.

예시:

```{code-block} nix
:class: expression
''
multi
line
string
''
```

```{code-block}
:class: value
"multi\nline\nstring\n"
```

각 줄 앞에 공통으로 붙은 같은 양의 공백은 결과에서 제거됩니다.

예시:

```{code-block} nix
:class: expression
''
  one
   two
    three
''
```

```{code-block}
:class: value
"one\n two\n  three\n"
```

:::{note}
들여쓰기된 문자열은 [문자열 보간](string-interpolation)도 지원합니다. 자세한 내용은 [Nix 언어의 문자열 리터럴에 관한 문서](https://nix.dev/manual/nix/2.24/language/syntax#string-literal)를 확인하십시오.
:::

(file-system-paths)=
### 파일 시스템 경로

Nix 언어는 파일 시스템 경로를 위한 편의 문법을 제공합니다.

절대 경로는 항상 슬래시(`/`)로 시작합니다.

예시:

```{code-block} nix
:class: expression
/absolute/path
```

```{code-block}
:class: value
/absolute/path
```

경로에 슬래시(`/`)가 하나 이상 있지만 슬래시로 시작하지 않으면 상대 경로입니다. 상대 경로는 해당 표현식이 들어 있는 파일을 기준으로 한 경로로 평가됩니다.

다음 예시에서는 해당 Nix 파일이 `/current/directory`에 있다고(또는 `nix repl`을 `/current/directory`에서 실행한다고) 가정합니다.

예시:


```{code-block} nix
:class: expression
./relative
```

```{code-block}
:class: value
/current/directory/relative
```

예시:

```{code-block} nix
:class: expression
relative/path
```

```{code-block}
:class: value
/current/directory/relative/path
```

점 하나(`.`)는 주어진 경로 안에서 현재 디렉터리를 나타냅니다.

Nix 파일이 있는 디렉터리를 지정하는 다음 표현식을 자주 보게 될 것입니다.

예시:

```{code-block} nix
:class: expression
./.
```

```{code-block}
:class: value
/current/directory
```

:::{dropdown} 자세한 설명

상대 경로에는 슬래시(`/`)가 있어야 하지만 슬래시로 시작해서는 안 되고, 점(`.`)은 디렉터리를 바꾸지 않음을 뜻하므로, `./.` 조합은 현재 디렉터리를 상대 경로로 지정합니다.

:::

점 두 개(`..`)는 상위 디렉터리를 나타냅니다.

예시:

```{code-block} nix
:class: expression
../.
```

```{code-block}
:class: value
/current
```
:::{note}
경로는 보간된 표현식 안에서 사용할 수 있습니다. 이는 [이후 섹션](path-impurities)에서 자세히 다루는 [순수하지 않은 연산](impurities)입니다.
:::

(lookup-path-tutorial)=
#### 조회 경로

“꺾쇠괄호 문법(angle bracket syntax)”이라고도 합니다.

예시:

```{code-block} nix
:class: expression
<nixpkgs>
```

```{code-block}
:class: value
/nix/var/nix/profiles/per-user/root/channels/nixpkgs
```

[조회 경로](https://nix.dev/manual/nix/2.22/language/constructs/lookup-path)의 값은 [`builtins.nixPath`](https://nix.dev/manual/nix/2.22/language/builtin-constants#builtins-nixPath)의 값에 따라 달라지는 파일 시스템 경로입니다.

실제로 `<nixpkgs>`는 {term}`Nixpkgs`의 특정 리비전이 있는 파일 시스템 경로를 가리킵니다.

예를 들어 `<nixpkgs/lib>`는 해당 파일 시스템 경로의 하위 디렉터리 `lib`를 가리킵니다:

```{code-block} nix
:class: expression
<nixpkgs/lib>
```

```{code-block}
:class: value
/nix/var/nix/profiles/per-user/root/channels/nixpkgs/lib
```

이러한 예시를 많이 접하게 되겠지만, 조회 경로는 재현 가능하지 않은 [비순수성](impurities)이므로 프로덕션 코드에서는 [조회 경로를 피하십시오](search-path).

[NIX_PATH]: https://nix.dev/manual/nix/stable/command-ref/env-common.html?highlight=nix_path#env-NIX_PATH
[nixpkgs]: https://github.com/NixOS/nixpkgs
[manual-primitives]: https://nix.dev/manual/nix/stable/language/values.html#primitives

(functions)=
## 함수

함수는 Nix 언어 어디에나 등장하므로 특별히 주의를 기울일 필요가 있습니다.

함수는 항상 정확히 하나의 인자를 받습니다. 인자와 함수 본문은 콜론(`:`)으로 구분합니다.

Nix 언어 코드에서 콜론(`:`)이 보이면 어디서든 다음과 같습니다:
- 왼쪽에는 함수 인자가 있습니다
- 오른쪽에는 함수 본문이 있습니다.

함수 인자는 [속성 집합](attrset)과 [`let` 표현식](let) 외에 값에 이름을 붙이는 세 번째 방법입니다. 특히 값은 미리 알 수 없습니다. 이름은 [함수를 호출할](calling-functions) 때 채워지는 자리 표시자입니다.

Nix 언어의 함수 선언은 여러 가지 형태로 나타날 수 있습니다. 각 형태는 아래에서 차례로 설명하며, 개요는 다음과 같습니다:

- 단일 인자

  ```{code-block} nix
  x: x + 1
  ```

  - 중첩을 통한 여러 인자

    ```{code-block} nix
    x: y: x + y
    ```

- 속성 집합 인수

  ```{code-block} nix
  { a, b }: a + b
  ```

  - 기본 속성 포함

    ```{code-block} nix
    { a, b ? 0 }: a + b
    ```

  - 추가 속성 허용

    ```{code-block} nix
    { a, b, ...}: a + b
    ```

- 이름이 지정된 속성 집합 인수

  ```{code-block} nix
  args@{ a, b, ... }: a + b + args.c
  ```

  또는

  ```{code-block} nix
  { a, b, ... }@args: a + b + args.c
  ```

Nix 언어의 함수에는 이름이 없습니다. 함수는 익명이며, 이러한 함수를 *람다*라고 부릅니다.[^lambda]

[^lambda]: *람다*라는 용어는 [람다 대수](https://en.wikipedia.org/wiki/Lambda_calculus)의 [람다 추상화](https://en.wikipedia.org/wiki/Lambda_calculus#lambdaAbstr)를 줄여 부르는 말입니다.

예시:

```{code-block} nix
:class: expression
x: x + 1
```

```{code-block} nix
:class: value
<LAMBDA>
```

`<LAMBDA>`는 결괏값이 익명 함수임을 나타냅니다.

다른 값과 마찬가지로 함수에도 이름을 붙일 수 있습니다.

예시:

```{code-block} nix
:class: expression
let
  f = x: x + 1;
in f
```

```{code-block} nix
:class: value
<LAMBDA>
```

(calling-functions)=
### 함수 호출

"함수 적용"이라고도 합니다.

인자를 넘겨 함수를 호출한다는 것은 함수 뒤에 인자를 적는다는 뜻입니다.

예시:

```{code-block} nix
:class: expression
let
  f = x: x + 1;
in f 1
```

```{code-block}
:class: value
2
```

예시:

```{code-block} nix
:class: expression
let
  f = x: x.a;
in
f { a = 1; }
```

```{code-block}
:class: value
1
```

위 예시는 리터럴 속성 집합을 인자로 하여 `f`를 호출합니다. 인자를 이름으로 전달할 수도 있습니다.

예시:

```{code-block} nix
:class: expression
let
  f = x: x.a;
  v = { a = 1; };
in
f v
```

```{code-block}
:class: value
1
```

함수와 인수는 공백으로 구분되므로, 원하는 결과를 얻으려면 괄호(`( )`)가 필요한 경우가 있습니다.

예시:

```{code-block} nix
:class: expression
(x: x + 1) 1
```

```{code-block}
:class: value
2
```

:::{dropdown} 자세한 설명

이 표현식은 익명 함수 `x: x + 1`을 인수 `1`에 적용합니다. 함수를 인수와 구별하려면 함수를 괄호로 감싸야 합니다.

:::

예시:

리스트 요소 역시 공백으로 구분되므로, 다음은 서로 다릅니다:

```{code-block} nix
:class: expression
let
 f = x: x + 1;
 a = 1;
in [ (f a) ]
```

```{code-block} nix
:class: value
[ 2 ]
```

```{code-block} nix
:class: expression
let
 f = x: x + 1;
 a = 1;
in [ f a ]
```

```{code-block}
:class: value
[ <LAMBDA> 1 ]
```

첫 번째 예시는 `f`를 `a`에 적용하고 그 결과를 리스트에 넣으라는 뜻입니다. 결과 리스트에는 원소가 하나 있습니다.

두 번째 예시는 `f`와 `a`를 리스트에 넣으라는 뜻입니다. 결과 리스트에는 원소가 두 개 있습니다.

#### 여러 인수

“[커링된][curried] 함수”라고도 합니다.

Nix 함수는 정확히 하나의 인수를 받습니다. 여러 인수는 함수를 중첩하여 처리할 수 있습니다.

이렇게 중첩된 함수는 여러 인수를 받는 함수처럼 사용할 수 있으면서도 더 많은 유연성을 제공합니다.

[curried]: https://en.wikipedia.org/wiki/Currying

예시:

```{code-block} nix
:class: expression
x: y: x + y
```

```{code-block}
:class: value
<LAMBDA>
```

위 함수는 다음과 동일합니다.

```{code-block} nix
:class: expression
x: (y: x + y)
```

```{code-block}
:class: value
<LAMBDA>
```

이 함수는 인수 하나를 받아, `x`가 그 인수의 값으로 설정된 또 다른 함수 `y: x + y`를 반환합니다.

예시:

```{code-block} nix
:class: expression
let
  f = x: y: x + y;
in
f 1
```

```{code-block}
:class: value
<LAMBDA>
```

`f 1`의 결과로 나온 함수를 다른 인수에 적용하면 내부 본문 `x + y`(`x`는 `1`로, `y`는 그 다른 인수로 설정됨)가 나오며, 이제 이를 완전히 평가할 수 있습니다.

```{code-block} nix
:class: expression
let
  f = x: y: x + y;
in
f 1 2
```

```{code-block}
:class: value
3
```

<!-- TODO: exercise - assign the lambda a name and do something with it -->

### 속성 집합 인수

“키워드 인수” 또는 “구조 분해”라고도 합니다.

Nix 함수는 특정 구조를 가진 속성 집합을 인수로 요구하도록 선언할 수 있습니다.

이는 기대하는 속성 이름을 쉼표(`,`)로 구분하여 나열하고 중괄호(`{ }`)로 감싸서 나타냅니다.

예시:

```{code-block} nix
:class: expression
{a, b}: a + b
```

```{code-block} nix
:class: value
<LAMBDA>
```

인수는 해당 집합에 반드시 있어야 하는 속성을 정확히 정의합니다. 속성을 빠뜨리거나 추가 속성을 전달하면 오류가 발생합니다.

예시:

```{code-block} nix
:class: expression
let
  f = {a, b}: a + b;
in
f { a = 1; b = 2; }
```

```{code-block} nix
:class: value
3
```

반례:

```{code-block} nix
:class: expression
let
  f = {a, b}: a + b;
in
f { a = 1; b = 2; c = 3; }
```

```{code-block}
:class: value
error: 'f' at (string):2:7 called with unexpected argument 'c'

       at «string»:4:1:

            3| in
            4| f { a = 1; b = 2; c = 3; }
             | ^
            5|
```

<!-- TODO: not the same as x: x.a + x.b (!!!!) -->

#### 기본값

“기본 인수(default arguments)”라고도 합니다.

구조 분해된 인수는 속성에 대한 기본값을 가질 수 있습니다.

속성 이름과 기본값을 물음표(`?`)로 구분하여 표기합니다.

인수의 속성에 기본값이 있으면 그 속성은 필수가 아닙니다.

예시:

```{code-block} nix
:class: expression
let
  f = {a, b ? 0}: a + b;
in
f { a = 1; }
```

```{code-block}
:class: value
1
```

예시:

```{code-block} nix
:class: expression
let
  f = {a ? 0, b ? 0}: a + b;
in
f { } # empty attribute set
```

```{code-block}
:class: value
0
```

#### 추가 속성

말줄임표(`...`)를 사용하면 추가 속성을 허용할 수 있습니다:

```{code-block} nix
{a, b, ...}: a + b
```

앞의 반례와 달리, 추가 속성이 포함된 인수를 전달해도 오류가 발생하지 않습니다.

예시:

```{code-block} nix
:class: expression
let
  f = {a, b, ...}: a + b;
in
f { a = 1; b = 2; c = 3; }
```

```{code-block}
:class: value
3
```

### 이름이 지정된 속성 집합 인수

“@ 패턴”, “@ 구문” 또는 “‘at’ 구문”이라고도 합니다.

속성 집합 인수 전체에 접근할 수 있도록 이름을 붙일 수 있습니다.

속성 집합 인수의 앞이나 뒤에 이름을 붙이고 골뱅이 기호(`@`)로 구분하여 표기합니다.

예시:

```{code-block} nix
:class: expression
{a, b, ...}@args: a + b + args.c
```

```{code-block}
:class: value
<LAMBDA>
```

또는

```{code-block} nix
:class: expression
args@{a, b, ...}: a + b + args.c
```

```{code-block}
:class: value
<LAMBDA>
```

예시:

```{code-block} nix
:class: expression
let
  f = {a, b, ...}@args: a + b + args.c;
in
f { a = 1; b = 2; c = 3; }
```

```{code-block} nix
:class: value
6
```

(libraries)=
## 함수 라이브러리

[내장 연산자][operators](`+`, `==`, `&&` 등) 외에도, *함께* Nix 언어의 표준으로 볼 수 있는 널리 사용되는 라이브러리가 두 가지 있습니다. Nix 언어 코드를 이해하고 탐색하려면 두 라이브러리를 모두 알아야 합니다.

<!-- TODO: find a place for operators -->

어떤 기능이 제공되는지 익숙해지도록 대략 훑어보십시오.

[operators]: https://nix.dev/manual/nix/stable/language/operators.html

(builtins)=
### `builtins`

“기본 연산(primitive operations)” 또는 “primops”라고도 합니다.

Nix에는 언어에 내장된 함수가 많이 있습니다. 이 함수들은 Nix 언어 인터프리터의 일부로서 C++로 구현되어 있습니다.

:::{note}
Nix 매뉴얼에는 모든 [내장 함수][nix-builtins]와 그 사용법이 나와 있습니다.
:::

이 함수들은 `builtins` 상수를 통해 사용할 수 있습니다.

예시:

```{code-block} nix
:class: expression
builtins.toString
```

```{code-block}
:class: value
<PRIMOP>
```

[nix-builtins]: https://nix.dev/manual/nix/stable/language/builtins.html

(reading-nix-language-import)=
#### `import`

대부분의 내장 함수는 `builtins`를 통해서만 접근할 수 있습니다. 주목할 만한 예외는 `import`로, 최상위 수준에서도 사용할 수 있습니다.

`import`는 Nix 파일의 경로를 받아 파일을 읽고, 그 안에 담긴 Nix 표현식을 평가한 다음 결과 값을 반환합니다. 경로가 디렉터리를 가리키면 그 디렉터리의 `default.nix` 파일이 대신 사용됩니다.

예시:

```shell-session
$ echo 1 + 2 > file.nix
```

```{code-block} nix
:class: expression
import ./file.nix
```

```{code-block}
:class: value
3
```

:::{dropdown} 자세한 설명

앞의 셸 명령은 현재 디렉터리의 `file.nix` 파일에 `1 + 2`라는 내용을 씁니다.

위의 Nix 표현식은 이 파일을 `./file.nix`로 참조합니다. `import`는 파일을 읽고, 그 안에 담긴 Nix 표현식으로 평가됩니다.

파일 시스템 경로가 존재하지 않으면 오류가 발생합니다.

`file.nix`를 읽고 나면 이 Nix 표현식은 파일 내용과 동일합니다:

```{code-block} nix
:class: expression
1 + 2
```

```{code-block}
:class: value
3
```
:::

Nix 파일에는 어떤 Nix 표현식이든 담을 수 있으므로, `import`로 가져온 함수를 곧바로 인수에 적용할 수 있습니다.

`import` 호출 뒤에 추가 토큰이 있다면 반환된 값은 함수입니다. 그 뒤에 오는 것은 모두 해당 함수의 인수입니다.

예시:

```shell-session
$ echo "x: x + 1" > file.nix
```

```{code-block} nix
:class: expression
import ./file.nix 1
```

```{code-block}
:class: value
2
```

::::{dropdown} 자세한 설명

앞의 셸 명령은 현재 디렉터리의 `file.nix` 파일에 `x: x + 1`이라는 내용을 씁니다.

위의 Nix 표현식은 이 파일을 `./file.nix`로 참조합니다. `import ./file.nix`는 파일을 읽고, 그 안에 담긴 Nix 표현식으로 평가됩니다.

파일 시스템 경로가 존재하지 않으면 오류가 발생합니다.

파일을 읽고 나면 Nix 표현식 `import ./file.nix`는 파일 내용과 동일합니다:

```{code-block} nix
:class: expression
(x: x + 1) 1
```

```{code-block}
:class: value
2
```

이는 함수 `x: x + 1`을 인수 `1`에 적용하므로 `2`로 평가됩니다.

:::{note}
함수 선언과 함수 적용을 구분하려면 괄호가 필요합니다.
:::

::::

(pkgs-lib)=
### `pkgs.lib`

[`nixpkgs`][nixpkgs] 저장소에는 [`lib`][nixpkgs-lib]이라는 속성 집합이 있으며, 이 속성 집합은 유용한 함수를 많이 제공합니다. 이 함수들은 언어 자체의 일부인 [`builtins`](builtins)와 달리 Nix 언어로 구현되어 있습니다.

:::{note}
Nixpkgs 매뉴얼에는 모든 [Nixpkgs 라이브러리 함수][nixpkgs-functions]가 나열되어 있습니다.
:::

[nixpkgs-functions]: https://nixos.org/manual/nixpkgs/stable/#sec-functions-library
[nixpkgs-lib]: https://github.com/NixOS/nixpkgs/blob/master/lib/default.nix

관례상 Nixpkgs 속성 집합에 `pkgs`라는 이름을 붙이므로, 이 함수들에는 보통 `pkgs.lib`을 통해 접근합니다.

예시:

```{code-block} nix
:class: expression
let
  pkgs = import <nixpkgs> {};
in
pkgs.lib.strings.toUpper "lookup paths considered harmful"
```

```{code-block}
:class: value
LOOKUP PATHS CONSIDERED HARMFUL
```

:::{dropdown} 자세한 설명

더 복잡한 예시이지만, 이제는 모든 구성 요소가 익숙할 것입니다.

`pkgs`라는 이름은 어떤 파일에서 `import`로 가져온 표현식으로 선언됩니다. 그 파일의 경로는 조회 경로 `<nixpkgs>`의 값으로 결정되며, 이 값은 다시 이 표현식이 평가되는 시점의 `$NIX_PATH` 환경 변수로 결정됩니다. 이 표현식은 마침 함수이므로 평가하려면 인수가 필요하며, 이 경우에는 빈 속성 집합 `{}`를 전달하는 것으로 충분합니다.

이제 `pkgs`가 `let ... in ...`의 스코프 안에 있으므로 그 속성에 접근할 수 있습니다. Nixpkgs 매뉴얼을 보면 [`lib.strings.toUpper`] 아래에 함수가 있음을 알 수 있습니다.

[`lib.strings.toUpper`]: https://nixos.org/manual/nixpkgs/stable/#function-library-lib.strings.toUpper

간결하게 하기 위해 이 예제에서는 조회 경로를 사용하여 Nixpkgs의 *어떤 버전*을 가져옵니다. `toUpper` 함수는 충분히 단순하므로 Nixpkgs 버전이 달라도 다른 결과를 내지 않으리라 예상할 수 있습니다. 하지만 더 복잡한 소프트웨어는 이러한 문제를 겪을 가능성이 높습니다. 따라서 완전히 재현 가능한 예제는 다음과 같습니다.

```{code-block} nix
:class: expression
let
  nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/archive/06278c77b5d162e62df170fec307e83f1812d94b.tar.gz";
  pkgs = import nixpkgs {};
in
pkgs.lib.strings.toUpper "always pin your sources"
```

```{code-block}
:class: value
ALWAYS PIN YOUR SOURCES
```

자세한 내용은 [](pinning-nixpkgs) 문서를 참고하십시오.

또한 `pkgs`가 함수의 인자로 전달되는 경우도 자주 볼 수 있습니다. 관례상 이는 `lib` 속성을 가진 Nixpkgs 속성 집합을 가리킨다고 가정할 수 있습니다.

```{code-block} nix
:class: expression
{ pkgs, ... }:
pkgs.lib.strings.removePrefix "no " "no true scotsman"
```

```{code-block}
:class: value
<LAMBDA>
```

이 함수가 결과를 내도록 하려면 파일(예: `file.nix`)에 작성한 뒤 `nix-instantiate`를 통해 인자를 전달하면 됩니다.

```shell-session
$ nix-instantiate --eval file.nix --arg pkgs 'import <nixpkgs> {}'
"true scotsman"
```

NixOS 설정에서, 그리고 Nixpkgs 내부에서도 `lib`이 직접 전달되는 경우를 자주 볼 수 있습니다. 이 경우 이 `lib`은 `pkgs`만 사용할 수 있는 곳의 `pkgs.lib`과 동일하다고 가정할 수 있습니다.

예시:

```{code-block} nix
:class: expression
{ lib, ... }:
let
  to-be = true;
in
lib.trivial.or to-be (! to-be)
```

```{code-block}
:class: value
<LAMBDA>
```

이 함수가 결과를 내도록 하려면 파일(예: `file.nix`)에 작성한 뒤 `nix-instantiate`를 통해 인자를 전달하면 됩니다.

```shell-session
$ nix-instantiate --eval file.nix --arg lib '(import <nixpkgs> {}).lib'
true
```

때로는 `pkgs`와 `lib`이 모두 인자로 전달되기도 합니다. 이 경우 `pkgs.lib`과 `lib`은 동일하다고 가정할 수 있습니다. 이는 `pkgs.lib`을 반복해서 사용하지 않도록 하여 가독성을 높이기 위한 것입니다.

예시:

```{code-block} nix
{ pkgs, lib, ... }:
# ... multiple uses of `pkgs`
# ... multiple uses of `lib`
```

:::

역사적인 이유로 `pkgs.lib`의 일부 함수는 같은 이름의 [`builtins`](builtins)와 동일합니다.

(impurities)=
## 비순수성

지금까지 이 튜토리얼에서는 *순수 표현식*, 즉 데이터를 선언하고 함수로 변환하는 것만 다루었습니다.

실제로 derivation을 기술하려면 외부 세계를 관찰해야 합니다. derivation은 파일 시스템을 대상으로 함수형 프로그래밍을 가능하게 하는 Nix 언어의 핵심 기능입니다. [derivation](derivations)은 튜토리얼 뒷부분에서 다룹니다.

여기서 관련된 Nix 언어의 비순수성은 단 하나, 파일 시스템에서 파일을 *빌드 입력*으로 읽어 들이는 것입니다.

derivation은 새 파일을 도출하는 방법을 기술하기 위해 빌드 입력을 참조합니다. derivation은 실행될 때 명시적으로 선언된 빌드 입력에만 접근할 수 있습니다.

Nix 언어에서 빌드 입력을 지정하는 유일한 방법은 다음을 명시적으로 사용하는 것입니다.

- 파일 시스템 경로
- 전용 함수

Nix와 Nix 언어는 파일을 내용 해시로 참조합니다. 파일 내용을 미리 알 수 없다면 표현식 평가 중에 파일을 읽는 것은 피할 수 없습니다.

:::{note}
Nix는 [조회 경로](search-path)나 상수 [`builtins.currentSystem`](https://nix.dev/manual/nix/stable/language/builtin-constants.html#builtins-currentSystem)과 같은 다른 유형의 비순수 표현식도 지원합니다. 이들은 Nix 언어가 원칙적으로 작동하는 방식과는 관계가 없고, 바로 재현성을 해친다는 이유로 권장되지 않으므로 여기서는 더 자세히 다루지 않습니다.
:::

(path-impurities)=
### 경로

파일 시스템 경로가 [문자열 보간](string-interpolation)에 사용될 때마다, 부수 효과로 해당 파일의 내용이 파일 시스템의 특별한 위치인 *Nix 스토어*에 복사됩니다.

그러면 평가된 문자열에는 해당 파일에 할당된 Nix 스토어 경로가 포함됩니다.

<!-- TODO: link to explanation of the Nix store -->

예시:

```shell-session
$ echo 123 > data
```

```{code-block} nix
:class: expression
"${./data}"
```

```{code-block}
:class: value
"/nix/store/h1qj5h5n05b5dl5q4nldrqq8mdg7dhqk-data"
```

:::{dropdown} 자세한 설명

앞의 셸 명령은 현재 디렉터리의 `data` 파일에 문자 `123`을 기록합니다.

위의 Nix 표현식은 이 파일을 `./data`로 참조하고, 파일 시스템 경로를 [보간된 문자열](string-interpolation) `${ ... }`로 변환합니다.

이러한 보간된 표현식은 문자열로 표현할 수 있는 값으로 평가되어야 합니다. 파일 시스템 경로가 바로 그러한 값이며, 그 문자열 표현은 해당하는 Nix 스토어 경로입니다.

```{code-block}
/nix/store/<hash>-<name>
```

Nix 스토어 경로는 파일 내용의 해시(`<hash>`)를 구하고 이를 파일 이름(`<name>`)과 결합하여 얻습니다. 평가의 부수 효과로 파일이 Nix 스토어 디렉터리 `/nix/store`에 복사됩니다. 파일 시스템 경로가 존재하지 않으면 오류가 발생합니다.

:::

디렉터리도 마찬가지입니다. 디렉터리 전체(중첩된 파일과 디렉터리 포함)가 Nix 스토어에 복사되고, 평가된 문자열은 해당 디렉터리의 Nix 스토어 경로가 됩니다.

### 페처

빌드 입력으로 사용할 파일이 반드시 파일 시스템에서 와야 하는 것은 아닙니다.

Nix 언어는 평가 중에 네트워크를 통해 파일을 가져오는 비순수 내장 함수를 제공합니다.

- [`builtins.fetchurl`](https://nix.dev/manual/nix/stable/language/builtins.html#builtins-fetchurl)
- [`builtins.fetchTarball`](https://nix.dev/manual/nix/stable/language/builtins.html#builtins-fetchTarball)
- [`builtins.fetchGit`](https://nix.dev/manual/nix/stable/language/builtins.html#builtins-fetchGit)
- [`builtins.fetchClosure`](https://nix.dev/manual/nix/stable/language/builtins.html#builtins-fetchClosure)

이 함수들은 Nix 스토어 안의 파일 시스템 경로로 평가됩니다.

예시:

```{code-block} nix
:class: expression
builtins.fetchurl "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"
```

```{code-block}
:class: value
"/nix/store/7dhgs330clj36384akg86140fqkgh8zf-7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"
```

이 중 일부는 아카이브 자동 압축 해제와 같은 편의 기능을 추가로 제공합니다.

예시:

```{code-block} nix
:class: expression
builtins.fetchTarball "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc63430b33a5169c5e4784a0ff.tar.gz"
```

```{code-block}
:class: value
"/nix/store/d59llm96vgis5fy231x6m7nrijs0ww36-source"
```

:::{note}
[페처][nixpkgs-fetchers]에 관한 Nixpkgs 매뉴얼에는 네트워크를 통해 파일을 가져오는 다양한 추가 라이브러리 함수가 나와 있습니다.
:::

네트워크 요청이 실패하면 오류가 발생합니다.

[nixpkgs-fetchers]: https://nixos.org/manual/nixpkgs/stable/#chap-pkgs-fetchers

(derivations)=
## Derivation


Derivation은 Nix와 Nix 언어 모두의 핵심입니다.
- Nix 언어는 derivation을 기술하는 데 사용됩니다.
- Nix는 derivation을 실행하여 *빌드 결과*를 생성합니다.
- 빌드 결과는 다시 다른 derivation의 입력으로 사용될 수 있습니다.

derivation을 선언하는 Nix 언어의 기본 요소는 비순수 내장 함수 `derivation`입니다.

이 함수는 보통 Nixpkgs 빌드 메커니즘인 `stdenv.mkDerivation`으로 감싸서 사용하며, 이 메커니즘은 간단하지 않은 빌드 절차에 따르는 복잡성의 상당 부분을 감춰 줍니다.

:::{note}
실제로 `derivation`을 직접 접할 일은 아마 없을 것입니다.
:::

`mkDerivation`이 보이면 언제나, 그것은 Nix가 결국 *빌드*할 무언가를 나타냅니다.

예시: [`mkDerivation`을 사용하는 패키지](mkDerivation-example)

`derivation`(및 `mkDerivation`)의 평가 결과는 특정 구조와 특별한 성질을 가진 [속성 집합](attrset)입니다. 이 속성 집합은 [문자열 보간](string-interpolation)에 사용할 수 있으며, 이 경우 빌드 결과의 Nix 스토어 경로로 평가됩니다.

예시:

```{code-block} nix
:class: expression
let
  pkgs = import <nixpkgs> {};
in "${pkgs.nix}"
```

```{code-block}
:class: value
"/nix/store/sv2srrjddrp2isghmrla8s6lazbzmikd-nix-2.11.0"
```

:::{note}
출력 결과는 다를 수 있습니다. 다른 해시가 생성되거나 심지어 다른 패키지 버전이 나올 수도 있습니다.

derivation의 출력 경로는 입력에 의해 완전히 결정되며, 이 경우 입력은 Nixpkgs의 *어떤* 버전에서 옵니다.

이것이 설명만을 위한 예시를 제외하고는 예측 가능한 결과를 보장하기 위해 [조회 경로를 피해야 하는](search-path) 이유입니다.
:::

:::{dropdown} 자세한 설명

이 예시는 조회 경로 `<nixpkgs>`에서 Nix 표현식을 가져오고, 그 결과로 얻은 함수를 빈 속성 집합 `{}`에 적용합니다. 그 출력에는 `pkgs`라는 이름이 할당됩니다.

`pkgs.nix`는 derivation이므로, [문자열 보간](string-interpolation)으로 속성 `pkgs.nix`를 문자열로 변환할 수 있습니다. 즉, `pkgs.nix`는 결국 `derivation` 호출로 귀결됩니다.

결과 문자열은 해당 derivation의 빌드 결과가 최종적으로 위치하게 될 파일 시스템 경로입니다.

derivation의 내부 동작에는 더 깊은 내용이 있지만, 현재로서는 이러한 표현식이 Nix 스토어 경로로 평가된다는 것만 알아도 충분합니다.

:::

derivation에 대한 문자열 보간은 새로운 derivation을 선언할 때 해당 derivation의 빌드 결과를 파일 시스템 경로로 참조하는 데 사용됩니다.

이를 통해 Nix 언어로 임의로 복잡한 derivation 조합을 구성할 수 있습니다.

## 실전 예시

지금까지의 예시는 Nix 언어 구문을 인위적으로 보여 주는 예시였습니다.

이제 간단한 패키지와 설정에 대한 Nix 언어 코드를 읽을 수 있어야 하며, 다음의 실용적인 예시에 대해서도 비슷한 설명을 할 수 있어야 합니다.

::: {note}
다음 연습의 목표는 코드가 무엇을 의미하는지나 어떻게 동작하는지를 이해하는 것이 아니라, 코드가 함수, 속성 집합 및 기타 Nix 언어 데이터 타입의 관점에서 어떻게 구성되어 있는지를 이해하는 것입니다.
:::

### 셸 환경

```{code-block} nix
{ pkgs ? import <nixpkgs> {} }:
let
  message = "hello world";
in
pkgs.mkShellNoCC {
  packages = with pkgs; [ cowsay ];
  shellHook = ''
    cowsay ${message}
  '';
}
```

이 예시는 셸 환경(초기화 시 `shellHook`을 실행함)을 선언합니다.

설명:

- 이 표현식은 속성 집합을 인수로 받는 함수입니다.
- 인수에 속성 `pkgs`가 있으면 그것이 함수 본문에서 사용됩니다. 그렇지 않으면 기본적으로 조회 경로 `<nixpkgs>`에서 찾은 파일의 Nix 표현식(이 경우 함수임)을 가져와, 빈 속성 집합으로 해당 함수를 호출하고, 그 결과 값을 사용합니다.
- 이름 `message`는 문자열 값 `"hello world"`에 바인딩됩니다.
- `pkgs` 집합의 속성 `mkShellNoCC`는 속성 집합을 인수로 전달받는 함수입니다. 그 반환 값은 바깥 함수의 결과이기도 합니다.
- `mkShellNoCC`에 전달되는 속성 집합에는 속성 `packages`(원소가 하나인 리스트로 설정됨: `pkgs`의 `cowsay` 속성)와 `shellHook`(들여쓰기된 문자열로 설정됨)이 있습니다.
- 들여쓰기된 문자열에는 보간된 표현식이 포함되어 있으며, 이 표현식은 `message`의 값을 전개하여 `"hello world"`를 만들어 냅니다.


### NixOS 설정

```{code-block} nix
{ config, pkgs, ... }: {

  imports = [ ./hardware-configuration.nix ];

  environment.systemPackages = with pkgs; [ git ];

  # ...

}
```

이 예제는 NixOS 설정(의 일부)입니다.

설명:

- 이 표현식은 속성 집합을 인수로 받는 함수입니다. 이 함수는 속성 집합을 반환합니다.
- 인수는 최소한 `config`와 `pkgs` 속성을 가져야 하며, 그 외의 속성을 더 가질 수도 있습니다.
- 반환되는 속성 집합에는 `imports`와 `environment` 속성이 들어 있습니다.
- `imports`는 요소가 하나인 리스트이며, 그 요소는 이 Nix 파일과 같은 위치에 있는 `hardware-configuration.nix`라는 파일의 경로입니다.

  :::{note}
  `imports`는 순수하지 않은 내장 함수 `import`가 아니라 일반적인 속성 이름입니다!
  :::
- `environment`는 그 자체로 `systemPackages` 속성 하나를 가진 속성 집합이며, 이 속성은 요소가 하나인 리스트로 평가됩니다. 그 요소는 `pkgs` 집합의 `git` 속성입니다.
- `config` 인수는 (여기에 보이는 부분에서는) 사용되지 않습니다.

(mkDerivation-example)=
### 패키지

```{code-block} nix
{ lib, stdenv, fetchurl }:

stdenv.mkDerivation rec {

  pname = "hello";

  version = "2.12";

  src = fetchurl {
    url = "mirror://gnu/${pname}/${pname}-${version}.tar.gz";
    sha256 = "1ayhp9v4m4rdhjmnl2bq3cibrbqqkgjbl3s7yk2nhlh8vj3ay16g";
  };

  meta = with lib; {
    license = licenses.gpl3Plus;
  };

}
```

이 예제는 Nixpkgs의 (단순화한) 패키지 선언입니다.

설명:

- 이 표현식은 정확히 `lib`, `stdenv`, `fetchurl` 속성만 가져야 하는 속성 집합을 인수로 받는 함수입니다.
- 이 함수는 `stdenv`의 속성인 `mkDerivation` 함수를 재귀 집합에 적용하여 평가한 결과를 반환합니다.
- `mkDerivation`에 전달되는 재귀 집합은 `fetchurl` 함수의 인수에서 자신의 `pname`과 `version` 속성을 사용합니다. `fetchurl` 자체는 바깥쪽 함수의 인수에서 옵니다.
- `meta` 속성은 그 자체로 속성 집합이며, 그 안의 `license` 속성은 중첩된 속성 `lib.licenses.gpl3Plus`에 할당된 값을 가집니다.

## 참고 자료

- [Nix 매뉴얼: Nix 언어][manual-language]
- [Nix 매뉴얼: 문자열 보간][manual-string-interpolation]
- [Nix 매뉴얼: 연산자][operators]
- [Nix 매뉴얼: 내장 함수][nix-builtins]
- [Nix 매뉴얼: `nix repl`][`nix repl`]
- [Nixpkgs 매뉴얼: 함수 참조][nixpkgs-functions]
- [Nixpkgs 매뉴얼: 페처][nixpkgs-fetchers]

[manual-string-interpolation]: https://nix.dev/manual/nix/stable/language/string-interpolation.html

## 다음 단계

### 실제로 활용하기

- [](declarative-reproducible-envs) – Nix 파일로 재현 가능한 셸 환경 만들기
- [](./packaging-existing-software.md) – Nix를 통해 더 많은 소프트웨어를 사용할 수 있게 만들기


Nix 학습을 한동안 쉬려는 경우, 다음 명령으로 Nix 스토어에서 사용하지 않는 빌드 결과를 제거할 수 있습니다:

```console
$ nix-collect-garbage
```

### 더 알아보기

예제를 직접 따라 해 보셨다면, Nix 언어를 읽으면 코드의 구조는 드러나지만 코드가 실제로 무엇을 의미하는지까지 반드시 알 수 있는 것은 아니라는 점을 알아차리셨을 것입니다.

주어진 코드만으로는 다음을 판단할 수 없는 경우가 많습니다
- 이름이 붙은 값이나 함수 인자의 데이터 타입.
- 호출된 함수가 인자로 받는 데이터 타입.
- 주어진 속성 집합에 어떤 속성이 있는지.

예시:

```{code-block} nix
{ x, y, z }: (x y) z.a
```

다음을 어떻게 알 수 있습니까...
- `x`가 인자를 받으면 함수를 반환하는 함수라는 것을?
- `x`가 함수라고 할 때, `y`가 `x`에 적절한 인자라는 것을?
- `(x y)`가 함수라고 할 때, `z.a`가 `(x y)`에 적절한 인자라는 것을?
- `z`가 애초에 속성 집합이라는 것을?
- `z`가 속성 집합이라고 할 때, `a`라는 속성을 가지고 있다는 것을?
- `y`와 `z.a`가 어떤 데이터 타입인지를?
- 최종 결과의 데이터 타입을?

그리고 이 함수를 호출하는 쪽은 이 함수가 `x`, `y`, `z` 속성을 가진 속성 집합을 요구한다는 것을 어떻게 알 수 있습니까?

이러한 질문에 답하려면 주어진 표현식이 어떤 맥락에서 사용되도록 되어 있는지 알아야 합니다.

Nix 생태계와 코드 스타일은 관례에 따라 움직입니다. Nix 언어 코드에서 접하게 될 이름 대부분은 Nixpkgs에서 온 것입니다:

- [Nix Pills][nix-pills] - derivation과 Nixpkgs가 기본 원리부터 어떻게 구성되는지에 대한 자세한 설명

Nixpkgs는 널리 사용되는 범용 빌드 메커니즘을 제공합니다:

- [`stdenv`][stdenv] - 그중 가장 중요한 것은 `mkDerivation`
- [빌드 헬퍼][build-helpers] - 셸 스크립트와 단일 파일을 포함하여 derivation을 만들기 위한 것

Nixpkgs의 패키지는 여러 메커니즘을 통해 수정할 수 있습니다:

- [오버라이드][overrides] – 특히 개별 패키지를 수정하기 위한 `override`와 `overrideAttrs`
- [오버레이][overlays] – 개별적으로 수정된 패키지를 포함하는 Nixpkgs의 사용자 정의 변형을 만들기 위한 것

언어 생태계와 프레임워크마다 Nixpkgs에 수용되기 위한 요구 사항이 서로 다릅니다:

- [언어 및 프레임워크][language-support]에는 Nix로 특정 언어나 프레임워크용 패키지를 빌드하기 위해 Nixpkgs가 제공하는 도구가 나열되어 있습니다.

NixOS Linux 배포판은 고유한 관례를 따르도록 하는 [모듈식 설정 시스템](module-system-tutorial)을 사용합니다.

[nix-pills]: https://nixos.org/guides/nix-pills/
[stdenv]: https://nixos.org/manual/nixpkgs/stable/#chap-stdenv
[build-helpers]: https://nixos.org/manual/nixpkgs/stable/#part-builders
[overlays]: https://nixos.org/manual/nixpkgs/stable/#chap-overlays
[overrides]: https://nixos.org/manual/nixpkgs/stable/#chap-overrides
[language-support]: https://nixos.org/manual/nixpkgs/stable/#chap-language-support
[nixos-modules]: https://nixos.org/manual/nixos/stable/index.html#sec-writing-modules
