niedziela, 26 października 2014

CodeRetreat 2014 - nowe propozycje

To jest post promujący CodeRetreat 2014 w Łodzi. Sponsorem wydarzenia w 2014 roku jest firma Symphony Teleca. Spotkanie odbędzie się 15 listopada zaś link do meetupa i zapisów jest tutaj -> link do zapisów.

Jeśli tylko nie zapomniałem odblokować zapisów to już można się dodać. W tym roku Marek chciał zaprosić jakieś gwiazdy zagraniczne do poprowadzenia CR jednakowoż coś tam komuś wypadło i na razie pozostała moja skromna osoba do pomocy przy tym evencie. Jeśli komuś znudził się ten sam prowadzący 3 rok z rzędu może śmiało napisać na contact@juglodz.pl z własną propozycją ;)

Zmiany

Na ostatnim spotkaniu JUGa kilka osób narzekało - "nie tylko nie znowu Game of Life". Gra życie jako taka jest dosyć ciekawa pod względem edukacyjnym dlatego chce ją zostawić ale dla zwiększenia różnorodności i nauki świeżych technik nadszedł czas na jakieś nowe ograniczenia. Oczywiście dla tych, którzy chcą pozostaną standardowe ćwiczenia.

Jakiś czas napisałem artykuły o nowych podejściach do rozwiązania problemu komórek:

Ponieważ nie każdy lubi Scalę a również powyższe rozwiązania mogą być zbyt egzotyczne dla osób codziennie programujących w Javie - więc poniżej przyjrzymy się kilku patentom zaprezentowanym w Javie8.

Immutable i referential transparency

Pierwsze ograniczenie : obiekty muszą być Immutable (naprawdę nie wiem czy słowo "niemutowalne" w ogóle istnieje w słowniku polskim czy też to coś w stylu "komitować") i do tego należy zachować "referential transparency" (i znowu tłumaczenie tego zwrotu na polski może przynieść więcej szkody niż pożytku)

Spostrzeżenia :

  • Komórki nie przechowują w zasadzie żadnego stanu
  • Nie ma konieczności tworzyć za każdym razem nowej żywej/martwej komórki
abstract class Cell{
 abstract Cell evolve(int liveNeighbours);
}

class LiveCell extends Cell{
 @Override
 Cell evolve(int liveNeighbours) {
  if(liveNeighbours ==2  || liveNeighbours ==3){
   return liveCell();
  }else{
   return new DeadCell();
  }
 }
 
 static Cell liveCell(){
  return new LiveCell();
 }
} 

class DeadCell extends Cell{
 @Override
 Cell evolve(int liveNeighbours) {
  if(liveNeighbours ==3){
   return new LiveCell();
  }else{
   return new DeadCell();
  }
 }
        static Cell deadCell(){
  return new DeadCell();
 }
}
Ponieważ komórka jako obiekt nie może być raz żywa a raz martwa to nie ma potrzeby przechowywania informacji o stanie każdej komórki i możemy pokusić się o symulację nieskończonej planszy przy pomocy zwykłej mapy.
Map<Coordinates, Cell> board=new HashMap<>();
board.put(coordinates(1, 1), liveCell());
board.put(coordinates(1, 2), liveCell());
board.put(coordinates(1, 3), liveCell());
//evolve
Map<Coordinates, Cell> nextPhaseBoard=new HashMap<>();
//
Martwa komórka jest reprezentowana jako prosty "brak komórki" w mapie. I teraz możemy wykorzystać bardzo fajny patent z Javy 8, który został dodany do Mapy.
//calculations
Cell cellAt22 = board.getOrDefault(coordinates(2, 2), deadCell());
...

Bardziej Funkcyjnie

Na początek przyjmiemy ograniczenie - "Nie można tworzyć nowych klas" (Może z wyjątkiem klasy, która symuluje tuple/tupla bo to jest wygodne a w Javie8 jeszcze tego nie ma). Komórki będą zwykłym enumem co z jednej strony ogranicza rozszerzalność ale z drugiej ułatwia ćwiczenie.

