poniedziałek, 30 czerwca 2014

Play - bonusy do ćwiczeń

Poniżej lista małych tematów, które można dorzucać do większych ćwiczeń jak jakimś cudem zostanie trochę czasu.

Globalne info o braku strony

To je dobre bo w innych frameworkach ten mechanizm jest na porządku dziennym.

//Global.scala - w katalogu app- będzie warning o  tym by nie dodawać do domyślnej paczki.
object Global extends GlobalSettings {

  override def onStart(app:Application){
    Logger.info("startujemy ku*wa") //to tak dla bajery
  }

  override def onHandlerNotFound(request: RequestHeader) = {
    Future.successful(NotFound(
      views.html.nieZnalazlem(request.path) 
    ))
  }
}

//nieznalazlem.scala.html
@* nieZnalazlem Template File *@
@(param: Any)

<h1>Nie ma takie strony ku*wa</h1>

Taki niby Quartz

Tutaj jest pierwszy kontakt z Akka.

//Global.scala
override def onStart(app:Application){
    Logger.info("startujemy ku*wa")
    
    import play.api.Play.current
    import scala.concurrent.duration._
    import scala.concurrent.ExecutionContext.Implicits.global
    
    val quartzActor=Akka.system.actorOf(Props(new quartz.QartzActor()))
    Akka.system.scheduler.schedule(0.seconds,30.seconds,quartzActor,RobCos())
  }

class QartzActor extends Actor{
 def receive={
   case RobCos()=>println("robie cos")
   case _ =>println("robie nic")
 }
}

case class RobCos()

Zabawniejszy konfig

Konfig w Playu jest bardzo wygodny w użyciu i ma kilka ciekawych sztuczek. Są takie niby przestrzenie nazw dla propertasów oraz sklejanie propertasów, które fajnie działa przy sklejaniu ścieżek. Tu dla sportu stworzymy sobie ścieżkę z warzywa.

//application.conf
warzywo: {
 kolor=zielony,
 smak=kwaśny
}

property=${warzywo.kolor}/resztaSciezki

//html
@play.api.Play.current.configuration.getString("warzywo.kolor")
<br/>
@play.api.Play.current.configuration.getString("property")

Minimalizacja javaskryptu

Ten tamat znacznie się rozwinął w Play 2.3 za sprawą sbt-web (chyba) także tutaj uboższa wersja dla wersji 2.2

w app stworzyć paczkę assets.js

//test.js
function dzialaj(a){
 var tekst="aaa"
 console.log(tekst)
}

//_notCompiled.js
function dzialaj2(a){
 var tekst="aaa"
 console.log(tekst)
}

Teraz trzeba odświeżyć stronkę bo jakoś sam z siebie tych rzeczy nie kompiluje i mamy

//test.min.js
function dzialaj(a){var tekst="aaa";console.log(tekst)};

//_notCompiled.js
function dzialaj2(a){
 var tekst="aaa"
 console.log(tekst)
}

I to to nawet nam kompiluje javascript i sprawdza składnie! Bardzo ładnie!

No i to były właśnie takie krótkie małe tematy.

niedziela, 22 czerwca 2014

Play - formularze - fotki i spostrzeżenia

Najpierw komercha

Pizza sama się nie kupuje - więc jest i logo firmowe.
Obsługa wyjątków bez wyjątków.
W sumie trzeba było zrobić dwa zdjęcia - najpierw jedno z lewej strony - a w drugim wszyscy przechodzą na prawo i zakładają dla niepoznaki ubrania na lewą stronę - frekwencja 200%.
A tak się obsługuje formularze

Co następne

Teraz po wakacjach najpewniej :

  1. Powtórka wprowadzenia - teraz być może z Play 2.3
  2. Wystawianie RESTa przy pomocy JSONa i konsumpcja przy pomocy webservisów (żeby nie bawić się w javaskrypty)
  3. Integracja Playa z Akka

Spostrzeżenie

Aby w tym wpisie pojawiło się coś poza autopromocją - czas na ciekawe spostrzeżenie jakie pojawiło się w trakcie warsztatów.

Sytuacja wygląda tak : są jakieś okoliczności i pojawia się reakcja na te okoliczności. Reakcja wtapia się w światopogląd i nawet gdy już okoliczności się zmieniają i dana reakcja jest już być może niepotrzebna to już zdążyła stać się dogmatem, którego się nie kwestionuje bo nie. Dogmat w tym przypadku brzmi "nazwy zmiennych muszą zdradzać ich przeznaczenie czyli w skrócie - być czytelne". To było zbawianie gdy trzeba było pracować z 10000 liniami "managero-helkperów" w Javie (jeśli dla kogoś nie jest to czas przeszły to współczuję) ale czy jest to aktualne gdy nie pracuje się z okaleczonym kodem?

I teraz nowa sztuczka z kołczingu - aby uratować się przed flejmwarem należy sytuacje przedstawić nie w kontekście "co jest lepsze" ale raczej "czy zabawa w szukanie dziur w dogmacie da mi nowe możliwości i rozwinie moją osobowość abym stał się bardziej kompletnym bytem"- ano dokładnie tak!

Inspiracja tym tematem pojawiła się u mnie po lekturze jakiegoś bloga, którego teraz nie mogę znaleźć ale generalna koncepcja polegała na tym aby intencje zmiennej przekazywać nie przez jej nazwę ale przez jejże typ.

I teraz zerknijmy na dwie deklaracje metodki, która tworzy mi taki mały parserek do zapisywania pliku z formularza na dysku :


//po staremu
  def saveMultipartFile(multipartFileName:String,pathOnDisc:String)(data:MultipartFormData[TemporaryFile])=
//po nowemu
  def saveMultipartFile(m:MultipartFileName,p:PathOnDisc)(data:MultipartFormData[TemporaryFile])=
No i jaki widzę problem z pierwszą wersją? Ano taki jak z dokumentacją w wordzie - pisać można tam sobie wszystko co się komu podoba. I tka naprawdę to jaka jest pewność, że pierwszy parametr to faktycznie nazwa pliku a nie nazwisko panieńskie babci? W trakcie refaktoringu kod się zmieni a nazwa zostanie taka sama i dupa.

W drugim przypadku też 100% pewności nie ma ale raz, że w trakcie refaktoringu typy - czyli zwyczajne nazwy klas - zmienia IDE a nie JAnek programista z palca - a dwa, że jako String to mogę sobie przekazać co chcę i się zesra w Runtime. No i jest jeszcze trzecia strona medalu. Tak się wywołuje tę metodkę w kodzie :

