Jak nauczyć się (kolejnego) języka?

Listopad 21st, 2016

Odpowiedź na to pytanie jest prosta – napisać coś w nim.

Tyle tylko, że co mam napisać? Oczywiście najprościej jest naskrobać kolejnego klona Pet Clinic, własny system blogaskowy, albo bibliotekę (taką z książkami). Nie przeczę, są to zacne metody, ale mają pewną wadę. Wymagają od nas znajomości języka i narzędzi na pewnym minimalnym poziomie. Bez tego dość szybko popadniemy w kłopoty i zniechęcimy się po „10 godzinach walki z jakimś mavenem, eclipsem i innym springiem”, gdy chcemy się nauczyć podstaw Javy. Projekty są dobre, ale jako „praca końcowa”. Na początek znacznie lepsze są mniejsze zadania, które pozwalają poznać składnię języka i pobawić się z kodem. Przykładem może być samodzielna implementacja zadań z książki Mazes For Programers. Przykładowy kod jest napisany w Ruby, co samo w sobie może być motywacją do poznania tego języka, ale jest też na tyle prosty i dobrze opisany, że można zaimplementować rozwiązania w innym języku.

Inną drogą jest wzięcie udziału w jakiejś formie rywalizacji. Począwszy od starego dobrego SPOJa, poprzez Project Euler, czy też Top Coder. W tym przypadku mamy dowolnie dużo czasu i nie mamy narzuconej kolejności zadań. Ma to swoje plusy, ale też minusy. Jednym z nich jest brak bata i zachęty do codziennej pracy. Dlatego też warto zainteresować się Advent of Code, która niejako wymusza na nas systematyczną, codzienną pracę.

Te wszystkie metody są doskonałe też wtedy, gdy chcemy nauczyć się kolejnego języka. Najpierw zróbmy rozwiązanie w nowym języku, a następnie w naszym głównym. Zobaczycie różnice.

Na koniec małe przypomnienie. Już 26 listopada będę mówił o Elixirze na InfoMeet, start o 9:30 w sali C. Będzie zabawnie 🙂

„Obiektywne” struktury danych w Elixirze

Listopad 8th, 2016

„Obiektywne”, bo nie „obiektowe”. Przymierzam się do opisania interoperacyjności java-elixir trochę ponad „użyj JInterface”, ale zanim do tego się zabiorę, potrzebujemy mieć kilka dodatkowych narzędzi.

Na początek jednak małe przypomnienie. Już za dwa tygodnie z małym haczykiem tzn. 26 listopada będę mówił o Elixirze na InfoMeet, start o 9:30 w sali C. Będzie zabawnie 🙂

Klasy, obiekty i cała reszta

W językach obiektowych jest prosto. Jeżeli potrzebujemy usystematyzować dane, to definiujemy klasę. Ma ona pola, pola mają wartości. Za czynności odpowiadają metody, które powinny mieć swoje odzwierciedlenie w interfejsach (DDD dla oszczędnych), to tego jest jeszcze element JavaBeans i gotowe. Jak mamy już nasze dane i klasę, to możemy tworzyć obiekty, które reprezentują pewien stan danych. Do tego dochodzą jeszcze kontenery jak na przykład listy, mapy czy sekwencje.

W językach funkcyjnych jest pewien drobny problem…

Podejście funkcyjne

W językach funkcyjnych mamy do dyspozycji m.in. czysto funkcyjne strukturyW, które w uproszczeniu można przyrównać do niezmiennych kolekcji z języków obiektowych (w rzeczywistości najbliżej do tych struktur mają kolekcje z pCollections). Jak jednak odwzorować „klasyczne” obiekty? Zazwyczaj robi się, to poprzez zagnieżdżanie i definiowanie własnych typów.

Na przykładzie Elixira

Osadzanie struktur

Trochę jak w JavaScriptcie 🙂 Czyli nasza złożona struktura to mapa map (o listach, sekwencjach itd. innym razem, ale to nuda jest i tak). Jak wygląda mapa w Elixirze?

Mapy w Elixirze definiuje się w prosty sposób:

Listing 1. Mapa z trzema elementami

iex > mapa = %{:A=> 1, 2=> :b, "C"=> "3"}
%{2 => :b, :A => 1, "C" => "3"}

Jako że Elixir jest słabo typowany, to zarówno klucze, jak i wartości mogą być dowolnego typu. Oczywiście mapy można zagnieżdżać:

Listing 2. Mapa map

iex > mapaMap = %{ "pole" => 1, :pole => mapa}
%{:pole => %{2 => :b, :A => 1, "C" => "3"}, "pole" => 1}

Jest tu jednak pewien „myk”, który będzie bardzo przydatny:

Listing 3. Mapa z kluczami – atomami