Na początek nieskomplikowana implementacja coby mieć miłe wrażenie pracy z funkcjami :
Function<Integer, Cell> evolveLife = liveNeighbours -> {
  if (liveNeighbours == 2 || liveNeighbours == 3)
   return LIVE;
  else
   return DEAD;
 };
 
 Function<Integer, Cell> evolveDead = liveNeighbours -> {
  if (liveNeighbours == 3)
   return LIVE;
  else
   return DEAD;
 };

 BiFunction<Cell,Integer, Cell> evolve = (cell,liveNeighbours) -> {
  if (cell == LIVE) {
   return evolveLife.apply(liveNeighbours);
  } else {
   return evolveDead.apply(liveNeighbours);
  }
 };

 enum Cell {
  DEAD, LIVE
 }
W powyższej próbce kody uwazny czytelnik zauważy funkcję "evolve" i dwie pomocnicze funkcje dedykowane dla konkretnych typów komórek - generalnie powinno łatwo dać się to testować. No i możemy rzucić okiem jak wyglądać będzie wywołanie:
System.out.println(evolveLife.apply(3));
  System.out.println(evolveLife.apply(4));
  System.out.println(evolveDead.apply(3));
  System.out.println(evolveDead.apply(4));
  System.out.println(evolve.apply(LIVE,4));
  System.out.println(evolve.apply(LIVE,3));

Currying

Kolejne ograniczenie to "maksymalnie jeden parametr na funkcję". W tym przypadku nie trzeba używać BiFunction (a tak w ogóle to Java8 zdaje się nie ma czegoś takiego jak funkcja z 3 lub więcej parametrami).

Function<Cell,Function<Integer, Cell>> evolve = cell->liveNeighbours -> {
  if (cell == LIVE) {
   return evolveLife.apply(liveNeighbours);
  } else {
   return evolveDead.apply(liveNeighbours);
  }
 };

//wywołanie
System.out.println(evolve.apply(LIVE).apply(4));
System.out.println(evolve.apply(LIVE).apply(3));

W dużym uproszczeniu cały patent z jednym parametrem pozwala nam wstrzykiwać "kontekst" uzyskując funkcję jedno-argumentową przygotowaną do rozwiązania specyficznego problemu.
Function<Coordinates, List<Coordinates>> findNeigbhoursCoordinates=coordinates->{
  return asList(//jakies tam obliczenia);
 };
 
 Function<Map<Coordinates, Cell>, Function<Coordinates,Integer>> countNeighbours=board->coordinates->{
  return (int)findNeigbhoursCoordinates.apply(coordinates)
  .stream()
  .map(c->board.getOrDefault(c, DEAD))
  .filter(cell->cell==LIVE)
  .count();
 };
 
 Function<Integer,Function<Cell, Cell>> evolve = liveNeighbours->cell-> {
  if (cell == LIVE) {
   return evolveLife.apply(liveNeighbours);
  } else {
   return evolveDead.apply(liveNeighbours);
  }
 };
I działać to mogłoby tak :
void test(){
  Map<Coordinates, Cell> boardCurrentPhase=new HashMap<>();
  Map<Coordinates, Cell> boardNextPhase=new HashMap<>();
  List<Coordinates> potentialCellsWithChangeState=new LinkedList<>();
  
//wstrzykujemy planszę z danej fazy gry
  Function<Coordinates, Integer> currentPhaseNeighbours = countNeighbours.apply(boardCurrentPhase);
  
  potentialCellsWithChangeState.forEach(c->{
   Integer n = currentPhaseNeighbours.apply(c);
   Cell cell = boardCurrentPhase.getOrDefault(c, DEAD);
//tutaj akurat nie ma konkretnego zysku z rozdzielenia parametrów ale nadal zróbmy to dla ćwiczeń
   Cell evolvedCell = evolve.apply(n).apply(cell);
   if(evolvedCell==LIVE) boardNextPhase.put(c,evolvedCell);
  });
  
 }

Powyższy przykład być może nie oddaje jakoś specjalnie wszystkich zalet konstrukcji [jeden argument,zwracana funkcja] ale na tę chwilę nic lepszego nie przychodzi mi do głowy. Jeśli ktoś jest ciekaw owych zalet to niech zerknie na link -> what-is-the-advantage-of-currying

W naszym kodzie można jeszcze zrobić coś takiego :

Map<Cell, Function<Integer, Cell>> handlers=new HashMap<Cell, Function<Integer, Cell>>(){{
  put(LIVE,evolveLife);
  put(DEAD,evolveDead);
 }};
 
 Function<Map<Cell, Function<Integer, Cell>>,Function<Integer,Function<Cell, Cell>>> evolveGeneral = 
   handlers->liveNeighbours->cell-> handlers.get(cell).apply(liveNeighbours);
 
 Function<Integer, Function<Cell, Cell>> evolve = evolveGeneral.apply(handlers);

Teraz możemy niezależnie modyfikować funkcje obsługi ewolucji poszczególnych komórek ale kosztem potworka w systemie typów (czyli to co tam jest po lewo)

Małe podsumowanie

  • Funkcja evolve nic nie wie o tym na jakieś planszy toczy się gra czyli uzyskaliśmy tzw. "information hiding"
  • Funkcja countNeighbours być może wie trochę za dużo - tutaj można by jeszcze oddzielić funkcjonalność zliczania określonych typów komórek od pobierania ich z planszy(Może zwrócić stream?).
  • evolveLife i evolveDead to proste funkcje do testowania
  • Można jeszcze podzielić to co dzieje się w metodzie test
  • Teoretycznie po zmianie w countNeighbours ze stream na parallelStream wszystko powinno działać
Czy można to zrobić lepiej? Na pewno - rok temu próbowałem napisać coś takiego w scali i wyszła masakra :) - generalnie za każdym razem idzie to coraz lepiej. Na tym właśnie polega nauka. Na CR nikt nikogo nie ocenia i tez po to usuwamy zawsze kod - by nie bać się oceny. Strach przed oceną to chyba jeden z największym hamulców w nauce i samorozwoju ogólnie.

Bardziej obiektowo

Nie koniecznie trzeba rozwiązywać CR funkcyjnie - po prostu można i z Javą 8 jest łatwiej (niż z Javą7). Można też pokusić się o rozwiązanie CR według niektórych bardziej obiektowo niż na klasach - a mianowicie na aktorach. Może coś w tym temacie uda mi się jeszcze opisać przed 15 listopada.

niedziela, 12 października 2014

Subiektywny poziom szczęścia i lepsza percepcja

Artykuł rozpoczniemy od czegoś zupełnie z nim nie związanego. Niedawno Erik Meijer zareklamował swój kurs programowania funkcyjnego na stronie edukacyjnej o której istnieniu nawet nie wiedziałem : https://www.edx.org/. (A sam kurs jest tutaj : link do kursu) Dzięki temu szczęśliwemu splotowi zdarzeń jakże szczęśliwie udało mi się na tej samej stronce natrafić na inny szczęśliwy kurs : The Science of Happiness

Wbrew pozorom nie jest to kurs przyrządzania kompotu czy też gotowania grzybów lecz całkiem naukowe podejście do tego o co chodzi w ogólnie pojętym poczuciu szczęście, jak ludzie je odczuwają oraz co dla programistów może być najciekawsze : jak ono wpływa na naszą percepcję (No a percepcja w trakcie programowania to jest ważna).

Czy to się może przydać?

Według badań bardzo - ale co to za badania(standardowo amerykańskich naukowców)? Otóż Ludziom z różnym acz nierównym poziomem subiektywnej percepcji szczęścia prezentowano pewne obrazki i sprawdzano "jak szeroka" była ich percepcja - czyli np. czy zauważyli jakieś dziwne szczegóły w tle lub na peryferiach sceny. Robi się przy tym odpowiednio dużą ilość pomiarów aby mieć pewność, że konkretne zachowanie nie wynika ze skłonności indywidualnych.

Albo lepszy patent - popularnie pożądane "myślenie out-of-the-box". Ludzie oceniają obrazki składające się z trójkątów i kwadratów i w zależności od poziomu "poczucia szczęścia" rozpoznają więcej podobieństw w szczegółach (tu i tu są trójkąty) lub - przy większym poczuciu szczęścia - podobieństwa w ułożeniu kształtów - gdzie "kształt" czy "ułożenie" raczej idą w stronę operowania na abstrakcjach - czyli lekko wychodzi się ze wstępnie zdefiniowanego schematu.

A sytuacja gdzie ktoś skupiał się bardziej na szczegółach też niekoniecznie musi mieć swoje plusy. Generalnie ogólny "stan niepokoju" wyzwala reakcję walcz lub uciekaj, które przydaje w razie walki z dzikami lub żulami w nocnym ale niekoniecznie do szukania bugów.