val savingFileParserFunction=saveMultipartFile(MultipartFileName("file"), PathOnDisc("/tmp/testy"))_
I tutaj od razu w kodzie widać co jest co bez potrzeby wyświetlania Popapów z docami. Dla porównania w wywołaniu klasycznym :
val savingFileParserFunction=saveMultipartFile("file", "/tmp/testy")_
Tutaj dla odmiany raczej trzeba będzie zajrzeć do doca.

Na Code Retreat jest takie ćwiczonko gdzie należy każdą koncepcję w systemie przedstawić przy pomocy tzw. "klasy domenowej" czyli np. współrzędna jest typu "Wspolrzedna" a nie typu "Integer". No i w Javie pisanie tych wszystkich klas jest niby ok ale czułem się wewnętrznie, że w życiu raczej tego nie zastosuje bo wymaga to zbyt dużo prądu. W Scali sobie gdzieś w module trzasnę :

case class MultipartFileName(name:String) extends AnyVal
case class PathOnDisc(path:String) extends AnyVal
I już.

I jeszcze jedna ciekawa rzecz

Ten mały fragmencik :

extends AnyVal
Podobno sprawia, że kompilator w trakcie kompilacji "rozpakuje" typ i stawia w jego miejsce String wywalając wrappera. Czyli nie ma narzutu na pamięć kosztem tam chwilę dłuższej kompilacji. Chyba tak to działa.

I najważniejsze, żeby nie stawiać tej koncepcji w opozycji do tego co się wie i w co się wierzy ale spróbować chwilę się nią pobawić. Być może będzie to nowy bodziec do rozwoju i usprawnienia naszej pracy? A może po chwili się go wyrzuci i wróci do dotychczasowych metod? Najważniejsze to traktować to jako ćwiczenie a nie spór o to co lepsze. Po to też między innymi kasuje się kod w Code Retreat - aby pobawić się być może niewygodnymi pomysłami ze świadomością, że i tak to pójdzie do kibla i zostaną jedynie przeżycia w mojej czy twojej głowie.

Coś z zupełnie innej beczki

Bluzganie redukuje ból oraz zmniejsza wewnętrzne napięcie :

A co najważniejsze bluzganie buduje team spirit! Wiarygodności tych badań sprawdzać nie muszę gdyż weryfikuję je od lat trzydziestu. Ewentualnie jak ktoś był chowany pod kloszem i życia doświadczył tylko z odcinków Drużyny A to może zerknąć na : http://filing.pl/ilustrowany-podrecznik-dla-mlodziezy-dobrze-wychowanej/

niedziela, 1 czerwca 2014

Play Formularze - opis ćwiczeń

Poniżej opis drugiego modułu (słowo "moduł" brzmi tak zajebiście) ćwiczeń z Play Framework. Pierwszy moduł pojawił się w dwóch częściach :

Poniższy materiał to krótki zbiór ćwiczeń opracowany na podstawie tutoriali i książek - przystosowany do nauki Playa w trakcie 2-3 godzinnego spotkania z przerwą na pizze czy na jakieś zdrowsze jedzenie. Program nie jest jeszcze przetestowany w życiu ale powinien płynnie pokazać uczestnikom epickość formularzy w Play gdzie testować można po prostu wszystko. Także miłej zabawy z lektury i ćwiczeń.

Gdy to tak sobie piszę jest jeszcze jedno wolne miejsca na spotkanie także jak ktoś ma ochotę niech się śmiało dopisuje -> Link do spotkania na meetupie

Co przed startem

Aby nie zamulać już na samym początku dobrze by było gdyby każdy miał już rozpakowany Play przez zipa czy activatora. Dodatkowo fajnie przypomnieć sobie podstawy w tym celu każdy musi :

  • Zrobić nową aplikację
  • Import do Eclipse czy co kto tam używa
  • Stworzyć nowy widok jako ćwiczenie - czyli kontroler,mapowanie i szablon
  • Zostawić stronę index bo tam się wyświetla link do dokumentacji
  • Jak ktoś używa eclipse to poustawiać sobie zgodnie z http://scala-ide.org/docs/tutorials/play/

Wio

Dobrze mieć pod ręką otwartą lokalną dokumentację by powklejać importy i takie tam :

import play.api.data.Forms._
import play.api.data._

Tutaj zastanawiałem się czy większą wartość edukacyjną będzie miał jakiś zabawny przykład czy też coś generycznego jak User. Trzaśniemy coś z jedzeniem to może ludzie nauczą się patrzeć na to co jedzą (no i będzie bardziej różnorodnie bo nie zerżniemy przykładu prosto z dokumentacji).

case class Meal(name: String, calories: Int)

val mealForm=Form(mealMapping)
  
lazy val mealMapping=mapping(
    "name"->text,
    "calories"->number
)(Meal.apply)(Meal.unapply)

Deklaracja mappingu na zewnątrz wydaje mi się czytelniejsza a lazy jest potrzebne bo bez tego czasami się wywala gdzieś tam wewnątrz inicjalizacja Forma.

Tak czy inaczej mając tylko deklaracje mappingu i forma można teraz pokazać na testach, iż testować można wszystko.

    "bind data to meal mapping" in {
      val data=Map("name"->"hamburger","calories"->"5000")
      val mealData=Application.mealMapping.bind(data)
      mealData.isRight must beTrue
    }
    
    "bind data to meal mapping with errors" in {
      val data=Map("name"->"hamburger","calories"->"5000b")
      val mealData=Application.mealMapping.bind(data)
      mealData.isRight must beFalse
    }
    
    "bind data to meal form with error" in {
      val data=Map("name"->"hamburger","calories"->"5000b")
      val mealData=Application.mealForm.bind(data)
      mealData.errors must have(_.key=="calories")
    }

I ogólnie fajne i czytelne asercje są. Tutaj można zrobić szybko skok do Scali i wytłumaczyć co to je to Left i Right - będzie to też dobry wstęp do walidacji formularzy.

Either

Ten temat wywołuje u niektórych dużo zabawy za pierwszym razem bo w Javie informacja o tym, że coś może się nie udać zazwyczaj nie jest zawarta w typie. I jak mamy np. coś takiego:

def divide(a:Int,b:Int)=a/b       //> divide: (a: Int, b: Int)Int
divide(4,2)                       //> res0: Int = 2
divide(4,0)                       //> java.lang.ArithmeticException: / by zero

To przez zero dzielić nie można. I można zrobić tak :