iex > mapa = %{:age => 32, :name => "Koziołek"}
%{age: 32, name: "Koziołek"}

Zwróćcie uwagę na, to co stało się z dwukropkami przy nazwach atomów. Zmieniły położenie i z przedrostka stały się przyrostkiem. Technicznie nadal jest, to mapa:

Listing 4. Typ naszej mapy to

iex > i mapa
Term
  %{age: 32, name: "Koziołek"}
Data type
  Map
Reference modules
  Map

I jeszcze jedna zagadka:

Listing 5. Czym jest lista dwuelementowych krotek?

iex > lista = [{:age, 32}, {:name, "Koziołek"}]
[age: 32, name: "Koziołek"]

Lista dwuelementowych krotek, w których pierwszym elementem jest atom, jest tzw. keyword list. Najlepszym odpowiednikiem tego terminu jest lista asocjacyjna. Tak też, to przetłumaczyliśmy w ramach ElixirSchool. W przypadku map jeżeli kluczami są atomy, to Elixir zapewnia spójne z listami asocjacyjnymi API. I jeszcze krótko o typie naszej listy:

Listing 6. Ano jest listą

iex > i lista
Term
  [age: 32, name: "Koziołek}]\n\n"]
Data type
  List
Description
  This is what is referred to as a "keyword list". A keyword list is a list
  of two-element tuples where the first element of each tuple is an atom.
Reference modules
  Keyword, List

Skoro wiemy już, że można zagnieżdżać mapy i możemy budować w ten sposób struktury podobne do JSON-ów, to rzućmy jeszcze okiem na definiowanie własnych typów.

Własne typy

Zanim zajmiemy się typami mała uwaga, własne typy w Elixirze warto definiować w ramach wydzielonego modułu, ponieważ przy bardziej złożonych strukturach będziemy też korzystać z definicji struktur.

Na początek zdefiniujmy sobie moduł User.Struct (można w nazwie, można zagnieździć, dostęp będzie taki sam), który będzie przechowywać informacje o strukturze użytkownika:

Listing 7. Moduł User.Struct

defmodule User do

  defmodule Struct do
      defstruct [age: nil, name: nil]
  end
    
end

Teraz dodajmy do naszej struktury typ, konwencja jest taka, że typ nazywamy t i będziemy się do niego odwoływać przez User.Struct.t (plus jedna metoda do testów):

Listing 8. Definicja typu

defmodule User do

  defmodule Struct do
      defstruct [age: nil, name: nil]

      @type t :: %User.Struct{age: integer, name: String.t}

      @type t(age, name) :: t :: %User.Struct{age: age, name: name}

  end

  @spec printInfo(User.Struct.t) :: nil
  def printInfo(user) do
    IO.puts("User name  is #{user.name} and age is #{user.age}")
  end

end

Mamy tu tak naprawdę dwa typy. t, który jest „właściwym” zdefiniowanym jako mapa pól dla struktury i t(age, name), który rozszerza t oraz pełni funkcję czysto pomocniczą jeżeli chcemy przedefiniować, któryś z elementów struktury. A jak tego używać?

Listing 9. przykładowe użycie

iex > user = %{:age=> 32, :name=>"Koziołek"}
%{age: 32, name: "Koziołek"}
iex > User.printInfo(user)
User name  is Koziołek and age is 32
:ok

Tworzymy sobie naszego użytkownika jako mapę i aplikujemy funkcję. Jeżeli coś popieprzymy, to dostaniemy błąd.

Podsumowanie

Własne typy w Elixirze nie są tak mocne, jak w Javie, ale też nie mamy do czynienia z językiem statycznie typowanym. Warto je jednak wprowadzać i używać, by zachować pewien poziom bezpieczeństwa w naszym kodzie. Miłe…

Jedno z dwojga – Either w akcji

Październik 31st, 2016

Na początek kilka spraw organizacyjnych. Jak widać pisanie po wiosennym ruchu idzie mi średnio 🙂 Ale się staram. Powodów jest kilka, jeden ma 5 lat, drugi 2 miesiące 🙂 Do tego dość dużo pracy i zdecydowanie za krótka doba.

26 listopada będę mówił o Elixirze na Infomeet. Jak chcecie posłuchać dlaczego moim zdaniem warto, to zapraszam. Slajdy z prezentacji, która odbyła się w ramach targów KarieraIT tutaj. I lecimy z tematem.

Either kto to?

Jednym z problemów związanych ze zwracaniem wyników z funkcji (w rozumieniu zarówno metody obiektu, jak i „właściwej” funkcji) jest „upchnięcie” informacji o wyniku i jego poprawności. Klasycznie można taki kod napisać w następujący sposób:

