Swift Operatory

operator

Czas odczarować operatory. Przestać się ich bać i zacząć traktować tak jak każde kolejne narzędzie. Mają swoje miejsce, są użyteczne i ułatwiają pracę przy pewnych zadaniach.

Operatory, które znamy

Już we wczesnych latach edukacji jesteśmy nauczani operatorów. Na 100% gdy powiem, że chodzi mi o dodawanie, odejmowanie, mnożenie i dzielenie będziesz mieć w głowie zbiór graficznych symboli, które reprezentują te arytmetyczne operacje.

+ − ✕ ÷

Użycie ich też jest bardzo “intuicyjne”. Używam tutaj cudzysłowu ponieważ ta intuicja jest w nas wpajana od najmłodszych lat. Z tego powodu te operatory są oswojone. Znamy ich znaczenie i bez problemu można przewidzieć wynik ich operacji.

2 + 2 = 4 
3 − 2 = 1
2 ✕ 4 = 8
4 ÷ 2 = 2

Niestety nie wszystkie operatory są tak łatwo dostępne na klawiaturze. I aby je wygodnie szybko pisać w programowaniu kilka z nich wygląda nieco inaczej ale też nie tak zupełnie od czapy:

2 + 2 // 4
3 - 2 // 1
2 * 4 // 8
4 / 2 // 2

Świat bez operatorów

Zobaczmy przez chwilę jak to samo wygląda bez użycia operatorów a za pomocą funkcji.

     add(2, 2) // 4
subtract(3, 2) // 1
multiply(2, 4) // 8
  divide(4, 2) // 2

Tragedii nie ma. Można nawet powiedzieć, że fajnie się czyta. Jednak czy też tak samo fajnie się skaluje? Sprawdźmy na nieco bardziej skomplikowanym przykładzie. Obiecuje, że nie dużo bardziej:

// (2 + 8 / 2) * 4 + 1

add(multiply(add(2, divide(8, 2)), 4), 1)

Czytelność i zwięzłość tego kodu wyleciała przez okno. Na pierwszy rzut oka trzeba się zastanowić co w jakiej kolejności się wykona. Owszem operatory arytmetyczne też mają swoją kolejność wykonywania, ale jest to tak oswojone, że praktycznie nie trzeba o tym myśleć. Intencja programisty jest jaśniejsza i mniej pokręcona (chociaż w obu przypadkach na końcu dostaniemy ten sam wynik: 25).

Modyfikacja kodu z operatorami jest łatwiejsza. Przez to, że zapis jest dużo bardziej gęstszy to dużo łatwiej jest dodać tam kolejne operacje lub je usunąć. W wypadku zapisu z “funkcjami” wcale nie jest to takie oczywiste, która para nawiasów to ta para nawiasów.

Reputacja Operatorów

W pracy wiele razy spotkałem się z opiniami, że nie powinno się używać własnych operatorów. Trzeba przyznać że nadużywane mogą sprawić, że trudno będzie odgadnąć co dany kod robi. Z tym się zgadzam, ale… musi być jakieś ale… to samo można powiedzieć o dowolnej bibliotece, która wprowadza nieznane API. Co tak na prawdę robi metoda merge lub connect, send, exit, pay?

Nazwy w programowaniu są bardzo ważne. Gdy nic nie wiemy o danej metodzie to możemy zgadywać na podstawie nazwy i naszego doświadczenia co powinno się zadziać. Gwarancji oczywiście nie mamy ale pewne nazwy wywołują w nas fale skojarzeń. Trafnych czy nie to już nie ma znaczenia, ponieważ mamy coś czego możemy się złapać. Po prostu nikt nie lubi czuć się jak totalny idiota i nie wiedzieć o co chodzi.

Operator dobrze skonstruowany też może nieść ze sobą znaczenie. Co myślisz gdy widzisz lub ⛔️? Te symbole chociaż nie idealne też odnoszą się do czegoś co znamy. Co więcej są bardziej uniwersalne niż słowo pisane. I pewne kształty są bardziej rozpoznawalne niż inne, ale wszystko sprowadza się do tego czy coś znamy czy nie.

Kolejna trudność to jest pewna nie wiadoma jak działają operatory. Normalnie nikt nie ma problemu z wywoływaniem metod na obiektach. Jednak jak tylko pojawi się operator nagle całemu miastu zaczyna odwalać.

Anatomia Operatora

Operator w Swift jest zwykłą funkcją. Jedyne co jest nie zwykłe to sposób jej wywołania. Cała reszta zachowuje się dokładnie tak samo.

let      add: (Int, Int) -> Int = (+)
let subtract: (Int, Int) -> Int = (-)
let multiply: (Int, Int) -> Int = (*)
let   divide: (Int, Int) -> Int = (/)

Swift pozwala przypisywać funkcje do zmiennych (w tym wypadku stałych). Za każdym razem typ tej funkcji jest taki sam. Funkcja przyjmuje dwa argumenty typu Int i zwraca -> wartość typu Int. Całość prezentuje się tak (Int, Int) -> Int. Na końcu do każdej z tych referencji przypisujemy po odpowiednim operatorze np. = (+). Muszę owinąć to w nawiasy () aby Swift wiedział, że chodzi mi o referencje do tej funkcji a nie o jej wywołanie. Dokładnie tak zdefiniowałem funkcje w przykładzie wyżej add(multiply(add(2, divide(8, 2)), 4), 1).

Idąc krok dalej to mogę przykład ten zapisać z pomocą operatorów wywołanych jak po prostu funkcje. Będzie to wyglądało tak:

(+)((*)((+)(2, (/)(8, 2)), 4), 1) // 25

Powodzenia z odszyfrowaniem tego co tu się dzieje. Swift jednak pozwala funkcje, które są operatorami wywołać w sposób, który doskonale znamy i lubimy: (2 + 8 / 2) * 4 + 1.

Podsumowanie

W tej serii zaczniemy podróż po różnych operatorach. Zobaczymy ich kształt oraz działanie. Też postaram się pokazać, że dobrze użyte pozwalają się skupić na tym co chcemy zrobić (deklaratywny styl programowania) a nie jak to chcemy zrobić (imperatywny styl programowania).

Aby nie zostawić tego tak w próżni oto są źródła z których korzystam i na których się uczę. Szczerze zachęcam do zapoznania się z nimi.

  • pointfree.co
    Bardzo dużo informacji o programowaniu funkcyjnym. Wiele z operatorów jakie tu przedstawię zobaczyłem tam po raz pierwszy a potem wpadłem do Zoo Haskell-a. Może jako minus to, że jednak większość treści jest płatna. Jednak sporo bardzo dobrego contentu jest za free.
  • Swift 5’s brand new Result Type – iOS Conf SG 2019
    Daniel Steinberg ze swoim humorem opowiada o Result oraz pokazuje jak można użyć kilku operatorów aby nieco zagęścić kod.
  • Runes
    Biblioteka w Swift definiująca kilka mniej oczywistych operatorów.

1 myśl na “Swift Operatory”

  1. Pingback: Swift do Bohatera! Operatory Arytmetyczne oraz Przepełnienia - I do IT

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *