Skip to content

언어 구성

재귀 집합

재구 지비합은 단순한 집합이지만 특성들이 서로를 참조할 수 있습니다. 예를 들어,

rec {
  x = y;
  y = 123;
}.x
위는 123으로 평가됩니다. rec없이는 x = y;가 주변 참조 범위에 있는 변수 y를 부른다는 것을 기억하시기 바랍니다. 만약 하나 존재한다면, 유효할 것이고, 그런 변수를 찾을 수 없다면 무효한 것이 됩니다. 요약하자면, 평범한(재귀가 아닌) 집합에서는, 특성은 구문상의 탐색 범위에 추가되지 않습니다; 재귀 집합에서는 포함됩니다.

재귀 집합은 물론, 무한 재귀의 위험을 안고 있습니다. 예를 들어, 아래 식은

rec {
  x = y;
  y = x;
}.x
infninite recursion encountered 에러 메시지와 함께 작동이 중단됩니다.

Let-표현식

let 표현식은 표현식 내부에 로컬 변수를 정의할 수 있게 만들어줍니다. 예를 들어,

let
  x = "foo";
  y = "bar";
in x + y
위 표현식은 "foobar"로 평가됩니다.

상속 특성(inheritance attributes)

집합을 정의하거나 let 표현식을 사용할 때, 주변 구문상 탐색 범위의 변수값을 복사할 수 있다면 편리할 상황을 자주 마주합니다(예, 특성을 이어받아 사용하고 싶을 때). 이런 상황은 inherit으로 줄인 키워드를 활용해 해결할 수 있습니다. 예를 들어,

let x = 123; in
{ inherit x;
  y = 456;
}
위는 아래와 동일합니다.

let x = 123; in
{ x = x;
  y = 456;
}

둘 모두 { x = 123; y = 456; }으로 평가됩니다. (xlet 구문에 의해 구문상 탐색 범주에 들어와있으므로 작동한 다는 것을 확인하시기 바랍니다) 뿐만 아니라, 다른 집합으로부터 특성을 상속 받는 것도 가능합니다 예를 들어, 아래 부분에 all-packages.nix을 보여드리겠습니다.

graphviz = (import ../tools/graphics/graphviz) {
  inherit fetchurl stdenv libpng libjpeg expat x11 yacc;
  inherit (xlibs) libXaw;
};

xlibs = {
  libX11 = ...;
  libXaw = ...;
  ...
}

libpng = ...;
libjpg = ...;
...
../tools/graphics/graphviz에 정의되어 있는 함수를 호출하기 위해 사용되는 집합에는 그 주변 탐색 범위에 있는 몇 개의 변수 (fetchurl ... yacc)등이 상속됩니다, 그러나 그 뿐만이 아니라 libXaw(X 아테나 위젯)으로부터 xlibs(X11 클라이언트 측 라이브러리) 집합도 상속받습니다.

부분을 요약하자면

...
inherit x y z;
inherit (src-set) a b c;
...
위는 아래와 동일합니다.

...
x = x; y = y; z = z;
a = src-set.a; b = src-set.b; c = src-set.c;
...

즉, 로컬 변수를 정의하거나 let 표현식, 집합 정의 과정에 활용합니다.

함수

함수는 다음과 같은 형태를 지닙니다:

pattern: body

패턴은 함수에게 주어지는 인자가 분명히 어떤식이어야 하는지 명시하며, 함수 본문의 변수들을 인자(의 일부)와 연계시킵니다. 3가지 종류의 패턴이 존재합니다:

  • 패턴이 하나의 지칭자(identifier)라면, 함수는 어떤 인자든 부합시킬 수 있습니다. 예를 들어:

    let negate = x: !x;
        concat = x: y: x + y;
    in if negate true then concat "foo" "bar" else ""
    

    concat 함수는 하나의 인자를 받은 후, 함수를 반환하며, 반환되 해당 함수도 또 다른 인자를 받는 다는 것을 확인하시기 바랍니다. 이로써 부분 인자화(partial parameterisation)이 가능해집니다. (예, 함수의 일부 인자만을 채워놓기); 예를 들면,

    map (concat "foo") [ "bar" "bla" "abc" ]
    

    위는 [ "foobar" "foobla" "fooabc" ]로 평가됩니다.

  • { name1, name2, …, nameN }로 이루어지는 집합 패턴은 나열된 특성을 포함하는 집합을 인자로 받아, 해당 특성 값들을 함수 본문의 각 변수에 연계시킵니다. 예를 들어, 아래 함수는

    { x, y, z }: z + y + x
    
    x, y, z 특성들을 정확히 포함하는 집합과 함께여야만 호출이 가능합니다. 다른 어떤 특성도 허용되지 않습니다. 추가적 인자를 허용하고자 한다면 말줄임표(...)를 사용할 수 있습니다:

    { x, y, z, ... }: z + y + x
    
    위는 최소한 3개의 적혀있는 특성을 포함하는 집합이면 작동합니다.

    특성의 기본값을 제공하는 것도 가능합니다. 이 경우, 해당 특성이 생략되는 것이 허용됩니다. 기본값은 name ? e와 같은 식으로 작성됩니다. 여기서 e는 임의의 표현식입니다. 예를 들어,

    { x, y ? "foo", z ? "bar" }: z + y + x
    

    위는 특성 이름 x만을 반드시 요구하는 함수이지만 선택적으로 yz도 수락할 수 있습니다.

  • @ 패턴은 매칭된 전체 값을 가리키는 방법으로 제공됩니다:

    args@{ x, y, z, ... }: z + y + x + args.a
    
    위는 이렇게 쓰일 수도 있습니다:

    { x, y, z, ... } @ args: z + y + x + args.a
    
    여기서 args는 전체 인자에 연계됩니다. 이는 { x, y, z, ... }보다 더 나아가 매치됩니다. @ 패턴은 주로 말줄임(...)과 쓰일때 이해가 됩니다. 이는 함수에 주어진 추가적인 특성 이름 aargs.a와 같이 접근할 수 있도록 해주기 때문입니다.

    주의

    args@ 표현식은 함수에 넘겨진 인자들에만 연계되는데 이것은 명시적으로 특정되지 않은, 기본값은 주어질 특성들이 함수 호출 단계에서 평가 에러를 발생시키지 않지만 args에는 존재하지 않음을 의미합니다. 예를 들어,

    let
      function = args@{ a ? 23, ... }: args;
    in
      function {}
    

    위는 빈 특성 집합으로 평가됩니다.