def divide(a:Int,b:Int)={
 if(b==0) throw new IllegalArgumentException("cos tam")
 a/b
}

Ale można też :

def divide(a:Int,b:Int)=
       if(b==0) None else Some(a/b)

divide(4,2).map(_+1)              //> res0: Option[Int] = Some(3)
divide(4,0).map(_+1)              //> res1: Option[Int] = None

I tutaj ludzie, którzy robią w Javie od kilku lat maja jakaś dziwną radochę, ze typ nazywa się "Cośtam" albo "Nico". Pewnie będą jakieś pytanie "a po ch.." to w ogóle jest. Zycie pokaże co z tego wyjdzie. Tak czy inaczej mając Option chyba łatwiej będzie wytłumaczyć Either.

def divide(a:Int,b:Int)=
   if(b==0) Left(new IllegalArgumentException("cos tam"))
   else Right(a/b)           //> divide: (a: Int, b: Int)Product with Serializable with scala.util.Either[Ill
                                                  //| egalArgumentException,Int]
  divide(4,2).isRight               //> res0: Boolean = true
  divide(4,0).isRight               //> res1: Boolean = false

To będzie przykład w kontekście testów, które widzieliśmy chwilę wcześniej. A w kontekście obsługi forma, której jeszcze nie było - poniższe trzy linijki

val result=divide(4,2)
val funkcjaBiznesowa=(x:Int)=>x+1
result.fold(throw _, funkcjaBiznesowa)          //> res2: Int = 3

No i tutaj chyba każdy powinien zajażyć, że funkcjaBiznesowa nie jest sponiewierana przez wyjątki co ułatwia testowanie - z drugiej strony pewnie znowu ludzie będą śmiać się z podkreślinika throw _

No to robimy formularz

@(mealForm:Form[Meal])
<html>
<head>
<title>Formularz</title>
</head>
<body>

@import helper._