Listing 1. Klasyczne podejście do problemu

public int sumUpTo(int limit) throws SumException{
	//....
}

Mamy sobie metodę, która rzuca wyjątek, jeżeli coś się jej nie spodoba. Takie rozwiązanie jest OK, lecz ma kilka wad. Po pierwsze wyjątek należy przechwycić. O ile wyjątków niekontrolowanych nie musimy obsługiwać w jawny sposób, to jednak w porządnie napisanym sofcie należy pomyśleć o jakimś mechanizmie do ich przechwytywania. Niestety w klasycznym podejściu walimy blok catch(Throwable) gdzieś wysoko i nam to rybka. Po drugie wyjątki są stosunkowo ciężkim mechanizmem. Wyrzucenie wyjątku kontrolowanego wymaga dodatkowej pracy od JVMki, by odpowiednio posprzątać stan aplikacji bardziej szczegółowy opis tutaj. Po drugie czasami narzucone API ogranicza nasze możliwości, jeśli chodzi o wyjątki i musimy korzystać z konstrukcji przechwyć-opakuj-rzuć. Zazwyczaj zamieniamy wyjątek kontrolowany na ogólny RuntimeException. Po trzecie taka metoda, nawet jak rzuca wyjątek niekontrolowany, to bywa trudnym przypadkiem, gdy chcemy korzystać z kompozycji, czy strumieni. Po prostu napiszemy się tych map i filter, a potem i tak trzeba try-catch, bo coś.

Rozwiązaniem jest użycie Either z biblioteki Javaslang. Jest to interfejs, który posiada dwie implementacje Left i Right. Dodatkowo istnieje konwencja, że poprawny wynik trafia do Right, a błędny do Left. Nie jest ona w żaden sposób weryfikowana, ale API niejako „promuje” użycie Rigth, gdyż funkcje domyślnie korzystają z tej wartości. Można zatem przepisać naszą metodę do:

Listing 2. Zmiana API na korzystające z Either

public Either sumUpTo(int limit){
	//....
}

Either po co to?

Możemy potraktować Either jako rodzaj kontenera na wynik operacji. Jeżeli zakończy się błędem, to zostanie wypełniona jego lewa strona, w przeciwnym wypadku wypełniona będzie prawa. Bazując na tej informacji, możemy wprowadzić funkcje takie jak map czy filter. Pozwolą one na budowę odpowiedniego przepływu w aplikacji. Przykładowo jeżeli, chcemy wynik naszej funkcji zamienić na komunikat:

Listing 3. Wykorzystanie Either.map

public static void main(String[] args) {
	sumUpTo(10).map(s -> s + "").forEach(System.out::println);
}

Jeżeli z jakiegoś powodu nasza funkcja zwróci błąd, to wynik będzie… pusty. Po prostu mapowanie się nie wykona. Tu uwidacznia się pewna cecha Either. Jest on „stronniczy”, to znaczy gdy z nim pracujemy musimy określić, która strona nas interesuje. Funkcja map pracuje z wartością Right, ale jest też mapLeft. W dodatku mamy jeszcze projekcje…

Projekcje

Projekcje, to nic innego jak opakowane wartości. Dostarczają nam one odpowiednich funkcji w rodzaju map, filter itd., ale już w kontekście odpowiedniej wartości. By się do nich dobrać, należy wywołać right albo left na obiekcie Either. Kod z listingu 3 można zapisać jako:

Listing 4. Wykorzystanie projekcji

public static void main(String[] args) {
	sumUpTo(10).right().map(s -> s + "").forEach(System.out::println);
}

Jawnie opowiadając się po jednej ze stron, możemy skupić się tylko na interesującej nas części wartości albo błędzie.

Podsumowanie

Oczywiście nie przedstawiłem tu wszystkich możliwości Either. Zachęcam do zapoznania się z dokumentacją, kodem oraz samodzielnego eksperymentowania.

Elixir Mix – aktualizacja Hexa

Październik 24th, 2016

W dużym uproszczeniu Hex jest managerem pakietów w Elixirze. Dokładniej pełni on rolę narzędzia do zarządzania pakietami, zależnościami oraz jest też repozytorium zależności. Coś jak Ivy dla Anta z konfiguracją do repozytorium Mavena. Rzecz w tym, że dziś rzeźbiąc sobie kod otrzymałem błąd:

Listing 1. Błędny błąd