함수들에 이름이 없다는 점을 주목하기 바랍니다. 이름을 부여하고 싶다면 특성에 연계시키는 방법을 사용할 수 있습니다. 예를 들어, 아래와 같습니다.

let concat = { x, y }: x + y;
in concat { x = "foo"; y = "bar"; }

조건문

조건문은 다음과 같습니다:

if e1 then e2 else e3
e1이 불린값(참 또는 거짓)으로 평가되어야 하는 표현식입니다.

주장문(Assertion)

주장문은 일반적으로 특정한 기능을 위한/사이의 요구조건이나 의존성이 잘 성립하는지 확인하기 위해 사용합니다. 아래와 같습니다:

assert e1; e2

e1은 불린값으로 평가되어야하는 표현식입니다. 만약 값이 참으로 평가되면, e2가 반환됩니다; 그렇지 않으면 표현식 평가가 멈추고 에러경과보고(backtrace)가 출력됩니다.

아래에서 Subversion 패키지를 통해 주장문이 어떻게 사용되는지 보여드립니다:

{ localServer ? false
, httpServer ? false
, sslSupport ? false
, pythonBindings ? false
, javaSwigBindings ? false
, javahlBindings ? false
, stdenv, fetchurl
, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null
}:

assert localServer -> db4 != null; 
assert httpServer -> httpd != null && httpd.expat == expat; 
assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl); 
assert pythonBindings -> swig != null && swig.pythonSupport;
assert javaSwigBindings -> swig != null && swig.javaSupport;
assert javahlBindings -> j2sdk != null;

stdenv.mkDerivation {
  name = "subversion-1.1.1";
  ...
  openssl = if sslSupport then openssl else null; 
  ...
}

관심을 갖고 봐야할 포인트는 다음과 같습니다:

  1. 이 주장문은 Subversion이 로컬 레파지토리를 지원해야 하는지 확인하고, 그렇다고 한다면 버클리 DB를 필요로 합니다. 그러므로 만약 Subversion이 localServer 인자가 참으로 설정되어 호출되었으나 db4 인자가 null로 세팅되어 있다면, 평가는 실패할 것입니다.

  2. 이는 더 섬세한 조건입니다: 만약 Subversion이 아파치(httpServer) 지원과 함께 빌드되었다면, Subversion이 사용할 Expat 라이브러리(XML 라이브러리)가 아파치가 사용할 것과 같은 것이어야만 합니다. 이는 Subversion 코드 설정이 아파치 코드와 링크될 것이기 때문이고, 만약 Expat 라이브러리들이 서로 맞지 않으면, 빌드 시점 혹은 실행 시점 링크 에러가 나거나 호환 불가 에러가 발생할 수 있습니다.

  3. 이 주장문은 Subversion이 SSL 지원(https URL에 접근할 수 있기 위해)을 가지려면 OpenSSL 라이브러리가 반드시 주어져야 함을 의미합니다. 추가적으로, 만일 아파치 지원도 켜져 있다면, 아파치의 OpenSSL은 Subversion의 것과 일치해야 합니다. (아파치 지원이 켜져있지 않다면, 아파치의 OpenSSL은 신경쓰지 않는다는 점을 확인하시기 바랍니다.)

  4. 이 조건은 사실 실제로 주장문과 관련이 있지는 않지만, 살펴볼 가치가 있습니다: 만약 SSL 지원이 꺼져 있다면, null이 아닌 값이 주어지더라도, Subversion 제작물은 OpenSSL에 의존하지 않습니다. 이는 OpenSSL이 변화하더라도 불필요한 Subversion 재빌드가 발생하지 않게 해줍니다.

With-표현식

with-표현식이란,

with e1; e2

e1 집합을 e2의 구문 탐색 범주에 소개합니다. 예를 들어,

let as = { x = "foo"; y = "bar"; };
in with as; x + y

위는 withasxy특성을 x+y 탐색 범주에 추가하기 때문에, "foobar"로 평가됩니다. 가장 흔한 with의 사용 방식은 import 함수와 함께 쓰는 것입니다. 예를 들어,

with (import ./definitions.nix); ...

위 구문은 definitions.nix 파일에 정의된 모든 특성을 마치 let 포현식을 로컬에서 정의한것처럼 사용 가능하도록 만들어줍니다.

with로 소개되는 변수 연계는 다른 방식으로 소개되는 것들을 가리지 못합니다. 예를 들어,

let a = 3; in with { a = 1; }; let a = 4; in with { a = 2; }; ...
위는 아래와 같은 탐색 범주 순위를 가집니다.

let a = 1; in let a = 2; in let a = 3; in let a = 4; in ...

주석

주석은 한 줄인 경우, #글자로 시작하거나, 문서 내부/여러 줄인 경우 /* ... */로 표현됩니다.


Last update: November 4, 2021
Back to top