Za trzy dni będzie premiera Javy 9. Dlatego właśnie czas napisać coś o Elixirze. Dziś na tapecie ląduje jeden z ciekawszych elementów języka, czyli wykonywalna dokumentacja.
Chwila teorii
Gdy dokumentujemy jakiś fragment kodu, to często aż prosi się dołączyć jakiś przykład. Coś w rodzaju dla takich danych otrzymasz taki rezultat. Wszystko fajnie, bo taki przykładowy happy path pozwala na zrozumienie kodu. Z drugiej strony przykład zazwyczaj kopiujemy z testów, bo tam jest gotowe, a później, jak się testy zmienią, to już niekoniecznie aktualizujemy dokumentację.
A gdyby tak w dokumentacji zaszyć testy i niech narzędzia martwią się jak to uruchomić?
Chwila praktyki
Na tapetę wziąłem algorytm SlowsortW. Z kilku powodów. Po pierwsze jestem w trakcie pisania kursu ze struktur danych na potrzeby naszego centrum szkoleniowego i implementacja slowsorta jest całkiem fajnym ćwiczeniem. Proste, a jednocześnie nietypowe. Po drugie chcę wykorzystać ten kod w czasie mojej prezentacji na JDD. Po trzecie, jak już implementować coś głupiego, to niech to będzie naprawdę głupie 😀
Kod wygląda tak:
Listing 1. implementacja Slowsort w Elixirze
defmodule Slowsort do @moduledoc """ Documentation for Slowsort. """ @doc """ Example implementation of Slowsort algorithm. ## Examples iex> Slowsort.sort([0]) [0] iex> Slowsort.sort([1,0]) [0, 1] iex> Slowsort.sort([3, 11, 1, 0, 22, 2]) [0, 1, 2, 3, 11, 22] """ def sort(numbers) do l = length numbers slowsort(numbers, 0, l - 1) # we need maximum index that is length - 1 end defp slowsort(numbers, i, j) when i>=j, do: numbers defp slowsort(numbers, i, j) do m = Integer.floor_div((i + j), 2) slowsort(numbers, i, m) |> slowsort(m + 1, j) |> swap(j, m) |> slowsort(i, j - 1) end defp swap(numbers, j, m) do max_left = Swap.nth(numbers, m); max_rigth = Swap.nth(numbers, j); swap(numbers, j, m, max_rigth, max_left) end defp swap(numbers, j, m, mr, ml) when mr < ml, do: Swap.of(numbers, j, m) defp swap(numbers, _, _, mr, ml) when mr >= ml, do: numbers end
Do tego oczywiście są jeszcze testy:
Listing 2. Zestaw testów
defmodule SlowsortTest do use ExUnit.Case @moduletag timeout: 1000 doctest Slowsort test "the empty array" do sorted = Slowsort.sort [] assert sorted === [] end test "the simple array of two elements" do sorted = Slowsort.sort [1, 0] assert sorted === [0, 1] end test "the array of many elements" do sorted = Slowsort.sort [3, 11, 1, 0, 22, 2] assert sorted === [0, 1, 2, 3, 11, 22] end end
Technicznie są tu dwa zestawy testów. Zwróćcie uwagę na dokumentację funkcji sort. Zdefiniowana jest tam sekcja Examples, która wygląda jak fragment kodu w markdownie. I rzeczywiście, jeżeli wygenerujemy dokumentację, to otrzymamy jakiś nagłówek. Znacznie ciekawiej jest, jeżeli uruchomimy testy za pomocą mix test.
Listing 4. Zrzut z testów
Compiling 1 file (.ex) ...... Finished in 0.1 seconds 6 tests, 0 failures
Uruchomiono sześć testów. Trzy zdefiniowanie w slowsort_test i trzy zdefiniowane w dokumentacji.
Podsumowanie
Możliwość zdefiniowania prostych testów bezpośrednio w dokumentacji jest bardzo fajnym rozwiązaniem. Pozwala to na jednoczesną aktualizację testów, jak i dokumentacji. Szkoda, że w Javie nie ma takiego narzędzia. Oj szkoda…
Kod dostępny jest tutaj.
na tapet a nie na tapetę 😉