Belajar Nix

April 28, 2024

Belajar tentang apa itu nix, bukan *nix

Apa itu Nix?

Membaca kata Nix mungkin muncul berbagai macam istilah, pertama yang terbiasa dengan Linux mungkin lazim dengan istilah Unix-like yang sering disebut sebagai *nix.

Namun, yang akan dipelajari dalam postingan blog ini yaitu package manager / manajer paket perangkat lunak atau aplikasi yang bisa dipasang pada beberapa sistem.

Paket perangkat lunak pada Nix didefinisikan melalui pemrograman fungsional yang lazy. lazy disini bermaksud lazy evaluation, strategi evaluasi ekspresi dimana evaluasi akan di delay sampai value nya benar-benar dibutuhkan.

Nix berawal dari beberapa paper yang berawal dari bahasa dan packages (Nix & nixpkgs) kemudian dilanjut ke sistem operasi NixOS

Persiapan

Pertama yang harus dilakukan adalah menginstal Nix pada sistem yang anda gunakan. Ada halaman official download Nix pada https://nixos.org/download/, yang akan memberi tahu cara instalasi pada sistem yang didukung seperti Linux, MacOS, Windows menggunakan WSL versi 2, dan Docker.

Khususnya untuk pengguna MacOS, saya merekomendasikan mengunakan installer dari Determinate Systems https://github.com/DeterminateSystems/nix-installer, lebih dapat diandalkan dan masih bisa digunakan jika kita melakukan upgrade Sistem Operasi.

curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install

Selain itu Determinate Systems installer juga menginstall Nix Flakes.

$ nix --version
nix (Nix) 2.18.1
$ nix flake show
path '/Users/sakti' does not contain a 'flake.nix', searching up
error: could not find a flake.nix file

Bahasa Nix

Untuk menyederhanakannya, anda bisa menganalogikan bahasa Nix sebagai JSON dengan fungsi.

nix adalah bahasa domain spesifik

Primitif / literal

Berikut tipe data yang ada pada Nix dan cara menulis nya secara literal/harfiah di kode Nix.

{
  string = "hello"; # komentar
  integer = 1;
  float = 3.141;
  #  komentar ditengah
  bool = true;
  null = null;
  list = [ 1 "two" false ];
  attribute-set = {
    a = "hello";
    b = 2;
    c = 2.718;
    d = false;
  }; # komentar
}

Untuk mengevaluasi kode diatas bisa menggunakan nix-instantiate, sebagai contoh kode diatas disimpan sebagai primitif.nix. Jalankan nix-instantiate --eval --strict primitif.nix menghasilkan output:

{ attribute-set = { a = "hello"; b = 2; c = 2.718; d = false; }; bool = true; float = 3.141; integer = 1; list = [ 1 "two" false ]; null = null; string = "hello"; }

Operator

Operator yang dapat dipakai di Nix:

Sintaksis Penjelasan
+, -, *, / operasi numerik
+ menggabungkan string
++ menggabungkan list
== pengecekan. persamaan
>, >=, <, <= operasi pembanding
&& logis AND
|| logis OR
e1 -> e2 implikasi logis
! negasi boolean
set.attr akses atribut attr di atribut set set
set ? attr mengetes apakah attr ada dalam atribut set set
kiri // kanan menggabungkan atribut set kiri dan kanan, dengan kanan didahulukan
{
  c = 1 + 2;
} // {
  d = 4;
} 

menghasilkan

{ c = 3; d = 4; }

untuk mengevaluasi dengan atribut yang ada dalam set dapat menggunakan rekursif rec, contoh:

rec {
  a = 1;
  b = 2;
  c = a + b;
}

menghasilkan

{ a = 1; b = 2; c = 3; }

Fungsi

Nix mempunyai beberapa fungsi bawaan, seperti:

rec {
  value = builtins.toString (10);
  json = builtins.toJSON ({
    name = "glados";
    id = 42;
  });
  system = builtins.currentSystem;
  robots = builtins.fetchurl "https://saktidwicahyono.name/robots.txt";
  robots_content = builtins.readFile (robots);
}

jika dievaluasi menghasilkan