Jak na razie zainteresowanych dokładnymi badaniami mogę odesłać do prac dwóch pań, które zajmują się tą dziedziną.

I zamiast linka filmik z opisem badań -

Adaptacja hedonistyczna

Na wstępie ważny komunikat aby ktoś błędnie nie zrozumiał poniższych wywodów. Stawianie sobie wyzwań, bycie ambitnym, posiadanie pasji która popycha nas do kolejnych działań, walki o lepsze życie i nie pozwala stać w miejscu to dobre rzeczy. W dalszych akapitach chodzi jedynie o subiektywne odczucie tego co się dzieje i o zastąpienie poczucia subiektywnie wygenerowanego poczucia nieszczęścia czymś lepszym

Standardowy dzień : Wstajesz i myślisz - o jak zajebiście mam łóżko i w ogóle mam jakieś mieszkanie - jak cudownie. Idziesz do lodówki i pełen szczęścia myśli - "no jak wyśmienicie, że mam jakieś jedzenie". Podążasz na przystanek i w twoje głowie rozbrzmiewa pieśń fascynacji - "czy to nie cudowne, że mamy w ogóle komunikację miejską?". Jedziesz i z radością spoglądasz w okno ze świadomością, że przecież nie trzeba się denerwować bo nikt nie będzie do ciebie strzelać.

Mechanizmem odpowiedzialnym za to, że poniższy opis się w zasadzie u nikogo nie wydarzy jest - Adaptacja hedonistyczna czyli zwykłe przyzwyczajenie się do naszego obecnego otoczenia.

Pomyślimy jakie jest najgorsze położenie w jakim możemy się znaleźć? Może zaczniemy od autora Viktor Frankl i jego książki Man's Search for Meaning. On opisuje tam swoje przeżycia - zwłaszcza wewnętrzne - z obozu koncentracyjnego w trakcie drugiej wojny światowej. Wydaje mi się, że to właśnie w tej książce są opisy ekstazy jaką przeżywał autor gdy znalazł gdzieś na ziemi kawałek nadającego się do jedzenie zagubionego ziemniaka.