@form(action=controllers.routes.Application.submitForma){
@inputText(mealForm("name"),'id->"name")
@inputText(mealForm("calories"))
<input type="submit">
}

</body>
</html>

I zmiana w routach oraz kontrolerze :

//routes
POST   /submitForma    controllers.Application.submit()

//Application
def form = Action {
    Ok(views.html.formularz(mealForm))
  }
  
  def submitForma=TODO

Czas na obsługę POSTA - tutaj wąłśnie przydaje się wiedza z ćwiczenia "Either".

def submitForma = Action { implicit request =>
    mealForm.bindFromRequest.fold(
      badForm => BadRequest(views.html.formularz(badForm)),
      _ => Ok("mealOK")
    )

  }

Na razie bez styli wygląda to koślawo ale na prostszych przykładach łatwiej się uczyć.

Dokladniejsza walidacja

Na razie walidacja jest uboga dlatego dodamy sobie kilka parametrów aby ograniczyć to co można wprowadzić i do tego wrzucimy trzecie pole opcjonalne aby pokazać jak ładnie wszystko nam się skomponuje. Warto dodać, że wszystkie validacje są w obiekcie Forms

lazy val mealMapping = mapping(
    "name" -> nonEmptyText(3, 10),
    "calories" -> number(1000, 8000),
    "description" -> optional(text)
  )(Meal.apply)(Meal.unapply)


case class Meal(name: String, calories: Int,description:Option[String])

I w ten czas jest dobry moment aby uaktualnić testy o sprawdzenie obsługi pełnego żądania http.

//routes
POST   /submitForma    controllers.Application.submitForma()

//testy
"submit user form successfully" in new WithApplication{
     val formRequest=FakeRequest(POST,"/submitForma").withFormUrlEncodedBody("name"->"hamburger","calories"->"5000")
     val result=route(formRequest).get
     status(result) must equalTo(OK)
    }
    
    "return bad request when form data is missing" in new WithApplication{
      val formRequest=FakeRequest(POST,"/submitForma").withFormUrlEncodedBody("name"->"hamburger")
      val result=route(formRequest).get
      status(result) must equalTo(BAD_REQUEST)
    }

No i technicznie nawet nie musieliśmy odpalać formularza aby wiedzieć, że wsio działa :D. Teraz czas na jeszcze jeden patent - globalna walidacja!

//Application.scala
lazy val mealMapping = mapping(
    "name" -> nonEmptyText(3, 10),
    "calories" -> number(1000, 8000),
    "description" -> optional(text)
  )(Meal.apply)(Meal.unapply) verifying("zle dane tak ogolnie", mealData=>mealData.calories>2000 && mealData.name!="Warzywo")


//test
 "bind data to user form with global error" in {
     val data=Map("name"->"Warzywo","calories"->"5000")
     val mealData=Application.mealForm.bind(data)
     mealData.errors must have(_.message=="zle dane tak ogolnie")
    }

//html
@if(mealForm.hasGlobalErrors) {
<ul>
@for(error<-mealForm.globalErrors){
<li>@error.message</li>
}
</ul>
}

I można też verifying pojedyncze pola -

"name"->text(3, 10).verifying("can not be Warzywo", _!="Warzywo")

No i pewnie padnie pytanie jak te napisy pod polami pozmieniać. To można na szybko tak :

@inputText(mealForm("calories"),'_showConstraints->false,'_help->"moj koment")

Ale zapewne to nie wystarczy i czas przejść do definiowania własnych komunikatów.

Custom messages

I teraz jest taka opcja - odpowiednie komentarze muszą pojawić się w pliku conf/messages, którego jeszcze nie ma. Wszystkie możliwe komunikaty zaś znajdziemy tam gdzie rozpakowaliśmy archiwum playa - $PLAY_HOME/framework/src/play/src/main/resources/messages

Co by ni szukać poniżej pełna lista - za długa to ona nie jest

# Default messages

# --- Constraints
constraint.required=Required
constraint.min=Minimum value: {0}
constraint.max=Maximum value: {0}
constraint.minLength=Minimum length: {0}
constraint.maxLength=Maximum length: {0}
constraint.email=Email

# --- Formats
format.date=Date (''{0}'')
format.numeric=Numeric
format.real=Real

# --- Errors
error.invalid=Invalid value
error.invalid.java.util.Date=Invalid date value
error.required=This field is required
error.number=Numeric value expected
error.real=Real number value expected
error.real.precision=Real number value with no more than {0} digit(s) including {1} decimal(s) expected
error.min=Must be greater or equal to {0}
error.min.strict=Must be strictly greater than {0}
error.max=Must be less or equal to {0}
error.max.strict=Must be strictly less than {0}
error.minLength=Minimum length is {0}
error.maxLength=Maximum length is {0}
error.email=Valid email required
error.pattern=Must satisfy {0}

error.expected.date=Date value expected
error.expected.date.isoformat=Iso date value expected
error.expected.jodadate.format=Joda date value expected
error.expected.jsarray=Array value expected
error.expected.jsboolean=Boolean value expected
error.expected.jsnumber=Number value expected
error.expected.jsobject=Object value expected
error.expected.jsstring=String value expected
error.expected.jsnumberorjsstring=String or number expected
error.expected.keypathnode=Node value expected

error.path.empty=Empty path
error.path.missing=Missing path
error.path.result.multiple=Multiple results for the given path

Walnijmy sobie takie zestawik jak poniżej i zerknijmy co wyjdzie.

#forms
constraint.required=wymagane
format.numeric=musi być numerem

custom template

Czas by bliżej przyjrzeć temu co jest generowane przez te funkcje "@inputText" - ...i to zrobimy na zajęciach a nie tutaj. W tym miejscu po prostu poznamy patent na zmianę domyślnej struktury html jeśli nam nie pasuje.

// tworzymy template playowy z htmlem
@* myFormField Template File *@
@(elements: views.html.helper.FieldElements)

<div class="formGroupMoje">
<label for="@elements.id"></label>
<div class="formInputMoje">
@elements.input
</div>
</div> //formatter pola z wykorzystaniem szablonu object MyFormConstructor { import views.html.helper.FieldConstructor implicit val myFields = FieldConstructor(myFormField.f) } // jest implicit to sam import w stronie forma wystarczy @import helper._ @import views.MyFormConstructor._

I od razu wygląd forma się zmienił. Czy na lepsze? Każda zmiana jest na lepsze poza zmianami na gorsze.

upload pliku

To ćwiczenie jest w całości zerżnięte z książki "Play for Scala developers". Wygodnie jest zadeklarować najpierw
//Application
def fileUpload=TODO
//routes
POST   /uploadFile     controllers.Application.fileUpload()
bo wtedy kompilator nie pluje się przy używaniu backward routing.
<form action="@routes.Application.fileUpload" method="post" enctype="multipart/form-data">
<input type="file" name="image">
<input type="submit">
</form>
I tutaj ważna i ciekawa rzecz - po raz pierwszy uczestnicy kursu spotykają się z parserami requestów - co będzie tematyką trzecich albo czwartych zajęć.
  def pokazPlikForm = Action {
    Ok(html.fileForm())
  }

  def fileUpload = Action(parse.multipartFormData) { request =>
    request.body.file("image").map { file =>
      file.ref.moveTo(new File("/tmp/image"),true)
      Ok(s"retrieved file ${file.filename}")
    }.getOrElse(BadRequest("file mising."))
  }

No i to tyle

No i to tyle

poniedziałek, 26 maja 2014

Droga do szczęścia?

Droga do szczęścia zaczyna się niespodziewanie od czwartej edycji programowania funkcyjnego w Scali na Courserze a właściwie od tego co wyświetliło się na samym dole strony w sugerowanych kursach.

Nie wiem jakiego algorytmu oni używają do znajdywania polecanych pozycji ale w wyniku błyskotliwego wyszukiwania lub zwykłego random.next - na dole pojawił się kurs Buddhism and Modern Psychology czyli skrzyżowanie neurologii i najnowszych odkryć psychologii z kilkutysięczną filozofią.

Jeśli kogoś odpycha słowo buddyzm to niech je zwyczajnie oleje bo to był tylko taki abstrakcyjny dodatek do ścisłych faktów psychologicznych. Kurs będzie zamknięty w piątek, także jak ktoś chce obejrzeć wykłady to musi się pośpieszyć (polecam bardzo).

Na początek mały starterek.

Usiadł sobie koleś pod drzewem...

...kilka tysięcy lat temu - medytował i medytował - i wyprodukował Cztery szlachetne prawdy. Jeśli ktoś chce może sobie poczytać w linku rys filozoficzny - ale nas interesuje bardziej kontekst psychologiczny - a raczej psychologiczno-filozoficzny z naciskiem na część psychologiczną.

Pierwsza kwestia to koleś, który prowadzi wykłady ( Robert Wright - napisał fajne książki z pogranicza psychologii ewolucyjnej - "Moral Animal" i "The evolution of GOD") tłumaczy pewne nieporozumienie związane ze słowem "cirepienie", gdyż nie chodzi tu o cierpienie a o brak satysfakcji co jest chyba codziennością większości lemingów na świecie. I teraz uwaga - podobna koncepcja pojawia się w takich książkach jak "Power of habit" czy "Neuromarketing" co jest popularną częścią biblioteczki "skutecznego reklamodawcy". O co tu dokładnie chodzi?

I teraz paczta na ten obrazek bo mechanizm jest zabójczy (w swej prostocie i w ogóle). Weźmy bodziec R, który może być czymkolwiek - browar, wypłata, pochwała, nowe buty, jedzenie, sex itd. Gdy tworzy się nawyk najpierw możemy zaobserwować korelację ze skokiem dopaminy zaraz po otrzymaniu bodźca - np. małpka naciska dźwignie i dostaje banana - powstaje dosyć pożyteczny nawyk bo banan jest bogaty w potas i węgle proste.

I teraz pojawia się fascynująca rzecz - gdy nawyk się stabilizuje skok dopaminy przesuwa się przed "bodziec" - ma to sens gdyż cały mechanizm niejako zachęca małpkę do naciśnięcia dźwigni. A na ludzkie - to właśnie staje się istotą poszukiwań - nie bodziec a samo dążenie do bodźca, właśnie oglądamy neurologiczne podstawy ciągłej gonitwy po więcej oraz wytłumaczenie nieustającego braku satysfakcji. Dodatkowo jest to wzmocnione faktem, że z biegiem czasu wyskok dopaminy po samym bodźcu staje się coraz mniejszy - pozostawiając nas w stanie nienasycenia.

Mam nadzieję, że jest to wystarczająco jasne bo tutaj jest to tylko kilkoma zdaniami a na wykładzie było omawiane przez ponad dwie godziny - tak czy inaczej z ewolucyjnego punktu widzenia zagarnianie tego co się tylko da i ciągłe zaspokajanie potrzeb okazało się taktyką skuteczniejszą niż puszczanie bąków - dlatego mamy takie coś teraz w sobie (a bąki na zewnątrz). W trakcie kursu padło ciekawe sformułowanie - "hedonic threadmill".

Ale spoko - trzecia i czwarta z tych "szlachetnych prawd" mówi o rozwiązaniu - a neurologia wyjaśnia o co tu dokładnie chodzi. Lekarstwo pozostawimy jednak na koniec bo najpierw prawdziwe mięsko kursu.

Aby uzyskać fajny efekt kinowego przejścia na blogu tekstowym to teraz wyobraź sobie takie subtelne i delikatne ściemnienie się obrazu - jednocześnie dźwięk lekko zanika udostępniając przestrzeń nowym doznaniom - przed twymi oczyma pojawia się poniższy fragment z youtube (jak net będzie zamulał to trzeba sobie wyobrazić odpowiednio dłuższe przejście).

Kim nie jesteś

Taka ciekawa rzecz - musiałem to kilka razy przewijać aby załapać - generalnie ogromną tamą dla szczęścia w życiu jest pogoń każdego z nas za atrybutami, które błędnie kojarzymy ze sobą. Powoli.

mental formations

Czyli nasze przekonania. Weźmy sobie takie forum dyskusyjne - jak sama nazwa wskazuje forum dyskusyjne służy do napierdalania się do nieprzytomności o to kto ma rację. O proszę bardzo http://www.goldenline.pl/pawel-wlodarski2/wypowiedzi/ - 770 wypowiedzi z czego duża część jest bezwartościowym pierdoleniem o wyższości jednej rzeczy nad drugą. Masa irracjonalnych nerwów i straconego czasu.

Gdy zyskujesz doświadczenie zmieniasz swoje poglądy - Nie jesteś tym co uważasz

perception

Siedzisz sobie, walisz wódę i percepcja się zmienia. I tutaj fajne badanko - w zależności od tego jakiej muzyki słuchali badacze - strasznej lub przyjemnej - widzieli na obrazku aligatora lub wiewiórkę - Aligator czy wiewiórka - Nie jesteś tym czego doświadczasz

feeling

Do tego tematu powrócimy za chwilę bo uczucia to raczej sygnały, które oddziałują na nas a nie część nas. - Nie jesteś tym co odczuwasz

form (body)

To już w ogóle temat abstrakcja - jak już ktoś się zbierze i skoczy na siłkę, dobry trening siłowy zamiast stepera i maszyn - po kilku miesiącach "inny człowiek".

świadomość

To już w ogołe jest mindfuck - czas na modułową teorię swiadomości

Świadomość i moduły

Temat jest tak nieintuicyjny, że lekko go tu musnę tak czubeczkiem po powierzchni - generalnie polecam amazon i wpisać "Michael S. Gazzaniga" pojawią się takie książeczki jak Who's in Charge?: Free Will and the Science of the Brain.

Polecam też kliknąć w te linki :

  • Modułowa teoria umysłu
  • http://psychologydictionary.org/beneffectance/ - termin tłumaczący po co wyewoluowała samoświadomość. Nie jest ona prezesem a raczej działem marketingu - inaczej rzucenie palenia czy zmiana nawyków żywieniowych byłaby zwykłą sekundową decyzją. Nie chcę palić - no dobra to nie palę - nichuja to tak nie działa
  • http://biasandbelief.pbworks.com/w/page/6537179/Beneffectance - bardziej praktyczne opracowanie.
  • Samooszukiwanie - a to takie życiowe - jak mi się nie udało to miałem pecha. Jak mi się udało to jestem zajebisty. Jak komuś kogo nie lubię się udało to miał oczywiście farta ("Fartuch pierdolony"). A jak mu się nie udało to było do przewidzenia...
  • No i generalnie różne uczucia włączają różne moduły a później nasza świadomość to wszystko racjonalizuje aby miało sens w kontekście skomplikowanej sieci społecznych zależności. Weźmy te kłótnie na forach. Tutaj działa między innymi modułu odpowiedzialny za obronę statusu społecznego i później umysł świadomy racjonalizuje, że ja się nie napierdalam tak dla zasady ale naprawdę ten PHP jest zjebany (no bo jest ale chyba wiadomo o co chodzi). W końcu! Neurologiczne wytłumaczenie świętych wojen!

    Cytat : "One last bit of evidencein support of the argumentthat the self is essentially a tool of impression management and the product of a specialize dprocessor. Severely brain damaged individuals, eventhose subjected to commissurotomies, maintain a steady stream of rationalization and denial of their handicap. They appear to be striving to convey an impression of being reasonable and sensible (Gazzaniga 1985). The algorithms of the selfs specialized processor appear to demand this." by jerome barkow - http://www.omega23.com/Journal_of_Ideas/Barkow-Folk_Pyscholog_Free_Will_Evolution-JoI_V2_2_3-1991.pdf

    No dobra na razie jest tak trochę mrocznie - ale jest lekarstwo

    Mindfulness meditation

    Specjalnie zostawiłem angielski tytuł aby uniknąć "szklanej pułapki" tłumacza. Generalnie pierwsze pojęcie-klucz to Default mode network. Jest to to coś co działa w nas kiedy nie myślimy o czymś konkretnym.

    I tutaj jest ciekawy link : Wpływ medytacji na default mode network. Generalnie medytacja wycisza to DMN co ma dosyć ciekawe konsekwencje gdyż pozwala spojrzeć nam na emocje - czyli trigery zachowań - z dystansu. I teraz bardzo ważny rodzaj medytacji - wspomniana w tytule "mindfulness meditation"

    Idea jest prosta - odbierać uczucia,odczucia, obserwacje bez oceniania ich. Co ciekawe ta koncepcja atakuje z różnych stron w różnych publikacjach - w NLP to była praca z "presupozycjami", było też o tym w wizualizacjach metody Silvy jeśli dobrze pamiętam. Generalnie pomaga skupienie się na scence gdzie oglądamy w trzeciej osobie siebie odbierającego dane wrażenia co tworzy formę interesującego dystansu od uczuć. Przywraca nam ograniczoną kontrolę nad tym co robimy i pomaga programować siebie na przyszłość.

    Zmieniamy postrzeganie świata, zmieniają się uczucie, zmienia się nasze zachowanie ... i postrzeganie świata. I o to tutaj kurwa chodzi. A no i przy okazji zrozumiałem dlaczego tak mi się podoba rzucanie kurwami w tekście. Generalnie to takie podświadoma próba uwolnienia się od mentalnych łańcuchów społecznych. Tak naprawdę awersja do słów kurwa,chuj itd to takie lekko snobistyczne podejście podobnie jak jedzenie śledzia pięcioma widelcami - niby udajemy, że jesteśmy szlachta - ale wracając do tematu.

    Gdy zerwie się łańcuchy ewolucji i naturalnej selekcji wtedy czeka nas.. dobra ostatniej częsci jeszcze nie skumałem ale pojawia się słowo "enlightenment" (I nie chodzi o javascript enligtment) oraz pojęcie "emptiness" co wbrew pozornym negatywnym zabarwieniu ma znaczenie pozytywne gdyż pozwala nam dostrzec rzeczy takimi jakie są. To jest ciekawe arcy arcy ciekawe. Może zrozumiem przy okazji drugiej edycji.

    I jeszcze jeden ciekawy cytat dlaczego świat jest generalnie taki zjebany - niestety nie zapisałem autora a i słowa są z pamięci : "This planet is settled by beings which each one has build in system to convince them that they are more important than others"

    Następny odcinek będzie pewnie coś o Scali aby czytelnicy nie pouciekali - chociaż z drugiej strony jebać to - nie jestem swoim blogiem

    niedziela, 18 maja 2014

    Jeden slajd z Geecon 2014

    DRY - tutaj są dobre recenzje :

    Zwykły acz niezwykły slajd

    Powyższy slajd zaprezentował Sam Newman podeczas swojej prezentacji o Mikroserwisach.

    To jest arcyciekawy slajd gdyż jedzie walcem po pseudo-perfekcjonistycznej nasyconej strachem przed porażką kulturze wielu korporacji. Tutaj jest wyraźnie narysowane, że będziemy testować po deployu i trzeba się szykować na ewentualną "cofkę". Na slajdzie użyte jest słowo remediation zamiast rollback ale chyba chodzi mniej więcej o coś podobnego.

    Problem polega na tym, że jak coś wrzucimy na produkcję a później się cofamy to metryki stoją w miejscu. W tematyce samorozwoju istnieje takie pojęcie jak learning experience co też trochę trąci bullshitem ale jednocześnie ma za zadanie pokazać, że pewne zdarzenia owocują jedynie niematerialnym zastrzykiem wiedzy, który generuje zmiany w połączeniach neuronowych - niestety nichuja z tego nie da się wyrysować fajnego wykresu do powerpointa na spotkanie.

    Jakby np. wyglądała nauka jazdy na rowerze według korpo procesu? Najpierw potrzebny jest jakiś ambitny target metodą smart więc jebnijmy sobie "przejechać 100m", co zostanie zmienione na "100km" przez innego managera posługującego się formułkami "push harder" i "reach higher". Teraz koleś wsiada na rower i gleba. Jego mózg przetwarza nowe informacje jak tu się utrzymać na dwóch kółkach ale zanim będzie miał okazję wsiąść na rower musimy odbyć meeting dlaczego się nie udało, "This situation makes me very concern" i inne takie "postmortem analysis".

    Dobra, koleś siada drugi raz - 5 metrów i znowu gleba. Trzeba zrobić meeting z "root cause anlysis" dlaczego wciąż ponosimy porażki - a przecież ponosimy porażki bo według flow chartu rower stoi w miejscu a przecież na kursie z zarządzania pan Zdzichu wyraźnie mówił, że jak "stoimy w miejscu to się cofamy".

    I tutaj nagle na dużej konferencji pojawia się slajd, który mówi, że to w sumie dobra opcja wyskoczyć do świata rzeczywistego, zebrać rzeczywiste info z tego świata rzeczywistego i ewentualnie się wycofać - "Reality driven development".

    I nawet ci ludzie od robotów odnieśli spektakularny sukces bo zdobyli niesamowicie cenną wiedzę "co może pójść źle w czasie prezentacji" - i teraz jak wyskoczą przed potencjalnych inwestorów maja szansę zabłysnąć.

    Apropo robotów to nawet one były najebane.

    Do rozwiązania ponadto pozostaje standardowy problem wszystkich konferencji IT czyli blokowanie się wątków przy wejściu do męskiego kibla.

    ***

    niedziela, 11 maja 2014

    PlayFramework - drugie spotkanie i scenariusz nauki

    Gdy coś się dzieje po raz drugi to już trudniej nazwać to coś tymczasową anomalią. I w ten sposób - ku mej uciesze - warsztaty z Playa i Scali wydają się mieć dobrze i będą rozwijać się nadal.

    Uwagi i wnioski

    1. Tym razem na początku było trochę więcej chaosu związanego z konfiguracją środowisk toteż będę musiał pisać dokładną instrukcję tak krok po kroku jak się przygotować.
    2. Muszę pamiętać by ustawiać limity na meetupa bo tym razem przyfarciłem i zorientowałem się jak było akurat 15 osób zapisanych.
    3. Nie udało się zrobić tyle zadanek ze scali ile planowałem i do tego niektóre koncepcje nie są od razu jasne przy pierwszym zetknięciu. Być może będzie opcja na niezależne warsztaty ze scali?
    4. Chociaż programowanie w parach upraszcza prowadzenie warsztatów to jakoś ludzie wydają się być niechętni co do tego sposobu pracy na zajęciach.

    Co dalej

    W czerwcu jedno lub dwa spotkanka z dalszej części warsztatów. Czas na formularze, parsery i kompozycje akcji. Materiału jest trochę także może podzielę to jeszcze na dwa części.

    Play 2.3 RC1 jest już do pobrania także w lipcu może dla kolejnych chętnych będzie powtórka startu z playa - tym razem wersja 2.3 i scala 2.11.

    Scenariusz warsztatów

    To jest tak jakby kontynuacja Części pierwszej. Poniżej rozwinięta wersja punktu 3 z programu warsztatów --> Program warsztatów

    Start

    Tutaj standardowo tworzymy aplikację i w akcji index zmieniamy Your new application is ready. na pomidor albo dupa żeby było więcej zabawy

    HTTP

    Od razu trzeba pokazać, że dla Playa HTTP to przyjaciel a nie wróg. Robimy dodatkową akcję i prezentujemy jedyny chyba działający "quick fix" dla playa w eclipse - "ctrl+1" i automatyczne generowanie "ruta".

    //application.scala
    def moja = Action {
        Ok("siema")
    }
    //routes
    GET   /moja           controllers.Application.moja()
    

    I od razu działa bez restartu. Tutaj trzeba zrobić efekt WoW, o ku*wa itd. Wydaje mi się, że za pierwszym razem wyszło mi to bardziej epicko.

    Na razie akcja zwraca nam text/plain ale łatwo i przyjemnie można to zmienić.

    Ok("siema").as(HTML) //text/html;charset UTF-8
    Ok(siema) // application/xml
    import play.api.libs.json._
    Ok(Json.obj("klucz"->"siema")) // application/json
    

    Parametry

    Tutaj przede wszystkim dobrze pokazać jak jasno Play raportuje gdzie brakuje parametru i gdzie popełnione są błędy w mapowaniu. Przy każdej okazji staram się podkreślać, że kompilator stara się tutaj pomóc jak może. Albo się walczy z kompilatorem albo z Runtimem - to pierwsze wydaje mi się bezpieczniejsze i przyjemniejsze

    //Application.scala
    def moja(name:String) = Action {
       Ok(s"siema $name")
      }
    

    A jak już mapowanie będzie ok, to nadal raportowanie o złym użyciu api jest zajebiste. Aby uspokoić zmysły kolor czerwony zamienia się w kolor klocka.

    Tutaj też jest dobry moment aby uświadomić uczestników warsztatów o tym, że play wygenerował nam ładną lokalną dokumentację, w której można sobie sprawdzić opcje na deklaracje parametrów w query, w urlu, na parametry zdefiniowane i opcjonalne. Była krótka dyskusja o tym czy da się zrobić tak aby wartość parametru była deklarowana w akcji a nie w routach - chociaż wydaje mi się to bez sensu bo parametr to części API to jednak w wolnej chwili muszę to sprawdzić.

    Szablony

    Tutaj dobrze jest ujawnić opcję ~run oraz ciekawe ustawienie w eclipse Window->preferences->General->Workspace->Refresh using native hooks pooling . Ze zwyklym run po stworzeniu nowego widoku trzeba by najpierw odświeżyć stronę a później projekt - co jest zwyczajnie słabe.

    I teraz też czas na pokazanie szczodrości kompilatora - ku naszej uciesze nie da się do widoku przekazać Int kiedy oczekiwany jest String. Mniej zjebek typu "Object w jsp" - ano i podpowiadanie jest IDE jak zna typ.

    Więcej o szablonach w opisie poprzednich warsztatów -> Szablony

    Testy

    Wygenerowane testy powinny nam nie przechodzić na tym etapie gdyż domyślny nagłówek strony głównej został zastąpiony słowem pomidor lub dupa. Co akurat pokazuje, że owe testy działają.

    Tutaj też fajnie pokazać, jak łatwo testuje się widoki, które są zwykłymi funkcjami.

    @* widok Template File *@
    @(name: String)
    <html>
    <head>
    <title>widok</title>
    </head>
    <body>
    <h1>Siema @name </h1>
    </body>
    </html>
    

    I jak mamy ów widoczek to najwygodniej otworzyć worksheet i najzwyklej w świecie widoczek wywołać.

    object warszt {
     
     html.widok("Bożenka")                     //> res0: play.api.templates.HtmlFormat.Appendable = 
                                                      //| <html>
                                                      //| <head>
                                                      //| <title>widok</title>
                                                      //| </head>
                                                      //| <body>
                                                      //| <h1>Siema Bożenka </h1>
                                                      //| </body>
                                                      //| </html>
    }
    
    

    I w końcu można testować widok w testach jednostkowych. Nie chce mi się drugi raz opisywać tego samego -->Testy jednostkowe dla widoku

    Reverse routing

    To jest dosyć ciekawa funkcjonalność bo chyba nie ma czegoś podobnego w innym frameworku javowym. Żeby był efekt jebnięcia wystarczy chyba utworzyć obok siebie link zwykły i link używający reverse routing aby pokazać, że problemy z updatem linków w aplikacji nie będą nas już niepokoić

    //widok.scala.html
    <a href="/">link do indexu </a> <br/>
    <a href="@routes.Application.index">samoupdatujący się link do indexu </a> //i zmiana w routes GET /nowaStrategiaMarketingowa controllers.Application.index

    I to na razie byłoby na tyle

    ***

    niedziela, 27 kwietnia 2014

    Nauka PlayFramework - program edukacyjny część pierwsza

    Poniżej program edukacyjny dla pierwszej części warsztatów "PlayFramework" (moduł 1 - "start"). Program wymyśliłem sobie sam i dla mnie subiektywnie jest dobry bo ująłem w nim swoje przeżycia z nauki Playa,Scali i w ogóle podejścia funkcyjnego. Czy będzie dobry dla wszystkich to się okaże...

    Jak komuś się do czegoś ten program przyda to niech śmiało bierze i korzysta.

    Pierwsze zetknięcie ze Scalą i programowaniem funkcyjnym

    Program startuje z założeniem, że uczestnicy nie zetknęli się jakoś w dużym stopniu ze Scalą i programowaniem funkcyjnym. Niby Java8 powinna spopularyzować trochę to podejście ale nie zdziwię się jak masa osób będzie cały czas jechać w Javie4 czy Javie5 - taki urok miasta "low cost centrów".

    Ćwiczenia będą się rozwijać a to co jest na tę chwilę opisałem pod linkiem : Playframework moduł 1 "Start" - opis warsztatów

    Najpierw idzie godzinka scali dla zaznajomienia się z podejściem a później w drugiej części jedziemy z Playem.Poniżej bardziej szczegółowy opis pierwszej części.

    Deklaracja funkcji jako zmiennej

    Ten punkt ma za zadanie zaznajomić słuchaczy z sytuacja gdzie w zmiennej jest trzymana pewna logika, operacja a nie zaś same dane

    val funkcja=(arg:Int)=>arg+1         //> funkcja  : Int => Int = |function1|
    val dane=1                           //> dane  : Int = 1
     
    funkcja(1)                           //> res0: Int = 2
    funkcja(dane)                        //> res1: Int = 2
    
    
    Kompozycje funkcji

    Zadanie : zbudować funkcję z już istniejących z użyciem andThen oraz compose. Ta część ma pokazać siłę prostoty kompozycji funkcji. Wystarczy by zgadzały się określone typy funkcji aby można było je ze sobą składać. W OOP często atakuje nas specyficzny kontekst danego rozwiązania i trzeba uciekać do tworzenia abstrakcji, które mogą (ale nie muszą) zaciemniać rozwiązanie.

    val pomnozPrzezDwa = (arg: Int) => arg * 2 //> pomnozPrzezDwa  : Int => Int = |function1|
      funkcja.andThen(pomnozPrzezDwa) //> res2: Int => Int = |function1|
      //można też bez kropek
      val dodajJedenIPomnozPrzezDwa = funkcja andThen pomnozPrzezDwa
      //> dodajJedenIPomnozPrzezDwa  : Int => Int = |function1|
    
      dodajJedenIPomnozPrzezDwa(1)
      //> res3: Int = 4
    
    

    Metodę "compose" można wytłumaczyć na czymś bardziej z życia wziętym - na przykładzie klasy User (bo teraz chyba każdy system an ziemi ma klasę User). Może pojawić się pytanie co to jest to "case" - na tym etapie to jest raczej nieistotne i może wprowadzić niepotrzebny zamęt. Można powiedzieć, że to generuje z paczki hashcode, toStringa i kilka podobnych metodek.

    case class User(email:String)
    val validacjaEmaila=(email:String)=>email.nonEmpty
                                                      //> validacjaEmaila  : String => Boolean = |function1|
    val emailUsera=(user:User)=>user.email    //> emailUsera  : poligon.User => String = |function1|
     
    val validujeEmailUsera=validacjaEmaila.compose(emailUsera)
                                                      //> validujeEmailUsera  : poligon.User => Boolean = |function1|
    validujeEmailUsera(User("aa@wp.pl"))      //> res0: Boolean = true
    validujeEmailUsera(User(""))              //> res1: Boolean = false
    
    
    Funkcje wyższego rzędu

    Tutaj może pojawić się pierwszy kolaps czaszki gdyż standardowy programista Javy, który rzeźbi całe życie CRUDy raczej rzadko kiedy spotyka się z sytuacją przekazywania kawałka logiki jako argumentu do innego kawałka logiki. Niby to jest podobne do "Strategii" ale zazwyczaj w Springu czy czymś takim wszystko jest już wstrzyknięte. Co najwyżej można to porównać do przekazywania komparatora w Collections.

    Szybka demonstracją może być sumowanie wszystkich liczb nieparzystych od 1 do 100. Drugim przykładem - bardziej przydatnym - wypisanie wszystkich maili Userow, które są krótsze niż 5 znaków.

    val lista=1 to 100 toList                 //> lista  : List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
                                                      //|  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
                                                      //|  35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
                                                      //|  54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
                                                      //|  73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
                                                      //|  92, 93, 94, 95, 96, 97, 98, 99, 100)
    val czyNieparzysta=(arg:Int)=>arg%2==0    //> czyNieparzysta  : Int => Boolean = |function1|
     val sumuj=(akumulator:Int,element:Int)=>akumulator+element
                                                      //> sumuj  : (Int, Int) => Int = |function2|
     lista.filter(czyNieparzysta).reduce(sumuj)//> res0: Int = 2550
    
    Uproszczenie zapisu

    W końcu dochodzi moment, w którym trzeba uczestnikom pokazać zapis z podkreślnikami - bez względu na wygodę takiego zapisu osoby niezaznajomione z tąże notacją mogą wykazać postawę "o ku*wa!". No i tutaj jest okazja wytłumaczyć kiedy nie trzeba deklarować typów bo kompilator może to wykminić z kontekstu.

    //poniższe linie są równoważne
    lista.filter(arg=>arg%2==0).reduce((akumulator,element)=>akumulator+element)  //> res0: Int = 2550
    lista.filter(_%2==0).reduce(_+_)          //> res1: Int = 2550
    
    Currying

    To będzie potrzebne później przy templatach Playa. Tutaj pokazać tylko na szybko prosty przykład aby słuchacze zaznajomili się z faktem wykorzystywania kilku par nawiasów przy deklaracji funkcji. Według mnie prościej takie funkcje deklaruje się wychodząc od metody "def" ale tutaj dla porównania dwie wersje

    def metoda(a:Int)(b:Int)=a+b              //> metoda: (a: Int)(b: Int)Int
    val funkcja=(a:Int)=>(b:Int)=>a+b         //> funkcja  : Int => (Int => Int) = |function1|
     
    val funkcjaZMetody=metoda(1)_             //> funkcjaZMetody  : Int => Int = |function1|
    funkcjaZMetody(2)                         //> res0: Int = 3
     
    val funkcjaZFunkcji=funkcja(1)            //> funkcjaZFunkcji  : Int => Int = |function1|
    funkcjaZFunkcji(2)                        //> res1: Int = 3
    
    
    Serwer jako funkcja

    Teraz nastąpi połączenie dotychczasowych ćwiczeń z Playem.

    case class Request(url:String)
    case class Response(resultCode:Int)
     
    
    val handler:(Request=>Response)=(request)=>request match{
     case Request("/jug/warsztaty") => Response(200)
     case Request("")=>Response(500)
     case wszystkoInne=>Response(404)
    }                                         //> handler  : poligon.poligon.Request => poligon.poligon.Response = 
                                                      //| 
    
    handler(Request("/jug/warsztaty"))        //> res0: poligon.poligon.Response = Response(200)
    handler(Request(""))                      //> res1: poligon.poligon.Response = Response(500)
    handler(Request("/poradnikSwiatlegoManagera"))
                                                      //> res2: poligon.poligon.Response = Response(404)
    

    Mając powyższe możemy dojść do zapisu Action z Playa

    def Action(handler:(Request=>Response))={
     println("jakies operacje na serwerze przed handlerem")
     val result=handler(Request("/aaa"))
     println("jakies operacje na serwerze po handlerze : "+result)
    }                                         //> Action: (handler: poligon.poligon.Request => poligon.poligon.Response)Unit
     
     
    Action(handler)                           //> jakies operacje na serwerze przed handlerem
                                                      //| jakies operacje na serwerze po handlerze : Response(404)
     
    Action{handler}                           //> jakies operacje na serwerze przed handlerem
                                                      //| jakies operacje na serwerze po handlerze : Response(404)
     
    Action{request=>
     println("inline function")
     Response(200)
    }                                         //> jakies operacje na serwerze przed handlerem
                                                      //| inline function
                                                      //| jakies operacje na serwerze po handlerze : Response(200)
    
    Podsumowanie

    Powyższe ćwiczenia mają za zadanie wprowadzić uczestników w świat Scali i programowania funkcyjnego. Maja także za zadanie zsynchronizować wszystkich gdyż część ludzi kończy robotę tak, że może przyjść na 17 a część na 18.

    Ćwiczenia są przewidziane na godzinę, Jeśli zajmą więcej czasu to można zacząć przeplatać je Playem. Za tydzień lub dwa lub trzy lub więcej opiszę część druga.

    ***