[koziolek@koziolek-laptop learn_elixir (master)]$ mix test
** (UndefinedFunctionError) function Hex.SCM.managers/1 is undefined or private
    Hex.SCM.managers([lock: nil, env: :prod, hex: :exml, build: "/home/koziolek/workspace/learn_elixir/_build/test/lib/exml", dest: "/home/koziolek/workspace/learn_elixir/deps/exml"])
    (mix) lib/mix/dep/loader.ex:173: Mix.Dep.Loader.scm_manager/2
    (mix) lib/mix/dep/loader.ex:44: Mix.Dep.Loader.load/2
    (mix) lib/mix/dep/converger.ex:179: Mix.Dep.Converger.all/9
    (mix) lib/mix/dep/converger.ex:114: Mix.Dep.Converger.all/7
    (mix) lib/mix/dep/converger.ex:99: Mix.Dep.Converger.all/4
    (mix) lib/mix/dep/converger.ex:50: Mix.Dep.Converger.converge/4
    (mix) lib/mix/dep.ex:95: Mix.Dep.loaded/1

Szybkie sprawdzenie mix.exs niby wszystko ok, ale dla pewności dociągnę zależności:

Listing 2. Błędny błąd – bardziej gadatliwy

[koziolek@koziolek-laptop learn_elixir (master)]$ mix deps.get
A new Hex version is available (v0.13.2), please update with `mix local.hex`
Running dependency resolution
** (UndefinedFunctionError) function Access.Map.get_and_update!/3 is undefined (module Access.Map is not available)
    Access.Map.get_and_update!(%Mix.Dep{app: :exml, deps: [], extra: [], from: "/home/koziolek/workspace/learn_elixir/mix.exs", manager: nil, opts: [env: :prod, hex: :exml, build: "/home/koziolek/workspace/learn_elixir/_build/dev/lib/exml", dest: "/home/koziolek/workspace/learn_elixir/deps/exml"], requirement: "~> 0.1.0", scm: Hex.SCM, status: {:unavailable, "/home/koziolek/workspace/learn_elixir/deps/exml"}, top_level: true}, :deps, #Function<11.61161633/1 in Hex.Resolver.attach_dep_and_children/3>)
    lib/hex/resolver.ex:190: Hex.Resolver.attach_dep_and_children/3
    lib/hex/resolver.ex:150: Hex.Resolver.get_deps/4
    lib/hex/resolver.ex:109: Hex.Resolver.activate/5
    lib/hex/resolver.ex:31: Hex.Resolver.resolve/3
    lib/hex/remote_converger.ex:33: Hex.RemoteConverger.converge/2
    (mix) lib/mix/dep/converger.ex:89: Mix.Dep.Converger.all/4
    (mix) lib/mix/dep/converger.ex:50: Mix.Dep.Converger.converge/4

Czyli ok… rzeczywiście robiłem ostatnio update Elixira i trzeba by podbić wersję Hexa! Zatem lecimy z tematem:

Listing 3. Błędny błąd – brak zmian

[koziolek@koziolek-laptop learn_elixir (master)]$ mix local.hex
Are you sure you want to install archive "https://repo.hex.pm/installs/1.3.0/hex-0.13.2.ez"? [Yn] Y
* creating /home/koziolek/.mix/archives/hex-0.13.2
[koziolek@koziolek-laptop learn_elixir (master)]$ mix deps.get
A new Hex version is available (v0.13.2), please update with `mix local.hex`
Running dependency resolution
** (UndefinedFunctionError) function Access.Map.get_and_update!/3 is undefined (module Access.Map is not available)
    Access.Map.get_and_update!(%Mix.Dep{app: :exml, deps: [], extra: [], from: "/home/koziolek/workspace/learn_elixir/mix.exs", manager: nil, opts: [env: :prod, hex: :exml, build: "/home/koziolek/workspace/learn_elixir/_build/dev/lib/exml", dest: "/home/koziolek/workspace/learn_elixir/deps/exml"], requirement: "~> 0.1.0", scm: Hex.SCM, status: {:unavailable, "/home/koziolek/workspace/learn_elixir/deps/exml"}, top_level: true}, :deps, #Function<11.61161633/1 in Hex.Resolver.attach_dep_and_children/3>)
    lib/hex/resolver.ex:190: Hex.Resolver.attach_dep_and_children/3
    lib/hex/resolver.ex:150: Hex.Resolver.get_deps/4
    lib/hex/resolver.ex:109: Hex.Resolver.activate/5
    lib/hex/resolver.ex:31: Hex.Resolver.resolve/3
    lib/hex/remote_converger.ex:33: Hex.RemoteConverger.converge/2
    (mix) lib/mix/dep/converger.ex:89: Mix.Dep.Converger.all/4
    (mix) lib/mix/dep/converger.ex:50: Mix.Dep.Converger.converge/4

No i chuj… Mniej więcej tak to wyglądało:

Życie...

Wychodzi na to, że Hex się jednak nie zaktualizował i nadal wisi w jakiejś przedpotopowej wersji. Nauczony doświadczeniem z Eclipse+Maven wybrałem się do katalogu z repozytorium mixa, czyli ~/.mix/archives/ i wywaliłem wszystko co związane z Hexem, czyli plik hex.ez i katalog hex-0.13.2. Po czym wróciłem do katalogu z projektem i powtórzyłem instalację hexa i ściągnięcie zależności.

Co się okazało?

Ano okazało się, że aktualizacja Hexa nie podmienia pliku hex.ez

Listing 4. Błędny błąd – przyczyna, zwróć uwagę na daty

[koziolek@koziolek-laptop archives]$ ll
razem 392
drwxrwxr-x 3 koziolek koziolek   4096 paź 24 21:07 ./
drwxrwxr-x 3 koziolek koziolek   4096 maj 23  2015 ../
drwxrwxr-x 3 koziolek koziolek   4096 paź 24 21:07 hex-0.13.2/
-rw-rw-r-- 1 koziolek koziolek 262010 maj 19  2015 hex.ez
-rw-rw-r-- 1 koziolek koziolek 123280 maj 23  2015 phoenix_new-0.13.1.ez

Ręczne usunięcie tego pliku wymusza jego ponowne pobranie, już w aktualnej wersji.

Zależności w Elixirze, czyli wstęp do Mixa

Wrzesień 27th, 2016

Jakoś tak w ostatnich latach się porobiło, że każdy szanujący się język programowania powinien się dorobić narzędzia do zarządzania procesem kompilacji, zależnościami, czy wdrożeniem. Dla Javy jest to Ant, Maven i Gradle. Pierwsze dwa reprezentują podejście opisowe, a trzeci to po prostu DSL w groovym. Dla Scali będzie to SBT, dla Rubiego Rake itd, itp. Można jednak zaobserwować, że wszystkie te narzędzia dążą do modelu, w którym zamiast opisu i konfiguracji (w xml-u) mamy wykonywalny skrypt. Jest to rozsądne, ponieważ daje programiście znacznie większe możliwości w zakresie kontroli procesu kompilacja-testy-wdrożenie niż w przypadku narzędzi w rodzaju mavena. Tu po prostu piszesz kawałek kodu i po problemie.

W przypadku Elixira narzędziem tym jest Mix. Dostarczany jest wraz z paczką języka i pracuje w oparciu o plik mix.exs, w którym zdefiniowany jest najzwyczajniejszy w świecie moduł. Musi on zawierać funkcję project zwracającą listę asocjacyjną (oryginalna nazwa tej struktury to keywords) z konfiguracją oraz używać (makro use) Mix.Project. Na przykładzie:

Listing 1. Przykładowy plik mix.exs

defmodule ElixirExample.Mixfile do
  use Mix.Project

  def project do
    [app: :elixir_example,
     version: "0.1.0",
     elixir: "~> 1.3",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps()]
  end

  # Configuration for the OTP application
  #
  # Type "mix help compile.app" for more information
  def application do
    [applications: [:logger]]
  end

  # Dependencies can be Hex packages:
  #
  #   {:mydep, "~> 0.3.0"}
  #
  # Or git/path repositories:
  #
  #   {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
  #
  # Type "mix help deps" for more examples and options
  defp deps do
    []
  end
end

Listing 1. zawiera plik, który powstaje po wywołaniu polecenia mix new. Jak widać jest w nim kilka dodatkowych rzeczy. Po pierwsze lista zależności została przesunięta do prywatnej funkcji deps. Jest to o tyle wygodne, że w przypadku większych aplikacji, gdzie lista zależności jest długa, kod będzie czytelniejszy. Ciekawostką jest możliwość wykorzystania zarówno repozytorium binarek Hex jak i wskazanie taga w repozytorium z kodem źródłowym (git). Po drugie mamy tu funkcję application, która odpowiada za wygenerowanie pliku .app, który opisuje z jakich zasobów korzysta aplikacja, jaka jest jej struktura i jak powinna zostać uruchomiona. Jest to element pochodzący bezpośrednio z erlanga.

I to są podstawy, które pozwalają nam na rozpoczęcie zabawy z programowaniem w Elixirze trochę bardziej skomplikowanych rzeczy niż proste zabawki z użyciem REPLa iex.

Mocno spóźniony protest

Wrzesień 22nd, 2016

Oczywiście chodzi o #czarnyprotest. O ile sama idea jest słuszna, ponieważ przerwanie ciąży jest normalną procedurą medyczną i jej wykonanie nie powinny podlegać karze, to wykonanie jest mocno spóźnione.

Język

W projekcie nie ma nic nadzwyczajnego, ale wynika to z użytego języka.

Nie popełnia przestępstwa […] uchylenia bezpośredniego niebezpieczeństwa dla życia matki dziecka poczętego.

Mówiąc prościej, dopóki kobieta nie jest umierająca, żaden lekarz nie zaryzykuje odsiadki, by ją ratować. Nawet jak będzie umierająca, to jeżeli coś pójdzie nie tak i płód obumrze, to od prokuratora zależy los lekarza.

Sam język prawa nie jest jednak problemem. W dyskusji o aborcji obowiązuje język tzw. ruchu pro-life. Mówi się o ochronie życia poczętego, o dziecku nienarodzonym itp. nowomowie. Chcąc prowadzić dyskusję w tej sprawie, jesteśmy niejako zmuszeni do korzystania z narzuconego przez jedną stronę języka.

Argumenty

Jak już przy dyskutowaniu jesteśmy. Dyskusja z grupami typu Ordo Iuris nie ma sensu. Dla nich jest, to dyskusja ideowa w dodatku o jednym z aksjomatów przyjętej filozofii. Żaden rzeczowy argument, poparty danymi, badaniami, rzeczywistymi zdarzeniami nie ma tu sensu. Równie dobrze można dyskutować z papieżem o istnieniu boga.

Dlaczego protest jest spóźniony?

Ponieważ protestować trzeba było te 20 lat temu, gdy przepychano przepisy. Ok, ale większość z nas w tym czasie zdzierała kolana w piaskownicach i na trzepakach. Tak. Dlatego słusznym czasem na protest przeciwko zaostrzeniu prawa oraz ogólniej durnemu kompromisowi aborcyjnemu powinny być ostatnie wybory. Jak pytam znajomych wstawiających hash tagi czy w ostatnich wyborach głosowali na partię, która nie chce zaostrzenia prawa/zachowania kompromisu to okazuje się, że nie… Powiedzmy sobie szczerze, w Polsce wszystkie partie znajdujące się w parlamencie mają konserwatywny światopogląd. Sorry Winnetou, trzeba było myśleć nad urną.

I dlatego też protest ten jest bezsensowny. Spotkałem się z argumentem, że jak ustawa przejdzie, to kolejne wybory PiS przegra. Nie przegra, bo kolejne wybory są za 3 lata z hakiem, a do tego czasu będziemy mieli znacznie więcej znacznie ciekawszych tematów. Zresztą tak samo, jak przyjęcie tej ustawy nie zaszkodzi też obsadzanie stołków misiewiczami. PiS ma jeszcze dużo czasu, a ludzie zapominają o tego typu duperelach.

ps. Michał, czyli młodszy nurgling już w domu 🙂

Chemiczny konkurs od DZone – rozwiązanie w Elixirze

Sierpień 12th, 2016

O ile rozwiązanie w Javie możecie sobie podejrzeć w repozytorium, a jego omówienie będzie w przyszłym tygodniu, to dziś pokażę, jak można rozwiązać zadanie w Elixirze.

W praktyce całość sprowadza się do kilku (dokładnie 22) linii kodu:

Listing 1. Rozwiązanie w Elixirze

defmodule Chemicals do
  import Enum, only: [map: 2, uniq: 1, min: 1, member?: 2]
  import String, only: [downcase: 1, split: 3 ]
  
  def main(args) do
      {_, [name|t], _} = OptionParser.parse(args)
      symbols = name |> downcase |> split("", trim: true )|> generateValidSymbols
      IO.puts member?(symbols, t|> List.first |> downcase )
      IO.puts length(symbols)
      IO.puts min(symbols)
  end

  def generateValidSymbols([head|tail]) when length(tail) == 1 do
      tail|> map( &(head  <> &1))
  end

  def generateValidSymbols([head |tail]) do
      symb = tail |> map( &(head  <> &1))
      uniq(symb ++ generateValidSymbols(tail))
  end

end

Kluczem są funkcje generateValidSymbols, które zwracają listę wszystkich poprawnych symboli dla podanej nazwy. To samo napisane w Javie (z zachowaniem tej samej logiki) to zdecydowanie więcej kodu. Co tu się dzieje?

Funkcja przyjmuje jako parametr listę liter z nazwy pierwiastka i od razu dzieli ją na pierwszy element (potocznie głowę – head) i resztę (ogon – tail). Jeżeli ogon ma długość 1, co weryfikuje strażnik, to elementy są łączone. Inaczej dla każdego elementu ogona tworzony jest symbol, a lista tak wygenerowanych symboli jest łączona z listą symboli wygenerowanych z ogona. Z tak powstałej listy usuwane są duplikaty. Następnie za pomocą kilku prostych testów sprawdzamy, czy symbol, podany jako drugi parametr, znajduje się na liście, jak duża jest lista, który symbol jest pierwszy w kolejności alfabetycznej.

A najwięcej czasu zajęło mi sprawdzenie jak to cholerstwo uruchomić, bo nie jest to takie oczywiste 🙂

Mały konkurs na DZone

Sierpień 8th, 2016

Dziś nic szczególnego. Małe ogłoszenie. DZone stworzyło konkurs/zabawę dla programistów. Wygląda zachęcająco. Rozwiązanie można wysyłać do jutra do 5 rano mniej więcej (11:59 EST).

Moje rozwiązanie będzie siedziało w repozytorium. Zatem jak ktoś nie ma zaplanowanego popołudnia, to już ma. Zachęcam do zabawy i dzielenia się swoimi rozwiązaniami.

Słowo o funkcjach anonimowych w Elixirze

Sierpień 5th, 2016

Na wczorajszym spotkaniu wroc-fp mieliśmy newsa w postaci „będzie grupa elixirowa” i tak przy okazji padło pytanie, jak działa operator & w elixirze.

Podstawy

Jak wiadomo, chociażby z tego artykułu, jedną z cech praktycznego programowania funkcyjnego jest możliwość przekazywania funkcji jako parametrów i zwracania ich jako wyników. Czasami potrzebujemy wykonać pewną operację przyjmującą jako argument funkcję, ale nie mamy odpowiedniej funkcji w API. W takim wypadku mamy dwie drogi. Pierwsza to utworzenie funkcji w module wraz ze wszystkimi tego konsekwencjami np. związanymi z metrykami, czy sposobem przekazywana funkcji jako parametru. Druga to przekazanie funkcji anonimowej.

Znak & jest powiązany z tą drugą ścieżką.

Funkcja anonimowa

Funkcja anonimowa w elixirze jest definiowana za pomocą konstrukcji fn -> end i może być przypisana do zmiennej:

Listing 1. Definiowanie funkcji anonimowej

iex> multipla = fn (a, b) -> a * b end
#Function<12.50752066/2 in :erl_eval.expr/5>
iex> multipla.(2, 2)
4

Wywołanie takiej przypisanej funkcji jest lekko dziwne, to przez kropkę, ale ma sens, jeżeli chcemy zasygnalizować, że multipla nie jest zwykłą nazwaną funkcją, a właśnie anonimową. Jak już wspomniałem, funkcję można przekazać jako parametr i zwrócić jako wynik:

Listing 2. Funkcja anonimowa jako parametr i wynik

iex>  b2 = fn (f2) -> fn (a) -> f2.(a, 2) end end
#Function<6.50752066/1 in :erl_eval.expr/5>
iex> multiplav2 = b2.(multipla)
#Function<6.50752066/1 in :erl_eval.expr/5>
iex> multiplav2.(2)
4

Działa i armaci. Jednak ten zapis ma pewną, drobną wadę. Jest słabo czytelny, gdy zaczynamy robić coś bardziej skomplikowanego.

Znak &

Odpowiedzią na problem czytelności jest użycie skrótowego zapisu wykorzystującego &:

Listing 3. Definiowanie funkcji anonimowej za pomocą &

iex> mlp = &( &1 * &2)
&:erlang.*/2
iex> mlp.(2, 2)
4

Mamy tu do czynienia z dwoma zastosowaniami znaku &. Pierwsze to oczywiście &(), które odpowiada fn -> end, czyli służy do zdefiniowania funkcji. Drugie to &1 i &2, które reprezentuje parametry funkcji. Jako że skrócona definicja funkcji nie zawiera parametrów nazwanych, to jasne jest, że musi istnieć jakiś sposób dostępu do nich. Zapis ze znakiem & jest takim właśnie sposobem.

Ograniczenia

Zapis ten ma pewne ograniczenia. Pierwszym jest brak możliwości stworzenia funkcji, która będzie wykonywać blok kodu:

Listing 4. & nie pozwala na „duże” funkcje

iex> &(
...> l =[&1, &2]
...> Enum.each(l , fn (el)-> IO.puts el end)
...> )
** (CompileError) iex: invalid args for &, block expressions are not allowed, got: (
  l = [&1, &2]
  Enum.each(l, fn el -> IO.puts(el) end)
)

iex> fn (a, b) ->
...> l = [a, b]
...> Enum.each(l, fn (el) -> IO.puts el end)
...> end
#Function<12.50752066/2 in :erl_eval.expr/5>

Jest to dobre ograniczenie. Dzięki niemu unikamy tego, co pojawiło się w Javie wraz z lambdami, czyli długich anonimowych bloków kodu nazwanych dla niepoznaki lambdami.

Drugim ograniczeniem jest brak możliwości zagnieżdżania się funkcji zapisanych za pomocą &:

Listing 5. Funkcje nie mogą się zagnieżdżać

iex> by2 = &( &( &1.(&1, 2)))
** (CompileError) iex: nested captures via & are not allowed: &(&1.(&1, 2))
    (elixir) src/elixir_fn.erl:114: :elixir_fn.do_capture/4

Oczywiście to też jest rozsądne, ponieważ nie mamy możliwości określenia, który parametr przynależy do której funkcji.

Podsumowanie

Podsumowując nasze dzisiejsze rozważania, możemy powiedzieć, że Elixir pozwala na definiowanie funkcji anonimowych. Mogą one być przypisane do zmiennej, mogą być parametrem, jak i wynikiem wywołania innej funkcji. Ze względu na intensywne użycie funkcji anonimowych mamy możliwość zapisania ich w skrócony sposób z wykorzystaniem znaku &. Zapis skrócony ma pewne ograniczenia. Z drugiej strony promuje on pisanie bardzo krótkich i zwięzłych funkcji.

Java 9 nadchodzi – prywatne metody w interfejsach

Sierpień 1st, 2016

Jedną z dużych zmian, jakie przyniosła ze sobą Java 8, było dopuszczenie implementacji metod w interfejsach. Używając słowa kluczowego default, możemy zdefiniować metodę, która będzie mieć implementację:

Listing 1. Przykładowy interfejs z implementacją z Javy 8

interface SomeService{

    default void validate(Client client){
        Preconditions.checkNotNull(client);
    }

    default void someLogic(Client client){
        validate(client);
        TransformedClient transformedClient = transform(client);
        emit(transformedClient);
    }

    default void emit(TransformedClient transformedClient){
        System.out.println(transformedClient);
    }

    TransformedClient transform(Client client);
}

Oczywistym zastosowaniem jest przygotowanie metod szablonowych. Nasz interfejs realizuje pewną większą logikę, która wymaga byśmy w ramach implementacji, dostarczyli jedynie fragmentu logiki reprezentowanego przez metodę someLogic. Tyle tylko, że zarówno validate jak i emit nie powinny być zmieniane (takie założenie). Na poziomie interfejsu jedynym rozwiązaniem, które nam to może zagwarantować, jest zamiana tych metod na statyczne. Nie możemy utworzyć metod finalnych w interfejsach. Jednak nadal pozostaje problem ujawnienia części implementacji. Klienta nie powinno interesować, w jaki sposób przeprowadzana jest walidacja ani jak są emitowane zdarzenia. Z doświadczenia wynika, że te dwie metody prędzej czy później staną się rozwiązaniem zastępczym dla jakiegoś kawałka kodu, gdzieś w odległym miejscu systemu. Skoro dostarczają tych mechanizmów, to czemu by z nich nie korzystać, zamiast zrobić to porządnie, na przykład wydzielając odpowiednią klasę.

A jak w Javie 9?

Java 9 dostarcza nam znacznie lepszy mechanizm, który pozwala na rozwiązanie tego problemu:

Listing 2. Przykładowy interfejs z prywatnymi metodami z Javy 9

interface SomeService{

    private void validate(Client client){
        Preconditions.checkNotNull(client);
    }

    default void someLogic(Client client){
        validate(client);
        TransformedClient transformedClient = transform(client);
        emit(transformedClient);
    }

    private void emit(TransformedClient transformedClient){
        System.out.println(transformedClient);
    }

    TransformedClient transform(Client client);
}

I to wszystko. Otrzymujemy zgrabny kod, który ma ukryte, to co powinno być ukryte i ujawnia tylko, to co trzeba.

Podsumowanie

Dziedziczenie wielobazowe zwane też wielodziedziczeniem w Javie jest realizowane za pomocą interfejsów. Dzięki temu nie mamy problemów związanych z konfliktami na poziomie implementacji. Wprowadzenie metod z domyślną implementacją w Javie 8 wymogło wprowadzenie kilku reguł dotyczących nadpisywania metod i rozwiązywania konfliktów w przypadku takich samych sygnatur. W dużym skrócie opierają się one, na wymuszeniu na programiście jasnej deklaracji co chce zrobić.
Java 9 wprowadza do tego mechanizmu rozwiązania, które pozwalają na uniknięcie części konfliktów, poprzez ukrycie niektórych metod. Ważniejsze jest jednak uzyskanie możliwości ukrywania elementów interfejsu. Dzięki temu zachowana będzie hermetyzacja naszego rozwiązania na odpowiednim poziomie. Zagrożeniem może okazać się wykorzystywanie interfejsów zamiast klas abstrakcyjnych. Szczególnie tam, gdzie klasy abstrakcyjne nie miały właściwości (pól). W ten sposób powstaną potworki, które będą miały wiele niepowiązanych odpowiedzialności.


Translate »