{ json = "{\"id\":42,\"name\":\"glados\"}"; robots = "/nix/store/kh9r31gaw9rkjamqh9fdxa90pcgx330h-robots.txt"; robots_content = "User-agent: *\nDisallow: /admin\n\nHost: saktidwicahyono.name\nSitemap: http://saktidwicahyono.name/sitemap.xml\n\n"; system = "aarch64-darwin"; value = "10"; }

Untuk mendeklarasikan fungsi yang kita definisikan sendiri, dapat menggunakan format berikut:

arg: "badan fungsi ${arg}"

dideklarasikan sebagai variable mengunakan variable bindings:

let
  salam = ", selamat siang";
  halo = nama: "halo ${nama}";
in
  halo "doug" + salam

menghasilkan

"halo doug, selamat siang"

Argumen lebih dari satu (currying)

Fungsi dengan beberapa argument dapat menggunakan currying, yang berarti membuat fungsi dengan satu argumen, yang mengembalikan fungsi dengan argumen lain, yang mengembalikan ... dan seterusnya.

let
  f = x: y: x * y;
in
  f 4 2

fungsi f merupakan fungsi perkalian yang menerima parameter x yang mengembalikan fungsi yang menerima parameter y.

Alternatif lain, menggunakan atribut set sebagai argumen, dengan ini kita dapat menentukan argumen default (sehingga pemanggil dapat menghilangkannya).

let salam =  { nama, umur ? 42 }: "${nama} berumur ${toString umur} tahun";
in salam { nama = "Cave"; }

menghasilkan

"Cave berumur 42 tahun"

Selain itu, kita dapat menggunakan elipsis ..., jika input yang diberikan berisi lebih banyak variabel daripada yang dibutuhkan untuk fungsi tersebut.

let
  salam =  { nama, umur, ... }: "${nama} berumur ${toString umur} tahun";
  orang = {
    nama = "Cave";
    umur = 420;
    email = "[email protected]";
  };
in salam orang

# menghasilkan: "Cave berumur 420 tahun"

Nix juga mendukung penggabungan seluruh atribut menjadi satu parameter menggunakan sintaks @:

let
  fungsi =  { nama, umur, ... }@args: builtins.attrNames args;
  orang = {
    nama = "Cave";
    umur = 420;
    email = "[email protected]";
  };
in fungsi orang

# menghasilkan: [ "email" "nama" "umur" ]

Percabangan

Nix memiliki dukungan kondisional yang sederhana. Perhatikan bahwa if adalah sebuah ekspresi dalam Nix, yang berarti bahwa kedua cabang harus ditentukan. Contoh kita ingin membuat fungsi untuk membedakan bilangan ganjil dan genap, untuk ini kita perlu nixpkgs lib.

{ lib, ...}:
let
  ganjilgenap = x:
    if lib.trivial.mod x 2 == 0 then
      "genap"
    else
      "ganjil";
in
  [(ganjilgenap 3) (ganjilgenap 4)]

Cara menjalankan nya dengan menambahkan parameter --arg:

$ nix-instantiate --eval --strict ganjilgenap.nix --arg lib '(import <nixpkgs> {}).lib'
[ "ganjil" "genap" ]

inherit

Kata kunci inherit digunakan dalam atribut set atau membiarkan binding untuk "mewarisi" variabel dari lingkup induk.

Singkatnya, pernyataan seperti inherit nama; diperluas menjadi nama = nama;.

let
  nama = "Malenia";
  saudara = {
    kembar = "Miquella";
  };
  orangtua = {
    ibu = "Queen Marika";
  };
in {
  inherit nama;
  inherit (saudara) kembar;
  inherit (orangtua) ibu;
}

# menghasilkan: { ibu = "Queen Marika"; kembar = "Miquella"; nama = "Malenia"; }

with

Pernyataan with "mengimpor" semua atribut dari kumpulan atribut ke dalam variabel dengan nama yang sama. Hal ini mempersingkat kode, kita mengakses variabel secara langsung tanpa atribut akses:

let attrs = { a = 15; b = 2; };
in with attrs; a + b  # tanpa attrs.a + attrs.b

# menghasilkan: 17

Bersambung

Dalam post selanjutnya:

Return to blog

footer