Może też być Etiopia czy też inny kraj gdzie ludzie na co dzień umierają z głodu. Czy wstając rano cieszysz się, że w okół ciebie nie ma strażników obozu a w lodówce jest jedzenie i do tego bieżąca woda w kiblu (a no i, że w ogóle jest jakiś kibel!). Raczej nie. Być może twoje życie jest "nieszczęśliwe" bo samochód ma już 7 lat i znowu w te wakacje nie udało ci się wyjechać dalej niż do Egiptu :((( I do tego ku**a robisz ciągle w Javie 6!!

Perspektywa

Jeśli np. życie przeciętnego Polaka nie jest wypełnione radością z faktu, iż wiedzie mu się znacznie lepiej niż przeciętnemu mieszkańcowi Etiopii to ciekawe czy ten efekt idzie jeszcze dalej i dalej...?

  • Mieszkaniec najbiedniejszych obszarów Afryki
    • "Wstaję codziennie rano i od samego początku, od najwcześniejszych godzin odczuwam głód a świeżej wody nie widziałem od miesięcy, dlaczego jaa, dlaczegooo?!?! "
  • Średnia klasa - Europa wchodnia
    • "Wstaję codziennie rano i od samego rana sąsiad kuje ściany, dlaczego jaa, dlaczegooo?!?! "
  • Średnia klasa - Europa zachodnia
    • "Czekałem na ten wyjazd od dwóch miesięcy i akurat wczoraj popsuła się moja terenówka, dlaczego jaa, dlaczegooo?!?! "
    Właściciel średniej firmy
    • "Cholera jasna byłą okazja zarobić 40 baniek ale za szybko się zgodziłem i mam tylko 30, fak,fak,fak,fak,fak,fak?!?! "
    Właściciel ogromnej firmy globalnej
    • "Cholera jasna i znowu Bil Gejts jest na okładce Tajmsa, co robię źle, co robię nie tak?!?!?! I jeszcze akcje poszły 0,1% w dół - nieszczęścia chodzą parami!!Dlaczego, dlaczegooo?!?!? "

Oczywiście to tylko przypuszczenia - być może ludzie z 4 ostatnich pozycji wstają rano i z pełną piersią mówią "ależ jestem niesamowicie szczęśliwy, że moje życie nie jest tak przejebane jak (przynajmniej) przeciętnego Polaka:D"

Lekarstwo

Istnieje bardzo pomocna technika - Cognitive reframing, która doskonale nadaje się do odzyskania poczucia szczęścia w "jedynie subiektywnie strasznej sytuacji". Potrzebna będzie jedynie dobra wyobraźnia, która pozwoli odczuć scenkę wszystkimi zmysłami. Trzeba się wczuć w wymyśloną sytuację a następnie przez odczucie różnic pomiędzy tym gdzie jesteśmy a gdzie moglibyśmy być - dojść do przekonania (popartego wszystkimi zmysłami), że w sumie to mamy dobrze :)

Scena 1

Mieszkasz gdzieś na północy Iraku. Budzi cię strasznie głośny huk. Po kilku sekundach gdy gdy twoja świadomość w końcu przestawia się ze snu w stan aktywny orientujesz się ku swemu przerażeniu, że twoja wioska jest otoczona przez odziały państwa islamskiego. Nie myśląc długo wybiegasz na podwórko - twoje serce bije 200 uderzeń na minutę. Niestety zauważa cię jeden z żołnierzy i niecelnym strzałem trafia w lewe kolano. Osuwasz się w bólu na ziemię. Żołnierz podbiega do ciebie skracając dystans tak aby mieć pewność, ze kolejna kula trafi w głowę. Czujesz zimno bijące od ziemi i przed oczyma przelatuje ci całe życie. Naqle staje się coś dziwnego - wszystko w okół zamiera. Żołnierz, który chciał cię zaatakować stoi w bezruchu. Niczego nie słychać. I wtedy tak jak na film S-F zagina się czasoprzestrzeń i pojawia się wróżka (lub wróżek jak kto woli) - taka mała latająca z różdżką - słyszysz głos. "- Dam ci wybór ale wybór nie będzie łatwy. Możesz zostać tu gdzie jesteś albo obudzisz się gdzieś w europie wschodniej w firmie informatycznej. Będziesz miał mieszkanie i samochód ale słuchaj jest tutaj haczyk
- co?
- otóż dzień w którym się obudzisz nie będzie zwykłym dniem. Jest to bardzo zły dzień - dzisiaj padły wam serwery, mieliście downtime i SLA poszło się walić i właśnie zaczyna się miting z menadżerami.
- co mnie to interesuje!?!?! zabierz mnie stąd
"

Jak będziesz się czuł gdy już obudzisz się gdzieś w Europie wschodniej? Jaki będzie twój poziom szczęścia gdy napastników z karabinami zastąpi spotkanie z managerami? I znowu nie chodzi o to, że awaria serwerów to coś dobrego - raczej jeśli to tylko możliwe warto jej unikać. Chodzi jedynie o to co się dzieje w tym momencie w tobie. Uspokoisz się - będziesz miał czystszy umysł i sprawniej rozwiążesz problem, nie będzie wrzodów i nadwagi wywołanej stresem.

Scena 2

Mieszkasz gdzieś na wschodzie Ukrainy. Ze smutkiem spoglądasz na gruzy twojego domu w którym spędziłeś całe swoje życie. Wózek z kilkoma ubraniami i zwinięty tobołek jedzenia - to wszystko co ci zostało gdy oddziały separatystów zbombardowały ostatniej nocy okoliczne zabudowania. Nie spałeś od 40 godzin, czujesz się niezwykle zmęczony, przed tobą długa droga chociaż w zasadzie nie wiesz gdzie będziesz podążał. Nie wiesz nawet czy dotrzesz tam żywy. Wtem z okolicznej drogi dobiega mrożący krew w żyłach odgłos gąsienic czołgu - są bliżej niż myśleliście... Błyskawicznie odgłos milknie, przyroda wokół ciebie wydaje się nie ruszać. Kolor stają się wyblakłe. Czujesz obecność czegoś bliżej nieokreślonego - nie wiadomo skąd przemawia do ciebie głos o bardzo niskim tonie "- Dam ci wybór ale wybór nie będzie łatwy. Możesz zostać tu gdzie jesteś albo obudzisz się gdzieś w europie wschodniej - będziesz miał całkiem wygodne życie w firmie informatycznej ale słuchaj jest tutaj haczyk
- co?
- Otóż stary słuchaj nie uwierzysz, kupiłeś płytki za 500 złotych a ludzie z ekipy, którą wynająłeś krzywo je położyli i trzeba wszystko zrywać.
- co mnie to interesuje!?!?! zabierz mnie stąd
"

Jak będziesz się czuł gdy już obudzisz się gdzieś w Europie wschodniej? Jaki będzie twój poziom szczęścia gdy spaloną wioskę zastąpisz krzywo położonymi płytkami? I znowu nie chodzi o to, że 500 złotych w plecy to coś dobrego - jeśli to tylko możliwe należy tego unikać. Chodzi jedynie o to co się dzieje w tym momencie w tobie. Uspokoisz się - będziesz miał czystszy umysł i sprawniej rozwiążesz problem, nie będzie wrzodów i nadwagi wywołanej stresem.

I trochę trucizny

Jak sprzedać produkt? Klient ma potrzebę, której jest świadomy. Ponieważ jest ona niezaspokojona to jest przez to mniej szczęśliwy - produkt zostaje sprzedany a klient jest teraz szczęśliwszy gdyż potrzebę zaspokoił.

Co gdy klient nie ma potrzeby? Albo trzeba mu ją uświadomić - na przykład, że istnieje narzędzie X, które pomoże lepiej robić pracę Y. Dopóki klient nie wiedział o narzędziu X nie mógł być nieszczęśliwy, że go nie ma (choć mógł być nieszczęśliwy, że wykonuje pracę Y). Jeśli np. obcinanie gałęzi wcale nie było takie straszne ale teraz z nowym sekatoremX można to robić przyjemniej to marketing musi się zająć tym aby aby uczucie niewygody spowodowanej używaniem starego sekatora było tak duże, że klient kupi nowy. Teoretycznie marketing musi sprawić aby klient poczuł się bardziej nieszczęśliwy w swojej obecnej sytuacji. (za wyjątkiem oczywiście sytuacji gdzie Y naprawdę warto zaspokoić nowym produktem bez zbędnej ingerencji zespołu z marketingu - np. zbiera pszczelego miodu nago niekoniecznie musi być przyjemnym zajęciem - dobry skafander nie potrzebuje tutaj reklamy).

Inna sytuacja to gdy nie ma absolutnie żadnej potrzeby by używać produktu X albo ta potrzeba jest marginalna. Ostatnio na przykład bombardują mnie jakieś reklamy szamponów na youtube. Koleś wchodzi na imprezę, laski go ignorują - później wali szampon na dłoń rozprowadza po włosach. Te mu się ca chwilę puszą i wyginają aż nagle staje się duszą towarzystwa. No ciekawe co sobie myśli łysy z brazzers jak ogląda tę reklamę :D Tak czy inaczej marketingu po raz kolejny próbuje wygenerować poczucie niespełnienia, niezaspokojonej potrzeby - nieszczęścia, którą zaspokoi produkt. Temat starałem się poruszyć dawno temu - Higiena psychologiczna - to było ponad dwa lata temu także, część przemyśleń może być już nieaktualna.

Zakońćzenie

Na polskim w technikum uczyli, że trzeba pisać zakończenie do wypracowań - a ja mam to w dupie. Będę czuł się szczęśliwy bez zakończenia czego i Tobie życzę drogi czytelniku.

piątek, 3 października 2014

PlayFramework warsztaty - REST - część 2

To jest dalszy ciąg lekcji o Jsonie i webserwisach -- A tutaj jest część 1

Pomimo iż materiału wydawało się być dużo to jednak udało się zrobić całość w 2,5 godziny (Między innymi dlatego, ze już nie chce mi się z pizzą kombinować i nie ma przerwy - ale zawsze ostrzegam - ludzie weźcie kanapki! (Albo odżywki - przyp. red.)).

Prąd i sala się same nie opłaciły to ten tego :
A poniżej wszyscy bez żadnego zewnętrznego przymuszenia promieniują wewnętrznym szczęściem i radością :

A tutaj są linki do poprzednich części :

Wywołanie

Do tego ćwiczenia zostawiamy włączoną aplikacyjkę z poprzedniej części - to będzie apiserwer , zaś to co powstanie tutaj to taki apiclient.

By obydwa serwery się nie pogryzły ten drugi uruchamiamy na innym porcie w ten oto wygodny sposób :

~run 8000

I wołamy :
def webserwis = Action.async {
    import scala.concurrent.ExecutionContext.Implicits.global
    val futureResult=WS.url("http://localhost:9000/list").get
    futureResult.map{response=>
     Ok("otrzymalem : "+response.json)
    }
  }

I to w zasadzie tyle w wariancie podstawowym - powinno śmigać. Trzeba uczestnikom wytłumaczyć tutaj co to jest to "async" i nadmienić, że z wywołania WS dostajemy

Future[Response]
. O samych Future będzie więcej za chwilę.

No i mogą pojawić się pytania o to co to jest i skąd się wziął ExecutionContext. W tym miejscu może się przydać strona z dokumentacji Playa o pulach wątków.

Wysyłanie posta z ciałem

Wygląda tak :
def dodajMeal = Action.async {
    val result = WS.url("http://localhost:9000/add").post(Json.obj("name" -> "kurczak", "calories" -> 1000))
    result.map(r => Ok("dostałem : " + r.body))
  }

Future poligon

Kiedy już mamy ogólne pojęcie o używaniu klienta Webserwisów przyjrzyjmy się bliżej temu co ono zwraca - czyli Future. Tutaj można wspomnieć jak Future jako abstrakcja ratuje nas od masy zagnieżdżonych callbacków.

Jedna ważna rzecz jeśli chodzi o zabawę Future - tutaj nie da się używać worksheet bo plik jest cały czas odświeżany i procek się pali dlatego normalnie w mainie napiszemy kod.

Najpierw zasymulujmy synchroniczne wywołanie serwisu z zamuleniem jednej sekundy

object Poligon {

  def main(args: Array[String]) {
    val result = servis1
    println("start")
    println(result)
    println("end")
  }

  def servis1 = {
    Thread.sleep(1000)
    10
  }
}

I wynik :
start
10
end

Z future


  def main(args: Array[String]) {
    val executorService = Executors.newCachedThreadPool()
    implicit val executionContext = ExecutionContext.fromExecutorService(executorService)

    val result = Future{servis1}
    println("start")
    result.onSuccess{case result=>println(result)}
    println("end")
  }
Wywołanie serwisu nastąpiło asynchronicznie więc rezultat będzie na samym końcu.
start
end
10
No i aby pokazać jakie patenty są możliwe to dajmy na to zmodyfikujemy sobie rezultat
val result = Future { servis1 }.map(_+3)
I mamy teraz :
start
end
13

dwa servisy

O a tutaj proszę jak ładnie komponujemy dwa asynchroniczne wywołania serwisów
object FuturePoligon {
  def servis1() = {
    Thread.sleep(1000)
    10
  }
  
  def servis2(arg:Int) = {
    Thread.sleep(1000)
    arg+7
  }
}

val result = Future { servis1 }.map(result1=>servis2(result1))
    println("start")
    result.onSuccess{case result=>println(result)}
    println("end")
start
end
17

Który future pierwszy

object FuturePoligon {
  def servis1() = {
    Thread.sleep(1000)
    10
  }
  
  def servis2(arg:Int) = {
    Thread.sleep(800)
    arg+7
  }
}

    val future1 = Future { servis1 }
    val future2 = Future { servis2(4) }
    val completedFuture=Future.firstCompletedOf(Seq(future1,future2))
    println("start")
    completedFuture.onSuccess{case result=>println(result)}
    println("end")

start
end
11

Obsluga faili

import scala.util.{Failure,Success}

 val futureFail = Future {
      throw new Exception("się zjebało")
    }

    futureFail.onComplete {
      case Success(value) => println(value)
      case Failure(e)     => println(e)
    }

    futureFail.onFailure {
      case e => println(e)
    }
wypisuje :
java.lang.Exception: się zjebało
java.lang.Exception: się zjebało

Zaginione strony

Ponieważ te ćwiczenia nie powstają za jednym razem ale ciągle wracam i coś poprawiam także czasem zostawiam sobie notatki na przyszłość. W tym miejscu zapisałem sobie "ćwiczenia na typach i zdjęcie z goblinsów" - o co mogło mi chodzić? Pojęcia nie mam. Ale obrazek będzie :

Co dalej?

Na pewno ciekawe będzie poćwiczenie użycia Play i Akka razem ale wcześniej - ponieważ jest zainteresowanie takimi ogólnymi warsztatami o Scali to coś zrobimy też w tym kierunku :