niedziela, 22 lutego 2015

Te i przyszłe warsztaty ze scali

Na horyzoncie są warsztaty Play+Akka, sama Akka, więcej Scali, programowanie funkcyjne oraz do tego uwaga - BigData i moze jakieś Machine Learning. Ale najpierw Wspomnienia po ostatnich warsztatach.

Dodatek do warsztatów pierwszych

W opisie do zajęć : Opis do zajęć z wątków były wątki, kolejki, egzekutory i forkjoiny ale zapomniałem dodać najfajniejsze czyli Java8 i Scala :

Dzięki nowemu api w Javie 8 implementacja prostych przekształceń na wielu wątkach jest banalna :

public static void main(String[] args) {
  OptionalInt result = IntStream.range(0, 100)
  .parallel() //1
  .map(e -> e * 2)
  //.map(e ->{System.out.println("thread : "+Thread.currentThread().getName());return e;}) //2
  .reduce(Integer::sum);
  
  result.ifPresent(System.out::println);
 }
  1. Ta jedna linia wystarczy aby obliczenia odbywały się w wielu wątkach
  2. Na dowód można sobie odkomentować tężę linijkę i zobaczyć, że w konsoli pojawią się różne wątki z domyślnej puli
thread : ForkJoinPool.commonPool-worker-7
thread : ForkJoinPool.commonPool-worker-2
thread : ForkJoinPool.commonPool-worker-1
thread : ForkJoinPool.commonPool-worker-4
thread : ForkJoinPool.commonPool-worker-4
thread : ForkJoinPool.commonPool-worker-3
thread : ForkJoinPool.commonPool-worker-6
9900

I Scala dla porównania. Jest trochę mniej tekstu i przez to raczej czytelniej.

def main(args: Array[String]) {
    val result=(1 to 100)
    .par //1
    .map(_*2)
    //.map(e=>{println(s"thread : ${Thread.currentThread()}");e}) //2
    .sum
    
    println(result)
  }
thread : Thread[ForkJoinPool-1-worker-5,5,main]
thread : Thread[ForkJoinPool-1-worker-11,5,main]
thread : Thread[ForkJoinPool-1-worker-3,5,main]
thread : Thread[ForkJoinPool-1-worker-9,5,main]
thread : Thread[ForkJoinPool-1-worker-3,5,main]
thread : Thread[ForkJoinPool-1-worker-9,5,main]
thread : Thread[ForkJoinPool-1-worker-9,5,main]
thread : Thread[ForkJoinPool-1-worker-9,5,main]
9900

Dlaczego warto się uczyć - ekonomia

To co widzimy powyżej to charakterystyka diody półprzewodnikowej (chyba, bo już 15 lat nie lutowałem). Poza tym, że jest dioda jest podstawowym składnikiem przemysłu elektronicznego to również koncepcja napięcia przebicia pełni rolę doskonałej metafory tegoż przemysłu.

Generalnie chodzi o to, że społeczność czy ogólnie pojęci programiści muszę w danym ekosystemie wygenerować odpowiednie "napięcie edukacyjne" zanim nastąpi przebicie ciekawych projektów. Jeśli np. będą chętni na zlecenie do Łodzi projektów w Scala,Play czy Akka to kto to zrobi jak mało kto umie? Dlatego też systematyczna edukacja, chociaż na początku wydaje się nie przynosić żadnych rezultatów (tak do 0,6-0,7 Volta jeśli dobrze pamiętam) może być kurą, która rozwiąże dylemat tego co było pierwsze..

Dlaczego warto się uczyć - psychologia

W przypadku nauki rzeczy, która nie przynosi nam natychmiastowych profitów mamy do czynienia z tzw. efektem odroczonej gratyfikacji Psychologia Współczesna - odroczona gratyfikacja. Generalnie sposobem najłatwiejszym do polecenia i jednocześnie najtrudniejszym do wdrożenia to "się zmusić". Bardzo pomaga np. siłka jako trening powtarzalnego wysiłku.

Plus przyda się świadomość efektu, który powszechnie nazywa się słomianym zapałem. To ma miejsce wtedy kiedy np. przewidujemy, że jak tylko usiądziemy do nauki Scali to od razu stanie się coś magicznego albo wtedy gdy bo jednej serii z hantlem spodziewamy się przyrostu bicepsa do 45 cm. Po pierwszym szoku od rzeczywistości umysł rewaluuje bilans zysk i strat i stwierdza, że jednak lepiej usiąść przed monitorem/tv i żyć życiem postaci z seriali czy gier komputerowych. Ponownie trzeba znaleźć sposób by się jakoś zmusić do wysiłku.

Dlaczego warto się uczyć - socjologia

Niektórzy ludzie wydaja się być bardzo zmotywowani poprzez rzeczy, które mogą wrzucić na fejsa i zebrać lajki. Gdyby tylko udało się jakoś powiązać ten mechanizm z faktem nauki czegokolwiek... (oczywiście nie chodzi tutaj o mechanizmy używane przez fejsbukowe aplikacje próbujące sztucznie wygenerować autolans poprzez broadcast informacji, ze ktoś w jakiejś grze zdobył odznakę czy level - co jest kurwa zwyczajnie żenujące. )

Kolejne warsztaty

W planach było zrobienie drugiej części warsztatów z wielowątkowości jakkolwiek są ciekawsze tematy :

  • Play+Akka - marzec/kwiecień
  • Scala i Obiektówka jakiej jeszcze nie widzieliście - marzec/kwiecień
  • Programowanie funkcyjne - to będzie niedługo być/ nie być
  • I kolejna ciekawa rzecz Scala/Akka + DDD. Na manningu są trzy MEAPy an ten temat.To się teraz podobno nazywa DDDD z Distributed na początku. kolejne D to będzie Double Distributed. I tak najlepszą nazwę miał język do programowania radzieckich głowic atomowych CCCP++.
  • I ogromny dosłownie temat na koniec - BigData. Pojawiają się coraz to ciekawsze narzędzia a Spark ma tak wygodną konsolę, ze można poprowadzić warsztaty bez pracochłonnej konfiguracji.

I na koniec kilka ćwiczeń, które były przeznaczone na drugą część warsztatów z wielowątkowości. Na razie do samodzielnej pracy a w przyszłości może coś się z tego zorganizuje. W java.util.concurrent można znaleźć wiele ciekawych klas rodem ze startreka jak np.Phasery.

Konstrukcje

Podobno większość rzeczy z tych ćwiczeń trzeba było kiedyś przy pomocy wait/notify robić ale tego nie pamiętam - za młody jestem.

ThreadLocal

ThreadLocal zapisuje dane w kontekście konkretnego wątku i inne wątki się w te dane nie wcinają. Patrzymy na przykład.

object ThreadLocalExercise {

  val statistics=new ThreadLocal[List[Int]]{
    override def initialValue()=List()
  }
  
  def main(args: Array[String]) {
    val ts=Vector.tabulate(3)(i=>new Worker(s"worker ${i}"))
    ts.foreach(_.start())
    ts.foreach(_.join())
  }
  
  class Worker(n:String) extends Thread{
    override def run()={
      val r=new Random()
      gatherStatistics(r)
      displayStatistics
      cleanStatistics //1
    }

    def gatherStatistics(r: java.util.Random) = {
       (1 to 3).foreach{i=>
        val v=r.nextInt(10)
        val l=statistics.get()
        println(s"thread ${n} is adding value ${v}")
        statistics.set(v::l)
        }
    }
    
    def displayStatistics=println(s" worker ${n} has ${statistics.get}")
    def cleanStatistics=statistics.remove()
  }
}

  1. To dosyć ciekawy moment. Otóż okazuje się, że nie czyszcząc ThreadLocal można doprowadzić do wycieku pamięci na serwerze gdzie dany wątek wróci do puli i będzie za chwilę obsługiwał inny request
    • http://avasseur.blogspot.com/2003/11/threadlocal-and-memory-leaks.html
    • http://frankkieviet.blogspot.com/2006/10/classloader-leaks-dreaded-permgen-space.html
    • http://frankkieviet.blogspot.com/2006/10/how-to-fix-dreaded-permgen-space.html
Rozwiązaniem może być użycie WeakReference
val statistics=new ThreadLocal[WeakReference[List[Int]]]{
    override def initialValue()=new WeakReference(List())
  }

CountDownLatch

Pejzaż poniżej przedstawia koncepcję działania CountDownLatch. Wątki wywołujące await grzecznie czekają aż coś zwolni blokadę - a blokad jest tyle jaka cyferka na początku czyli w naszym przypadku dwie

object CountDownLatchExercise {

  def main(args: Array[String]) {
    val cl=new CountDownLatch(2)
    
    val c1=new Controller(5,cl)
    val c2=new Controller(6,cl)
    
    val rockets=(1 to 10).map(new Rocket(_,cl))
    rockets.foreach(_.start)
    
    c1.start()
    c2.start()
    
    rockets.foreach(_.join)
  }
  
  class Rocket(n:Int,cl:CountDownLatch) extends Thread{
    
    private val r=new Random()
    
    override def run()={
      TimeUnit.SECONDS.sleep(r.nextInt(4))
      println(s"przygotowania rakiety ${n}")
      cl.await()
      println(s"start rakiety ${n}")
    }
  }
  
  class Controller(sleep:Int,cl:CountDownLatch) extends Thread{
    override def run()={
      TimeUnit.SECONDS.sleep(sleep)
      println(s"kontroler zwolniony po ${sleep} sekundach")
      cl.countDown()
    }
  }
}
I wynik :
przygotowania rakiety 5
przygotowania rakiety 6
przygotowania rakiety 4
przygotowania rakiety 2
przygotowania rakiety 8
przygotowania rakiety 3
przygotowania rakiety 10
przygotowania rakiety 7
przygotowania rakiety 1
przygotowania rakiety 9
kontroler zwolniony po 5 sekundach
kontroler zwolniony po 6 sekundach
start rakiety 6
start rakiety 2
start rakiety 3
start rakiety 4
start rakiety 9
start rakiety 5
start rakiety 1
start rakiety 7
start rakiety 10
start rakiety 8

Bariera

Dzieło poniżej pokazuje, iż wątki czekają aż wszystkie odhaczą się w barierze (t1), potem ruszają dalej itd. Do tego po każdej kolejce barman dolewa wódy.

object CyclicBarrierExercise {

  def main(args: Array[String]) {
    val b=new CyclicBarrier(5,new Runnable{
      def run={
        println("barman dolewa wódy")
      }
    })
    
    
    val threads=Vector.tabulate(5)(i=>new Shot(b,i+1)).map(new Thread(_))
    threads.foreach(_.start())
    
  }
  
  class Shot(b:CyclicBarrier,i:Int) extends Runnable {
    def run(): Unit = {
      println(s"wątek ${i} pierwsze kolejka")
      b.await()
      println(s"wątek ${i} druga kolejka")
      b.await()
      println(s"wątek ${i} trzecia kolejka")
      b.await()
      println(s"wątek ${i} do dna")
    }
  }
}
wątek 2 pierwsze kolejka
wątek 1 pierwsze kolejka
wątek 3 pierwsze kolejka
wątek 5 pierwsze kolejka
wątek 4 pierwsze kolejka
barman dolewa wódy
wątek 3 druga kolejka
wątek 4 druga kolejka
wątek 5 druga kolejka
wątek 1 druga kolejka
wątek 2 druga kolejka
barman dolewa wódy
wątek 2 trzecia kolejka
wątek 1 trzecia kolejka
wątek 4 trzecia kolejka
wątek 5 trzecia kolejka
wątek 3 trzecia kolejka
barman dolewa wódy
wątek 3 do dna
wątek 1 do dna
wątek 5 do dna
wątek 4 do dna
wątek 2 do dna


poniedziałek, 26 stycznia 2015

Warsztaty o programowaniu wielowątkowym z Javy ale w Scali (chyba część pierwsza)

Poniżej materiał do warsztatów uczących używania mechanizmów wielowątkowych dostępnych w Javie aleee programować będziemy w Scali. Kiedy się odbędzie warsztat? Warsztat odbędzie się w momencie jego dobycia. Pewnie w przyszłym miesiącu.

Celem warsztaty będzie zapoznanie uczestników (którzy w założeniu nie stykają się z problemami wielowątkowości na co dzień) z ewolucją którą przeszła Java do wersji 1.7 - (java.util.concurrent, fork-join i takie tam.) Jednocześnie nie zarzucając słuchaczy zbyt wielką listą szczegółów.

Aby upiec dwie pieczenie na jednym ruszcie - będziemy przy okazji uczyć się Scali. To trochę jak nauka angielskiego poprzez tłumaczenie najnowszego wydania "Świata Dorsza" na tenże język.

Na warsztat przygotuję jakieś próbki kodu tak aby uczestnicy nie musieli pisać wszystkiego od początku. Kod będzie kompletny np. w 70% i nauka odbywać będzie się poprzez uzupełnienie brakujących fragmentów z towarzyszącym niezwykle krytycznym procesem wyciągania wniosków.

Po co uczyć się "starych" mechanizmów wielowątkowości jeśli ostatnio różne nowe rozwiązania działają na wyższym poziomie abstrakcji? No po to aby wiedzieć dlaczego one działają na wyższym poziomie abstrakcji i dlaczego w ogóle działają.

W jakieś wielkie i wyczerpujące opisy w tym artykule się nie bawiłem bo wszystko omówimy sobie w trakcie warsztatu.

Pojedyncze wątki na start

object SingleThreadExample {

  def main(args: Array[String]) {
    val r=new Thread(new MyRunnable("Interfejs"))
    val t=new MyThread("Klasa")
    
    r.start()
    t.start()
    
    r.join() //czekamy na zakończenie obydwu watków
    t.join()
  }
  
  class MyThread(m:String) extends Thread {
      override def run=println(s"Jestem w watku : ${m}")
  }  
  
  class MyRunnable(m:String) extends Runnable {
    def run = println(s"Jestem w runnable : ${m}")
  }
}
Nauka :
  • Runnable to interfejs ale w Scali nie ma słowa kluczowego implements.
  • Kiedy damy s przed stringiem to można wygodnie wstawiać weń zmienne
  • Na końcu dajemy join aby główny wątek main nie skończył się przed wątkami badanymi

Kilka wątków dla współpracy

W tym przykładzie tworzymy kilka wątków, które współpracują ze sobą sumując wybrane elementy z tablicy i następnie wyliczają ostateczną sumę. Takie MapReduce dla ubogich. Z przykładu można się także nauczyć ciekawego sposobu inicjalizacji kolekcji poprzez "tabulate" a także trochę deklaratywnych transformacji tychże kolekcji.

A i w ScalaIDE działa już generowanie metod w traitach przy pomocy ctrl+1.

object CoupleThreeadsCooperation {

  def main(args: Array[String]) {
    val n=100
    val v=Vector.tabulate(n)(i=>i*2) //1
    
    val runnables=(0 to n/10).map(i=>new Summarizer(i*10,v)) //2
    val threads=runnables.map(new Thread(_)) //2
    threads.foreach(_.start()) //3
    threads.foreach(_.join)  //3
    
    val sum=runnables.map(_.result).sum //4
    println(sum)
  }
  
  class Summarizer(startIndex:Int,v:Vector[Int]) extends Runnable {
    
    private var _result:Int=0 // 5
    
    def run(): Unit = {
      val dataForThread=v.slice(startIndex, startIndex+10)
      println(s"summarizing elements ${startIndex} : ${startIndex+10} - ${Thread.currentThread().getName}") //6
      _result=dataForThread.sum
    }
    
    def result=_result //5
  }
}
  1. Używamy wektora bo tam wydajniej dostać się do danego elementu z indexem "i" także "slice" też pewnie działa szybciej
  2. Zamiast dziwnymi pętlami przetwarzamy dane lamdami.
  3. Podobnie można zażądać wywołania konkretnej metody an wszystkich elementach
  4. Przy tej linii w ramach odpowiedniego czasu i chęci słuchaczy można wspomnieć jak to jest zrobione, że dla kolekcji intów mamy metodę sum
  5. Jest stan i jest mutable :(
  6. Na potrzeby ćwiczenia walimy tekst zwykłym println

Kilka wątków - rywalizacja

Tym razem zobaczymy dlaczego programowanie wielowątkowe może nie działać. Uruchamiamy 100 wątków, każdy zwiększa stan licznika o 1- powinno być 100 - powinno a nie jest.

object CoupleThreadsWithGlobalState {

  private var global=0
  
  def main(args: Array[String]) {
    val workers=Seq.fill(100)(new Worker()) //1
    workers.foreach(_.start)
    workers.foreach(_.join)
    
    println(s"global na końcu ${global}")
  }
  
  class Worker extends Thread{
    override def run=global=global+1
  }
}
  1. "Seq" - tutaj tak przy okazji skąd się wzięło Seq - zerknijcie tutaj - Scala - Hierarchia kolekcji

Locki

object CoupleThreadsWithGlobalStateGuardedByLocks {

  private var global=0
  private val monitor=new Object() //1 pokazać przykład ze złym monitorem
  
  def main(args: Array[String]) {
    val workers=Seq.fill(100)(new Worker())
    workers.foreach(_.start)
    workers.foreach(_.join)
    
    println(s"global na końcu ${global}")
  }
  
  class Worker extends Thread{
    override def run=monitor.synchronized{  //1
    	  global=global+1 
      }
  }
}
  1. W scali nie ma słowa kluczowego synchronized. Jest za to metoda na AnyRef, która robi to samo

Atomic Integer

AtomicInteger wykorzystuje specjalne instrukcje procesora, który w jednym cyklu może wykonać dwie operacje w sposób atomowy. Procesory robi się z krzemu, a krzem uzyskuje chyba z piasku. W Afryce i Azji działają gangi, które nielegalnie przemycają piasek - ale wracając do przykładu zastosowanie AtomicInteger bardzo uprościło kod

object CoupleThreadsWithGlobalStateGuardedByLocks {

  private val global=new AtomicInteger(0)
  
  def main(args: Array[String]) {
    val workers=Seq.fill(100)(new Worker())
    workers.foreach(_.start)
    workers.foreach(_.join)
    
    println(s"global na końcu ${global}")
  }
  
  class Worker extends Thread{
    override def run=global.incrementAndGet()
  }
}

Producent konsument - kolejka

Tutaj trochę dłuższy przykład. Tym razem do trzech współpracujących wątków dodajemy kolejkę, na której blokują się wątki producenta i konsumenta w zależności czy kolejka jest pusta czy pełna.

object ProducerConsumerQueue {

  def main(args: Array[String]) {
    val q: BlockingQueue[Int] = new LinkedBlockingQueue(3) //1
    val p1 = new Thread(new Producer(q))
    val p2 = new Thread(new Producer(q))
    val c = new Thread(new Consumer(q))

    p1.start()
    c.start()
    p2.start()
  }

  class Producer(q: BlockingQueue[Int]) extends Runnable {

    private val r = new Random()

    @tailrec  //3
    final def run(): Unit = {
      val v = r.nextInt(10)
      q.put(v)       //2
      println(s"producing ${v} :  queue size ${q.size}")
      TimeUnit.SECONDS.sleep(3)
      run() //3
    }
  }

  class Consumer(q: BlockingQueue[Int]) extends Runnable {
    def run(): Unit = {
      while (true) {
        val v = q.take() //2
        println(s"consumed ${v} :  queue size ${q.size}")
        TimeUnit.SECONDS.sleep(2)
      }
    }
  }
}
  1. Kolejkę ustawiamy na 3 elementy aby się "zapchała" w trakcie pracy
  2. put i take blokują wątek w oczekiwaniu na rezultat
  3. A to taki eksperyment - teoretycznie zastąpiliśmy popularną pętlę "while(true)" rekurencją - chyba działa

Executory

W poniższym ćwiczeniu nie będziemy już tworzyć wątków z palca lecz użyjemy dodanych do Javy5 "Executorów" (lub po polsku Egzekutorów lub poprawnie po angielsku Executors)

object ExecutorsExample {

  def main(args: Array[String]) {
    val e=Executors.newFixedThreadPool(3) //1
    
    (1 to 100).foreach{i=>
      println(s"run task ${i}") //2
      e.submit(new Task())
    }
    e.shutdown()
  }
  
  class Task extends Runnable {
    def run(): Unit = {
      println(s" running thread ${Thread.currentThread().getName}") //2
      TimeUnit.SECONDS.sleep(2)
    }
  }
}
  1. Inicjalizujemy pulę złożoną z trzech wątków
  2. I tutaj w logach zobaczymy, iż pomimo tego, że chcemy wykonania się 100 wątków to tylko trzy jadą w jednej chwili.
run task 93
run task 94
run task 95
run task 96
run task 97
run task 98
run task 99
run task 100
 running thread pool-1-thread-1
 running thread pool-1-thread-3
 running thread pool-1-thread-2
 running thread pool-1-thread-1
 running thread pool-1-thread-3
 running thread pool-1-thread-2

Callable

Tutaj zrobimy jeszcze raz to ćwiczenie, w którym watki liczą sumę elementów kolekcji. Drogi czytelniku zauważ, że tym razem nie ma potrzeby przechowywania żadnego stanu w wątkach ani nic takiego gdyż używamy Callable , które zwyczajnie zwraca rezultat.

object CalablesExample {

  def main(args: Array[String]) {
    val n=100
    val v=Vector.tabulate(n)(i=>i*2)
    val e=Executors.newFixedThreadPool(5)
    
    val callables=(0 to n/10).map(i=>new Summarizer(i*10,v))
    val futures=callables.map(e.submit(_))
    val result=futures.map(_.get).sum
    
    println(result)
    e.shutdown() //1
    e.awaitTermination(10, TimeUnit.SECONDS)
  }
  
  class Summarizer(startIndex:Int, v:Vector[Int]) extends Callable[Int] {
    
    def call(): Int = {
      val dataForThread=v.slice(startIndex, startIndex+10)
      println(s"summarizing elements ${startIndex} : ${startIndex+10} - ${Thread.currentThread().getName}") //6
      dataForThread.sum
    }
    
  }
}
  1. Pamiętajcie wyłączać po sobie światło.

Fork Join

I na koniec bonus z Javy7 - ForkJoin Framework - charakteryzuje się tym, że wątki sobie zabierają pracę jak nic nei robią.

object ForkJoinExample {

  def main(args: Array[String]) {
    val n = 100
    val v = Vector.tabulate(n)(i => i * 2) //1

    val t = new Task(v, 0, v.length)
    val p = new ForkJoinPool() //3
    p.execute(t) 

    p.shutdown()
    p.awaitTermination(10, TimeUnit.SECONDS)
    println(s"result ${t.get}")
  }

  import ForkJoinTask._ //4

  class Task(is: Seq[Int], start: Int, end: Int) extends RecursiveTask[Int] { //5
    def compute(): Int = {
      println(s"calculating ${start} - ${end}")
      if (end - start < 10)
        is.slice(start, end + 1).sum
      else {
        val mid = (start + end) / 2
        val t1 = new Task(is, start, mid)
        val t2 = new Task(is, mid + 1, end)
        invokeAll(t1, t2) //6
        t1.get() + t2.get() //7
      }
    }
  }
}
  1. Tutaj jakieś dane w kolekcji podobnie jak w poprzednich przykładach
  2. Tu był krzyż a teraz już go nie ma
  3. Nowy typ puli - FooooorkJoin
  4. To jest ciekawe , będziemy wykorzystywać statyczne metody z ForkJoinTask i w scali trzeba je niezależnie zaincludować (przynajmniej w ScalaIDE by działało.)
  5. Nowy typ klas do rozdziedziczenia - RecursiveTask[
  6. Statyczna klasa, która wywołuje pod-taski. Jak widać to my musimy zadbać o to by stworzyć ich odpowiednią ilość (i jakość).
  7. Na koniec suma i koniec

Podsumowanie

Jak już wspominałem w jakieś wielkie i wyczerpujące opisy się nie bawiłem bo wszystko omówimy sobie w trakcie warsztatu. Jeśli zajęcia się spodobają to na pewno będziemy je powtarzać i materiał uzupełniać.

To by było tyle jeśli chodzi o cześć pierwszą a w kolejnych : ThreadLocal,Semafory,Phasery,Lacze, Bariery, ThreadFactory i inne takie. A później(Albo w międzyczasie) jakieś najnowsze rozwiązania jak Akka i coś z JAvy8.

Oczywiście sam biorę udział w procesie edukacji, także jeśli ktoś znajdzie błędy w przykładach niech śmiało daje znać.

wtorek, 6 stycznia 2015

Szczęśliwej nauki w nowym roku

Nowy rok Bla bla bla postanowienia sratatata silna wola hejkumkeikum porady .

(Nie)racjonalni agenci

Chcę nauczyć się noweje technologii | schudnąć | dopakować | zmienić coś w swoim życiu.

Chcesz czy "musisz"? Różnica jest istotna i wręcz krytyczna do zrozumienia jak coś w sobie zmienić.

Zazwyczaj ("zazwyczaj") ludzie chcą/zmuszają się gdy:

  • (1) chcą grać w gry komputerowe
  • (2) starają się nauczyć nowego języka bo to może zaprocentować czymś tam w przyszłość. Mają postanowienie, że się nauczą ale jednocześnie mają ochotę porobić coś innego.
  • (1) chcą pić browary - trudno mi sobie wyobrazić sytuację, gdzie ktoś ma postanowienie noworoczne - "będę pić przynajmniej 2 browary dziennie chociaż nie mam ochoty"
  • (2) starają się ćwiczyć regularnie - chociaż woleliby posiedzieć i pooglądać filmy
Wytłumaczyć dlaczego te czynności z "1" wychodzą dla większości łatwiej niż te z "2" można na wysokim poziomie humanistyczno-psychologicznym - ot wystarczy zerknąć na taki model Dorosły,Rodzic i Dziecko. Kiedyś używałem tego do zmotywowanie siebie do treningów krav magi po tym jak dostałem kilka razy z rzędu bęcki na mieście - ale ten model jest tak ogólny jak ...(tutaj można wstawić żart z dowolną ogólną metodyką, która mówi "rób tak i nie pytaj dlaczego")

Zanim pójdziemy dalej zastanówmy się czym się różni granie w gry(na PC) od zwykłego programowania?

  1. Granie w gry - siadasz, masz przed sobą maszynę z pewnym ustawieniem bitów a pamięci. Klikasz myszkę, naciskasz klawiaturę i zapisujesz nowe ustawienie bitów w pamięci
  2. Programowanie- siadasz, masz przed sobą maszynę z pewnym ustawieniem bitów a pamięci. Klikasz myszkę, naciskasz klawiaturę i zapisujesz nowe ustawienie bitów w pamięci
Gdzie więc tkwi różnica?

Hamowanie

Co może nie być oczywiste na pierwszy rzut oka - obydwa powyższe obrazki przedstawiają mózg :). Z tyłu mózgu (w uproszczeniu) znajdują się pierwotne zaś z przodu nowożytne moduły - a jeśli to złe nazwy to w duchu personifikacji nazwijmy je "Staruch" i "Nowak".

Staruch towarzyszy nam od zarania ewolucji i to on ma misję "przetrwaj i rozmnażaj(się)". To on chce natychmiastowej gratyfikacji w postaci węglowodanów prostych, "low hanging frutów" czy tudzież innych łatwych natychmiastowych zysków. To on także każe nam siedzieć i nie wychylać się bo wtedy czujemy się komfortowo. Co bardzo ważny co okaże się póżniej krytyczne Nie ma poczucia czasu jako takiego. Staruch potrafi działać w ramach grupy doskonale rozumiejąc, że jeśli może łatwo i szybko pokonać smoka to zostanie się bohaterem w swoim domu

Nowak pojawił się niedawno i myśli abstrakcyjnie. Rozumie, że on za miesiąc czasu to także on. Że wybory z dnia dzisiejszego mają swoje konsekwencje w przyszłości i że czasami coś co jest nieintuicyjne tu i teraz ma swój sens "kiedyś". A i jeszcze jedno - Nowak wcale nie kontroluje starucha choć chce tak myśleć. To staruch ma więcej doświadczenia w zarządzaniu rdzeniem kręgowym, hormonami i podobnymi mechanizmami. Zawsze gdy Staruch wygrywa spór Nowak może albo czuć się źle albo wkręcić sobie, że on tak naprawdę to lubi dużo jeść/pić/palić a ten odpoczynek mu się zwyczajnie należał a ten koleś, któremu zajął miejsce parkingowe na pewno albo jest zły i mu się należało albo pewnie cwaniak jakiś i mu się no właśnie - należało.

Takie rzeczy niestety się dzieją gdy rozwijamy system bez refaktoringu :(

A jeśli komuś się nie podoba ta metafora to polecam Thninking fast and slow

Notka -Trochę mechaniki

Na mój obecny stan wiedzy w mózgu działa masa niezależnych procesów, które wykorzystują te same materiały do pracy (i znów na obecny stan wiedzy to będzie głównie wapń i coś tam jeszcze). Dlatego też istnieje skończony czas w jaki Nowak może się przeciwstawiać Staruchowi a proces nazywa się Hamowaniem.Np. Staruch chce sobie splunąć a Nowak go powstrzymuje bo przy Policji to tak ryzykownie. Czas ten skraca się po nieprzespanej nocy na skutek zalegania odpadów bio-chemicznych w mózgu (czy coś w ten deseń)

Relacje

Sytuacja 1 - koncepcja hedonistyczna

Staruch chce cukrów bo cukry były dobrym pożywieniem w czasach flinstonów, Nowak chce cukru bo to przyjemne. Staruch chce grać bo w swoim prostym rozumieniu świata zyskuje bogactwa/status/nowy dywan w simsach a z organizmu nie dochodzą żadne sygnały jako by to było męczące. Nowak chce grać bo to przyjemne. Czy ta sytuacja jest dobra czy zła? Generalnie wielu ludzi tak idzie przez życie niektórzy są szczęśliwi inni nie - Szczęście=Rzeczywistość-Oczekiwania.

Ta sytuacja może również wiązać się z czymś raczej pozytywnym. Np. możemy się uzależnić od dobrego samopoczucia po skończonym treningu a po miesiącu odpoczynku od cukrów i przetworzonej żywności nawet jedzenie brukselki może kojarzyć się z przyjemnością

Sytuacja 2 - Konflikt

Staruch chce grać a Nowak chce obejrzeć prezentację o Springu. Staruch chce obejrzeć film a Nowak chce iść na siłkę bo sobie zrobił postanowienie noworoczne. To jest dosyć nieprzyjemny stan z którego można się uwolnić techniką "fake it till you make it" - czyli jakoś zmusić Starucha do robienia czegoś aż mu się to spodoba lub wkręcić starucha, że robienie tego czegoś jest przyjemne- zaraz więcej o tym.

W tym przypadku ma miejsce tzw. "słomiany zapał". Czyli sytuacja gdy Nowak wkręcił Starucha wizualizacjami o tym jak po dwóch tygodniach rozpiętek będzie miał klatę ala Arnold - atu kicha i do tego trzeba jeszcze jakąś dietę utrzymywać - Staruch mówi - "Siadaj i oglądaj film w nie będziesz mi tutaj farmazony w głowie generował"

Sytuacja 3 - Inspiracja podstawowa

Przejdźmy na poziom abstrakcji wyżej - co tak naprawdę "motywuje starucha"? Przez ostatnie kilka milinoów lat dojrzewał on w grupach do 150 osobników gdzie wzajemne relacje i hierarchie decydowały o tym czy żyjesz/nie żyjesz. Jeśli zrobiłeś salto i jednym uderzeniem zabiłeś mamuta zostajesz bohaterem i grupa instynktownie "coś jest ci winna". Jeśli zrobiłeś salto i zabiłeś mamuta ale nikt tego nie widzi to pewnie nikt ci nie uwierzy.

Żarcia było mało, ludzie jakoś funkcjonowali nie jedząc 7 małych dobrze zbilansowanych posiłków dziennie. Nie wiem czy mamuty miały gluten czy nie ale raczej nikt o diecie nie myślał.

Czy nauka nowego języka może jakoś zająć miejsce widowiskowego poskromienia mamuta?

  • Przede wszystkim zabicie mamuta dostarczyło mięsa dla gromady. Niech mięso stanowi pewno wartość. Ucząc się języka możesz potencjalnie zwiększyć swoje szanse na zatrudnienie ale tutaj znowu może pojawić się konflikt bo Staruch woli grać w gry. Sytuacja będzie inna gdy już jesteśmy w miejscu gdzie jest dosyć prosta relacja pomiędzy "nauka języka"~"Profit" - pamiętajmy, ze Staruch ma proste pojmowanie świata. Można również uczyć się języka by nauczyć innych tego języka by oni więcej zarabiali - bardziej abstrakcyjne i trudniejsze do realizacji.
  • No i też w kontekście tego punktu trudniej zmotywować siebie do nauki języka dla samego siebie aniżeli do nauki języka gdy można się tym przed kimś pochwalić - i to w sposób pozytywny pomagając np. na forum.

Sytuacja 4 - Inspiracja wyższych celów

Ponieważ ludzki umysł szuka wszędzie przyczyny i skutku toteż wiele dziwnych rzeczy robiono w imię różnych abstrakcyjnych idei. Pamiętam z lekcji języka polskiego, że jakiś koleś przesiedział 14 lat na drzewie w imię lepszego życia po życiu. Generalnie słowo klucz to "wyższa Idea" czy w imię "wyższej idei spotka mnie coś dobrego". Nie znam dokładnej mechaniki ale jeśli komuś uda się to rozgryźć to zapewne i tędy wiedzie droga do spełnienia postanowień noworocznych.

Sytuacja 1b - antyhedonizm

Tak jak Staruch i Nowak mogą robić przyjemne odczucia tak mogą wspólnie nie lubić nieprzyjemnych. Np. można pobić swój rekord życiowy na setkę gdy gonią nas łysi na mieście. Ewentualnie nie lubimy wychodzić na niekonsekwentnych także jedna słynna technika mówi podziel się planami ze znajomymi - ten miecz ma dwa końce gdyż z drugiej strony "mienie w dupie" tego co myślą o nas inni jest ważnym składnikiem samorozwoju

Sytuacja 6 - Inspiracja braku celów - Nihilizm Egzystencjalny

To jest dosyć ciekawy przypadek - być może na pewnym poziomie rozwoju gdy zagłuszeniu ulegnie naturalna ignorancja a rzeczy oczywiste przestaną napędzać codzienność, wtedy Staruch sam sobie zada pytanie "przetrwać i rozmnażać - ale po co?". Nowak spyta "co robimy?" , "- no chyba szukamy?".

Może to się zakończyć głęboką depresją ale również niezwykłą motywacją w kierunku szeroko pojętych poszukiwań i nowych doświadczeń. Temat trudny ale niektóre postacie przewijające się przez ten nurt maja fajne cytaty :

  • "Ludzie ro­zumieją mnie tak słabo, że nie ro­zumieją na­wet moich narze­kań na to, że mnie nie rozumieją." - Soren Kierkegaard
  • "To niezwykłe, jak można kłamać, przyznając sobie rację." - Jean-Paul Sartre
  • "Życie można zro­zumieć, pat­rząc nań tyl­ko wstecz. Żyć jed­nak trze­ba naprzód." - Soren Kierkegaard
  • "Wszyscy jesteśmy tutaj, aby jeść i pić dla zachowania naszego drogocennego istnienia, a nie ma nic, nic, żadnej racji istnienia." - Jean-Paul Sartre
  • "Człowiek zaw­sze czu­je się czymś więcej niż tym, co osiągnął, więc co­kol­wiek by osiągnął, w niczym nic może zna­leźć uko­jenia, za­dowo­lenia, szczęścia." - Soren Kierkegaard
I tak ogólnie : Nihilizm egzystencjalny

Za długie motyla noga - TL;DR - szbka technika - obcy z przyszłości

  1. Artykuł - Neurologia prokrastynacji
  2. W skrócie - Skany mózgu pokazują, że nasze szare komórki nie traktują "nas tu i teraz" i "nas w przyszłości" jako tę samą osobę. "Ja z przyszłości" i "trzecia osoba z tu i teraz" generują te same reakcje.
  3. Należy stworzyć połączenie poprzez wizualizacje, medytacje i tego typu techniki dzięki czemu Staruch zrozumie, że krzywdząc siebie z przyszłości krzywdzimy siebie
I jeszcze jeden trik na deadlajny : http://www.nytimes.com/2015/01/04/business/if-you-want-to-meet-that-deadline-play-a-trick-on-your-mind.html

niedziela, 14 grudnia 2014

Personifikacja risorsów

Ten proces możesz zaobserwować samemu lub samej (w zależności do tego czy jesteś sam czy sama). Następnym razem gdy będzie jakoś segregować czy kategoryzować informacje upraszczając niejako ich formę - sprawdź czy poczujesz się wtedy lepiej czy gorzej? To jest niezwykle ciekawy temat na inny artykuł ale po pewnym czasie chyba każdy może wyćwiczyć u siebie zmysł samoobserwacji aby zauważać kiedy "układ nagrody" strzela z dopaminy. Następnie możemy możemy przejść do bardziej abstrakcyjnej analizy dlaczego np. dzieje się to na widok ciastka,wódy czy czegoś innego czego nie powinno się nadużywać --> http://en.wikipedia.org/wiki/Introspection

Za tą strzałką ---> Jakie diagramy lubimy najbardziej znajduje się link do artykułu z początku bieżącego roku, w którym podałem kilka badań pokazujących jak percepcja wizualna złożoności diagramu wpływa na odczucie czy to co znajduje się na rysunku ma merytorycznie sens czy nie. Jest to dosyć ważne - ba nawet rzekłbym niesamowicie ważne gdyż mam taką sobie intuicję, że ludzie a szczególnie ludzie w IT myślą, że ich proces decyzyjny jest maksymalnie rzeczowy i merytoryczny (a jeszcze sto tysięcy lat temu rzucaliśmy się bananami).

To jest już trzeci akapit a jeszcze nie wiadomo o czym ma być ten artykuł. Już mówię. Generalnie jeśli projekt klas wydaje się lepszy gdy diagram jest relatywnie przejrzysty to co się stanie jeśli zamiast klasy i modułów wstawimy tam ludzi? I uwaga to nie będzie tylko post o narzekaniu ale na końcu pojawi się pewne rozwiązanie, które w moim odczucia przyniosło pewne pozytywne rezultaty.

Co lubi organizacja?

Im większa firma tym silniejsze procesy unifikacji w niej działają. Jeśli 10000 ludzi pracuje w firmie i owa firma ma hierarchię (czyli jakieś 99,999% przypadków) pojawią się procesy upraszczające rzeczywistość do formy łatwej w ogarnięciu dla wyższych poziomów hierarchii (czytałem o próbach stworzenia struktury organizacji coś na wzór fraktali --> http://en.wikipedia.org/wiki/Holacracy ale nie wiem czy tam to będzie wyglądać jakoś inaczej).

Uproszczenie polega na tym, że zamiast 10000 indywidualnych historii życia i unikalnych zdolności mamy kilka ról jak np. Junior Developer, Architekt itd. Pojawiają się często też takie matryce, ze np. znajomość Javy=5 a SQL=4 (Cokolwiek to miałoby znaczyć). I w ten sposób złożoność informacji wraca do stadium wygodnego do operowania w pamięci krótkotrwałej --> http://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two

A co lubią ludzie?

To żadna nowość, że sformułowanie "trybik a maszynie" ma raczej skojarzenie pejoratywne (nie wiem czy używam tego słowa w dobrym znaczeniu ale miało nie być bluzg to musi być jakieś zastępstwo).

Wszelkie profile społecznościowe, fejsy,twitery dają coraz więcej opcji aby profil użytkownika był unikalny. I chyba w tej całej hipsterni też chodzi o to aby być maksymalnie różnym od mainstreamu.

Istnieje jeszcze jeden niezwykle ale to niezwykle ważny aspekt całej sprawy (ale to niezwykle ważny). Przy coraz bardziej przyśpieszającym rozwoju technologii dla każdej firmy IT skarbem po prostu skarbem są ludzie, którzy dbają o własny rozwój i dokształcają się we własnym zakresie (no chyba są skarbem). I istnieje naturalny mechanizm motywacyjny, który temu sprzyja --> z tak zwanych popularnych motywatorów wewnętrznych : Autonomia,Mistrzostwo,Poczucie celu -> to będzie Mistrzostwo

Ewolucyjnie to działało tak, że uzyskując unikalną zdolność w ramach grupy osobnik uzyskiwał specjalny status i respekcior w tej grupie. Ale jak można uzyskać specjalną zdolność jak wszyscy w grupie to "Inżynier" o znajomości Java=4? Jak mogę pokazać, że posiadam unikalną wiedzę w zakresie kolekcji, wielowątkowości czy przetwarzania synchronicznego? A może umiem łatwo przekazywać informacje?

I w tym momęcie nadchodzi....

Introducing Corpo RPG

Cała idea polega na tym aby upiec dwie pieczenie na jednym ogniu : dostarczyć odpowiednie uproszczenie informacji do wyższych poziomów hierarchii w firmie a jednocześnie zachować "ludzkość" osób w zespole, informacje o ich unikalnych zdolnościach i osiągnięciach oraz przedstawić jedyną w swoim rodzaju historię ich samorozwoju.

Wymyśliliśmy sobie zestaw umiejętności w których można było się rozwijać i do którego cały czas coś dorzucamy. Co bardzo ważne - każda umiejętność ma motywacje finansową bo generalnie ludzie na ważnych stanowiskach w firmie lubią rozmawiać o pieniądzach.

Bazki

Paypale

Front cześć 1
Front cześć 2

Funcjonalność

Mobilki Mobilki

Deploymenty

Performens

Nawijka

Rozwój innych:

Współpraca poza zespołem :

Automatyzacja testów

Testy widoku

Analytiksy

Kilka klas ma swój specjalny opis np :

Do tego za jednorazowe osiągnięcia dostajemy perki

CAB Driver
I specjalnie dla studentów

Takze ten tego - ludzie w zespole mówili, że im się podoba i menadżerowie wyższych szczebli też mówili, że im się podoba - przynajmniej tak mówili :)

czwartek, 4 grudnia 2014

Ciekawsze modele wielowątkowe (a Akka) + sprawy społecznościowe

Kącik społecznościowy

Jutro zapraszamy na prezentację Marcin Grzejszcza­ka Mikroserwisy

JUG Łódź Misja

Cezary Draus - zarząd JUG niezależna improwizacja o misji JUG Łódź.

moim zdaniem warto by propagować przede wszystkim samorozwój
jako sposób na życie
no ludzie tego nie kumaja chyba
pewnie mysla ze sa IT arystokracja
bo tak im się wmawia
a do tego idzie niz demograficzny
wiec raczej ludzi zdolnych wiecej nie bedzie
a samo sie nie nakurwi
Golf R sam się nie kupi
musisz kurwa sie dorobic
albo krasc
albo sie kurwic
albo sie nauczyc
wybór należy do Ciebie

a samo się nie nakurwi - to od tej chwili nieoficjalna hasło szczęścia poprzez samorozwój.

Właściwy artykuł

Czy jest sens nauki czegoś co wydaje się w danej chwili niepotrzebne(to pytanie retoryczne także nie należy się nad tym zastanawiać tylko czytać dalej)? Przez wiele lat web development wyglądał dla mnie bardzo jednowątkowo. Zazwyczaj jakiś serwer ogarniał requesty przy pomocy swojej puli wątków a ja jako programista musiałem tylko uważać na globalny stan podczas gdy cała reszta szła od góry do dołu po linii prostej (zwaliduj dane, wywołaj jakieś funkcje i zapisz do bazy danych)

Teraz procki mają coraz więcej rdzeni a sam PlayFramework na dzień dobry 4 różne pule wątków i trzeba ten temat zacząć ogarniać. Istnieje kilka ciekawych pozycji o wątkach w Javie i np. taka "Java Concurrency in Practice" nie tyle opisuje jak dobrze zabrać się do tematu ile przedstawia na jak wiele złych sposobów można to zrobić.

Dlatego też warto się zainteresować pewnymi rozwiązaniami wysokopoziomowymi, które pojawiają się tu i ówdzie a w których nie trzeba aż tak bardzo wchodzić w interakcje pomiędzy poszczególnymi wątkami.

A jeśli to nie wystarczy to...

... Historia Hrabiego Monte Christo opowiada historię kolesia, który w swojej naiwności daje się złapać w intrygę swoich fałszywych przyjaciół i ląduje w więzieniu na wyspie gdzie przypadkiem poznaje innego kolesia, który ma wiedzę o wszystkim i tę wiedzę(+ gdzie jest skarb) przekazuje głównemu bohaterowi. Cały proces trwa wiele lat ale nasz ziom w końcu ogarnia swoją ignorancję i zdobywa nowe, pełniejsze spojrzenie na świat.

W przypadku tej historii potrzebny był szok by wyzwolić naukę - a mamy przecież o wiele przyjemniejsze opcje - jak np. czytanie ciekawego internetu zamiast oglądania telewizji i takich tam.

Jak związek ma ta historia z nowoczesnymi modelami programowania wielowątkowego? Żaden ale przynajmniej stanowi ciekawy przerywnik, daje uzasadnienie dla wklejenia fajnego obrazka i być może zabija wrażenie, że poniższa treść jest zwyczajnie spisana z internetu. Ale wracając do tematu...

Klasyka

Jak zwykle za pomoc edukacyjną posłuży naciągany problem, który można łatwo rozwiązać za pomocą AtomicInteger - ale dla potrzeb ćwiczenia załóżmy, że nie można

  • Globalny stan - licznik
  • Kilka wątków, które wchodzą sobie w drogę
object LockingExerciseSimple {

  private var counter = 0

  def main(args: Array[String]) {
    val threads = (1 to 100).map(_ => createThread())
    threads.foreach(_.start())
    threads.foreach(_.join())
    println(counter)
  }

  def createThread() = new Thread {
    override def run() = {
      counter = counter + 1
    }
  }
}

Uruchamiając ten kod nigdy nie powinniśmy otrzymać wyniku 100 gdyż ile tam wątków najedzie na siebie i nadpisze licznik.

By rozwiąπać problem klasycznie tworzymy "loka" wokół inkrementacji licznika i zaczynamy się zastanawiać czy o czymś przypadkiem nie zapomnieliśmy co może zakończyć się zakleszczeniem.

  • Globalny stan - licznik
  • Kilka wątków, które wchodzą sobie w drogę
  • Blokowanie wątków na locku
object LockingSolutionSimple {
 private var counter = 0
 private val lock=new ReentrantLock()

  def main(args: Array[String]) {
    val threads = (1 to 100).map(_ => createThread())
    threads.foreach(_.start())
    threads.foreach(_.join())
    println(counter)
  }

  // TO JEST WAZNY FRAGMENT
  def createThread() = new Thread {
    override def run() = {
      lock.lock()
      try{
       counter = counter + 1
      }finally{
        lock.unlock()
      }
    }
  }
}

Agent

  • Stan nie jest dostępny globalnie
  • Dostęp synchroniczny
  • Modyfikacje asynchroniczne

Generalnie agent to specyficzny rodzaj aktora - czyli warto w pierwszej kolejności wspomnieć o aktorach. Ale aktorzy są na topie i każdy o nich wspomina i takie powielanie opisów byłoby nudne.

Gdzieś na githubie widziałem wątek, w którym jacyś ludzie implementujący bugfixy w akka dyskutowali czy jak w tym a tym miejscu będzie volatile to dobry asembler javy powstanie czy coś w ten deseń. To co chcę tutaj powiedzieć,to to że jakieś mądre głowy zadbały już o to, że logika w aktorze wykona się jednowątkowo i nie będzie deadlocków.

Agent to właśnie taki specjalny aktor, który z założenia kontroluje dostęp do konkretnej zmiennej. Przystosowanie polega na tym, że ów stan zmiennej można pobrać synchronicznie w dowolnym momencie.

object AgentsSolution {
  import akka.agent.Agent
  import scala.concurrent.ExecutionContext.Implicits.global

  private var counter = Agent(0)

  def main(args: Array[String]) {
    //każdy wątek zwiększa stan licznika o jeden
    val threads = (1 to 100).map(_ => createThread())
    threads.foreach(_.start())
    threads.foreach(_.join())
    
    /** 1 updejty są wysyłane do agenta asynchronicznie dlatego na początku get wypisze zero**/
    println(counter.get)
    Thread.sleep(1000)
    // ale już po sekundzie powinna być setka
    println(counter.get)
    
    /**2 
    // w dowolnej chwili można pobrać z agenta future, który zakończy się gdy wszystkie funkcje z danej chwili zostaną wykonane
    val future=counter.future
    future.onComplete {
      case result => println(result)
    }
    Thread.sleep(1000)**/
    

  }
  // TO JEST WAZNY FRAGMENT
  def createThread() = new Thread {
    override def run() = {
      // a tak się wysyła do agenta funkcje, które on wykona jednowątkowo
      counter.send(c => c + 1)
    }
  }
}

STM

To w zasadzie zwykłe transakcje w pamięci

  • Globalny stan opakowany w Ref
  • Zmiany odbywają się w transakcji
  • w przypadku konfliktu mamy rollback
object STMSimpleSolution {
  //zwykle transakcje ale tylko dla danych, które są w Ref
  private var counter = Ref(0)

  def main(args: Array[String]) {
    val threads = (1 to 100).map(_ => createThread())
    threads.foreach(_.start())
    threads.foreach(_.join())
    
    atomic{implicit txn=>
     println(counter.get)
    }
  }

  // TO JEST WAZNY FRAGMENT
  def createThread() = new Thread {
    override def run() = {
      // jeśli dwa wątki wjadą na raz z tą samą wartością to zmiany będą rollbackowane
      // i jedna z transakcji powtórzona
      atomic{implicit txn=>
          val currentValue=counter.get
       counter.set(currentValue+1)
      }
    }
  }
}

Dataflow

  • Nie ma faktów są jedynie obietnice
  • Do zmiennych dane można przypisać tylko raz
  • Każdy flow to inny wątek ale wszystko magicznie się układa

To jest fajne - przeterminowane -ale fajne bo zagina czasoprzestrzeń. Bo zobaczmy chociaż taką sytuację :

    // CAŁY FRAGMENT W ZASADZIE JEST WAŻNY
    val counter = Promise[Int]()
    val v1, v2 = Promise[Int]()

    //tutaj jest dodawanie
    flow {
      threadName()
      counter << v1() + v2()
      println("counter = " + counter())
    } 
    // a dopiero tutaj ustalenie co dodawać
    flow {
      threadName()
      v1 << 40 
    }
    flow {
      threadName()
      v2 << 2 
    }
    // i każdy flow odbywa się w innym wątku
    //ForkJoinPool-1-worker-11
    //ForkJoinPool-1-worker-13
    //ForkJoinPool-1-worker-9

To jest fajne ale już deprecated bo w najnowszej wersji Akka zastąpiła je scala async

Async Aysnc

  • Instrukcje do asynchronicznego odpalania obliczeń w osobnym wątku są tworzone na prawdę bardzo prosto
  • Można określać zależność czasową pomiędzy blokami przy pomocy prostego await
    var counter = 0

    val future1 = async {
      threadName()
      Thread.sleep(300)
      40
    }
    val future2 = async {
      threadName()
      Thread.sleep(200)
      2
    }

    val sumFuture = async {
      threadName()
      counter = await(future1) + await(future2)
      counter
    }

    sumFuture.onSuccess {
      case result => println(result)
    }
//ForkJoinPool-1-worker-13
//ForkJoinPool-1-worker-11
//ForkJoinPool-1-worker-9
//42

Na koniec

Z ciekawych modeli wielowątkowych, o których słyszałem są jeszcze "channels", które na pewno występują w języku Go i bibliotece clojure core.async ale nic o tym nie wiem to też nic nie napiszę ale zakończę artykuł w tym miejscu.

niedziela, 16 listopada 2014

Po Code Retreat 2014

Gdy piszę te słowy przyszły już pierwsze opinie na googlowym formularzu. Ludziom się generalnie podobało a po obecności słów "ch*j" i "du*a" wnioskuję, że studentom także.

Za rok trzeba będzie mocniej się postarać by ludzie mieli więcej szans popełnić błędy w kodzie i się na nich uczyć zamiast rozkminiać samą grę nad kartką. Najlepiej od razu podpowiedzieć kilka niedoskonałych rozwiązań na standardowe pytania - jak reprezentować planszę i od czego zacząć pisać testy.

Przez ostatnie dwa miesiące słyszałem wiele głosów w stylu "ej no znowu ta gra życie, może coś nowego..." a koniec końców chyba nie licząc jednej lub dwóch osób wszyscy uczestniczyli po raz pierwszy i doskonale pojechaliśmy "klasyką".

  1. Sesja wstępna-dowolna
  2. TDD i tylko jeden poziom zagłębienia kodu w metodach
  3. Jeden poziom zagłębienia, metody maksymalnie 5 linii długości i brak else
  4. obiad(oj jedzenia sporo było)
  5. Sesja "cicha" oraz do wyboru albo object-calisthenics lub not only OOP - wszyscy wybrali to pierwsze ;)
  6. Zostawiamy kod z poprzedniej sesji i mamy dodatkowe wymagania

Dominowała Java ale pojawił się też .Net, Ruby i Scala. Im więcej języków tym ciekawiej.

Dziękuję wszystkim za przybycie i organizatorom za zadbanie o wszystkie detale spotkania.

Ściana w kodzie

podobny temat poruszyłem już tutaj : o instanceof

Ciekawy problem powstaje gdy rozwiazujemy zadanie tworząc abstrakcję "Cell" i klasy pochodne "LiveCell" oraz "DeadCell". Jednocześnie gdzieś w kodzie musimy zliczać ilość żywych komórek - jak to zrobić?. Cześć osób dodaje do Cell metodę "isAlive"

Główny problem z tym rozwiązaniem polega na tym, że jeśli wyobrazimy sobie diagram klas to generalnie ten kwadrat co jest na górze nie powinien nic wiedzieć o tych co są na dole (chyba, ze ktoś rysuje do góry nogami) bo jeśli wie to jest ryzyko, że jak się zmieni coś na dole to trzeba będzie zmieniać przez tę zależność dosłownie wszystko. A jeśli w kwadracie na górze mamy metodę "isAlive" to on już wie, że gdzieś tam dziedziczy z niego żywa komórka, a teoretycznie cały bajer polega na tym aby bezboleśnie dodawać nowe podtypy bez mieszania w tym co jest zrobione do tej pory. Jeśli nie tak to jak można inaczej?

Prześledźmy taki tok myślowy :

  • Jeśli ktoś reprezentuje komórki przy pomocy boolean na tablicy to wykorzysta wartość true/false do stwierdzenia czy komórka jest żywa
  • Podobnie jeśli reprezentujemy je przy pomocy 0 i 1 - tutaj też nie będzie problemu.
  • enum {DEAD,ALIVE} - również łatwo rozpoznać typ komórki
  • LiveCell, DeadCell - tutaj można postąpić analogicznie. Generalnie dziedziczenie pozwala nam przenieść część logiki z kodu do kompilatora, który to wybierze odpowiednia implementację przez co sam kod jest bardziej elastyczny. Ale to nie znaczy, że wszystko ma się sprowadzać do tylko i wyłącznie tego mechanizmu. Tutaj łatwo można sprawdzić typ komórki w jakimś niezależnym i łatwym do testowania komponencie przy pomocy budzącego emocje "instanceof". Jeśli używamy tego mechanizmu do sprawdzenia ile elementów danego typu jest kolekcji wtedy raczej jest to ok, błąd wystąpi wtedy gdy używamy go by na podstawie typu wykonać jakieś operacji dla żywej komórki(i co gorsze rzutowanie) - wtedy w zasadzie wracamy do bazowego "if (komorka zywa) evoluujTak else evoluujInaczej"

Email Calisthenics

W trakcie ostatecznej retrospektywy wpadliśmy również na pomysł nowego ćwiczenia - bardziej w kierunku tych ludzie co więcej "komunikują niż programują".

W trakcie "Global Day of Email Retreat" uczestnicy grają w "Grę w życie" i mają ją rozwiązać odpowiednim łańcuchem maili. Ograniczenia.

  1. Respect people's time - Max 3 Recipients per email
  2. Focus - Only one discussion thread per email
  3. Save our eyes - whole email is written with the same font
  4. Don't overreact - No Top Management in CC
  5. Merit Arguments - No personal attacks in an email
  6. Avoid templates - participants can not use words "ASAP,Unacceptable,Concerned,Urgent or Opportunity"
Czy coś w ten deseń...

wtorek, 11 listopada 2014

Nauka Scali i Javy 8

Zanim przejdziemy do pierwszego spotkania-warsztatów przeznaczonych nauce Scali tylko i wyłącznie - wpierw kącik społecznościowy

Kącik społecznościowy

  • W najbliższa sobotę CodeRetreat - zapisy pod linkiem po prawej ---> link po prawej(zapisy) . Niby tam lista jest zamknięta ale dodajcie się na "waitlistę" bo kilka osób jeszcze pewnie zrezygnuje. W tym roku zapraszaliśmy speców z zagranicy do poprowadzenia wydarzenia i chociaż ich nie będzie to przysłali wskazówki jak tam to wydarzenie lepiej poprowadzić
    • Start by asking people what they would like to learn
    • Write down what they say on a flipchart or post-its
    • Group them into categories, and write down the number of "votes" for each category
    • Usually, you get things such as: pair programming, something new, TDD, Refactoring
    • If there are first timers, start with the normal free session that helps familiarize with the problem. Otherwise you can skip it
    • Then pick the categories in the order of votes and select the constraints that answer to those categories. You might need to improvise or ask for help - Adi and I will be available to help
  • A 20 listopada na DMCS odbędzie się wykład Michał Balińskiego o Amazon AWS. Michał jest na tę chwilę chyba najlepszym specjalistą w tych tematach w Łodzi - także na tę chwilę zapisało się już 40 osób i wy zapiszcie się także w linku obok Link obok do zapisów. Może pobijemy rekord publiczności. Obecny chyba nalezy do Grześka Borkowskiego do którego swego czasu przyszło około 70 osób na prezentacje o Javascript
  • .

Pierwsze spotkanie Nauki Scali

Scali warto się uczyć i jestem w pełni świadom tego, że są ludzie którzy pałają do tego języka nienawiścią (sam kiedyś tam byłem) ale jak ktoś nie chce nie musi używać natomiast możliwości edukacyjne są moim zdaniem ogromne :

  • REPL i worksheet są cudowne jeśli chodzi o prezentację uczestnikom tego co dzieje w kodzie. Nie trzeba printlainami iterować po elementach listy bo jest ona niejako "prześwietlana" Jeśli jest coś takiego w Javie to ja nie znaju.
  • Sam zacząłem interesować się scalą i programowaniem pod kątem nauki programowania funkcyjnego zanim Java 8 była gotowa i jeśli chodzi o ten temat to jest on dużo przejrzystszy w scali : funkcja to funkcja bez względu na to ile ma parametrów - gdzie w javie to programowanie funkcyjne jest zrobione najbardziej obiektowo chyba jak można było : Supliery,Consumery, funkcje, bifunkcje (a w dodatku "Updadek Bastionu" będą TriFunkcje i nowe zbroje).
  • W tym języku są koncepcje jak choćby pattern matching, które jak się okazuje (przynajmniej dla mnie było to odkrycie) są także nie obce wielu innym językom. Zamiast czekać nim pojawią się w Javie można z nimi zapoznać się już teraz.
  • I ostatni - moim zdaniem najważniejszy strategicznie punkt - generując anomalię edukacyjną polegającą na wzajemnej nauce relatywnie zaawansowanej technologi bez - (uwaga to bardzo ważne) - bez jasnego zysku komercyjnego - można przerwać zjawisko takiej "łódzkiej samo nakręcającej się spirali gówna" (rysunek dzięki uprzejmości mojej poniżej)

Jeśli ludzie nie byli tylko zwyczajowo mili to ogólnie się podobało. Generalnie idea jest taka aby pojawiła się jakaś grupa ludzi chcąca prowadzić warsztaty i to całe przedsięwzięcie się skalowało - są już chętni także następnym razem poprowadzi ktoś inny itd.

Co należy umieć aby poprowadzić warsztat

Tutaj dam linka sam do siebie - Pewność siebie poprzez niewiedzę. Generalnie najlepszą odpowiedzią co trzeba zrobić aby poprowadzić warsztat ze scali - należy nie wiedzieć, że nie ma się odpowiedniej wiedzy aby go poprowadzić. A później trzeba się tylko zgłosić, opracować materiał i przećwiczyć warsztat aby był ciekawy dla uczestników - i to tylko tyle.

Jak widać sam wykorzystuje tę technikę generując w sobie urojenia, że moje działania jakoś wpłyną na to co się dzieje w tym mieście. Ale dopóki ja mam z tego zabawę a ludziom się podoba i się czegoś uczą to będziemy to robić.

Co więcej

TomTom zaczął się angażować w edukację studentów w Łodzi. Na pierwszych zajęciach mieli okazję poznać trochę Scali i Playframework.

A TERAZ KOD.

Pierwsze porównanie

Na samym początku szybkie porównywanie scali i javy8 pod kątek filtrowania elementów listy przy pomocy lambdy (nie wiem czy to fachowo powinno nazywać się "przetwarzanie batchowe","przetwarzanie strumieniowe"czy też "przetwarzanie deklaratywne"?)

val lista=List(1,2,3,4,5) 
lista.filter(element=>element>2)
zadanie : stworzyć listę elementów 'a','b','c','d','e' i odfiltrować tak by zostało 'd','e'
val lista2=List('a','b','c','d','e')
lista2.filter(e=>e>'c')

Java
List lista = asList(1,2,3,4,5);
// a to nie wystarczy
Stream filter = lista.stream().filter(element->element > 2);
//trzeba tak
List result = lista.stream().filter(element->element > 2).collect(Collectors.toList());

Tworzenie listy

Aby słuchacze się nie nudzili - kilka ciekawych sposobów na inicjację(tudzież tworzenie) listy

List.range(0, 10, 2)
List.fill(7)(42)
List.tabulate(10)(elem=>s"element ${elem}")
"tekst".toList
zadanie : stworzyć listę z kolejnymi kwadratami liczb od 0 do N=20 i zostawić tylko podzielne przez 3
val lista=List.tabulate(20)(e=>e*e)
lista.filter(_ % 3==0)

sortowanie i mapowanie

lista.filter(element=>element>50).sortWith((elem1,elem2)=>elem1>elem2).map(elem=>elem+1)
zadanie : stworzyć listę od 1 do 100, zostawić elementy większe od 50 i posortować tak by parzyste liczby były na początku listy
List.range(1,100).filter(e=>e>50).sortWith((e1,e2)=>e1%2==0)

Java
List result = lista.stream()
    .filter(e->e > 2)
    .sorted(Comparator.reverseOrder())
    .map(e->e+1)
    .collect(Collectors.toList());
  
  result.forEach(System.out::println);
Przy pierwszym zetknięciu się z uproszczeniem zapisu przy pomocy podkreślenia czytelnik może przeżyć szok lecz zauważenie zysków tego zapisu to tylko kwestia czasu i praktyki.
 lista.filter(e=>e>2).sortWith((e1,e2)=>e1>e2).map(e=>e+1)
//lub lepiej
        lista.filter(_>2).sortWith(_>_).map(_+1)

Podzbiory

 val lista=List(1,1,2,1,3,1,4,5) 

lista.takeWhile(_==1)                     //> res0: List[Int] = List(1, 1)
lista.dropWhile(_==1)                     //> res1: List[Int] = List(2, 1, 3, 1, 4, 5)
Stream stream = Stream.of(1,1,2,1,3,1,4,5);
  
  List result = stream
    .limit(5)
    .skip(2)
    .sorted(Comparator.reverseOrder())
    .map(e->e+1)
    .collect(Collectors.toList());
  
  result.forEach(System.out::println);
zadanie : stworzyć listę od 0 do 100 ,posortować po drugiej cyfrze (czyli 19 jest większe od 88) i zostawić początek listy mniejszy od 90.
List.range(0, 100).sortWith((e1,e2)=>(e1%10) > (e2%10)).takeWhile(_<90)

Kilka kolekcji i Flatten

Przy tym cwiczeniu dla zwiększenia satysfakcji uczestników można (a nawet należy) wspomnieć różnicę pomiędzy listą i wektorem.

 val lista=List("elektromagnetyczny","pierun","trzy")
     //Vektor                                             
 val samogloski=Vector('a','e','u','y')    //> samogloski  : scala.collection.immutable.Vector[Char] = Vector(a, e, u, y)
 //Array
 def zostawSamogloski(s:String)=s.toCharArray().filter(samogloski contains(_))
                                                  //> zostawSamogloski: (s: String)Array[Char]

 lista.map(zostawSamogloski)               //> res0: List[Array[Char]] = List(Array(e, e, a, e, y, y), Array(e, u), Array(y
                                                  //| ))
 lista.map(zostawSamogloski).flatten       //> res1: List[Char] = List(e, e, a, e, y, y, e, u, y)
 
 //lub
 lista.flatMap(zostawSamogloski)
zadanie : zdublować przy pomocy flatMap wszystkie elementy listy List(1,2,3) -> List(1,1,2,2,3,3)
List(1,2,3).flatMap(e=>List(e,e))

redukcja

Generalnie redukcję na siłce robi się na wiosnę aby na lato by kaloryfer - tutaj jednak chodzi o redukcję kolekcji do jednej wartości. W poniższym przykładzie będzie również zaprezentowany mały przykład pokazujący różnice pomiędzy metodą i funkcją

val lista=List(1,2,3,4,5)

//redukcja
lista.reduce(_*_) 

//ponowne wykorzystanie funkcji
def add(a:Int,b:Int)=a+b 

lista.reduce(add)  
lista.foldLeft(0)(add)

//ale mozna i
val addf=(a:Int,b:Int)=>a+b  

//jaka jest roznica? Metody moga mieć generyki i na tym etapie tyle tłumaczenia musi wystarczyć :)

val reduceText=(a:Int,b:Int)=>a+":"+b
def reduceTextM[A](a:A,b:A)=a+":"+b

reduceText(1,2)                           //> res1: String = 1:2
reduceText('1','2')                       //> res2: String = 49:50
//BŁĄÐ KOMPILACJI //reduceText("1","2")
reduceTextM(1,2)                          //> res3: String = 1:2
reduceTextM('1','2')                      //> res4: String = 1:2
reduceTextM("1","2")                      //> res5: String = 1:2

lista.reduce(reduceTextM)                 //> res6: Any = 1:2:3:4:5
val lista2=List('a','b','c')                    //> lista2  : List[Char] = List(a, b, c)
lista2.foldLeft("")(reduceTextM)          //> res7: String = :a:b:c

zadanie : treść podobna "zdublować przy pomocy flatMap wszystkie elementy listy List(1,2,3) -> List(1,1,2,2,3,3)" ale tym razem w dwóch wersjach
  1. Funkcji, która przyjmuje listę typu Int i wykonuje wspomnianą operację
  2. Metody, która przyjmuje jakąś listę typu A i wykonuje wspomnianą operację
val double=(l:List[Int])=>l.flatMap(e=>List(e,e))
def doubleM[A](l:List[A])=l.flatMap(e=>List(e,e))

A jak to wygląda w Javie ?
Stream stream = Stream.of(1,1,2,1,3,1,4,5);
  Optional result = stream.reduce((a,b)->a+b);
  result.ifPresent(System.out::println);

Grupowanie

Tutaj zaś zastapimy działanie na intach działaniem na bardziej domenowych obiektach. Zobacyzmy tkaże ile więcej kodu trzeba napisac w Javie by osiągnąc podobny rezultat:(

case class User(name:String,age:Int,gender:String)
 val lista=List(User("Stefan",28,"M"),User("Joanna",20,"F"),User("Elżbieta",28,"F"))
                                                  //> lista  : List[akademia.kolekcje.User] = List(User(Stefan,28,M), User(Joanna,
                                                  //| 20,F), User(Elżbieta,28,F))

 lista.groupBy(_.age)                      //> res0: scala.collection.immutable.Map[Int,List[akademia.kolekcje.User]] = Map
                                                  //| (20 -> List(User(Joanna,20,F)), 28 -> List(User(Stefan,28,M), User(Elżbieta
                                                  //| ,28,F)))
 lista.groupBy(_.gender)                   //> res1: scala.collection.immutable.Map[String,List[akademia.kolekcje.User]] = 
                                                  //| Map(M -> List(User(Stefan,28,M)), F -> List(User(Joanna,20,F), User(Elżbiet
                                                  //| a,28,F)))
eksperymenty :
val random=new java.util.Random()
import Math.abs
val users=List.tabulate(100)(n=>User((n % 30).toString,abs(random.nextInt()) % 30,"F"))
users.groupBy(_.age)
users.groupBy(_.name) 

W javie niestety trzeba się trochę więcej rozpisać :
class User{
 private final String name;
 private final int age;
 public User(String name, int age) {
  this.name = name;
  this.age = age;
 }
 public String getName() {
  return name;
 }
 public int getAge() {
  return age;
 }
 @Override
 public String toString() {
  return "User [name=" + name + ", age=" + age + "]";
 }
}
List<User> users = asList(new User("ccc",12),new User("bbb",20),new User("aaa",12));
Map<Integer, List<User>> result = users.stream().collect(Collectors.groupingBy(User::getAge));

result.forEach((k,vl)->System.out.println(k+":"+vl));

Span i partition

Niech kod przemówi :
val numery=List(1,4,5,78,9,1,2,3)         //> numery  : List[Int] = List(1, 4, 5, 78, 9, 1, 2, 3)
 
 
 numery.partition(_%2==0)                  //> res0: (List[Int], List[Int]) = (List(4, 78, 2),List(1, 5, 9, 1, 3))
 numery.partition(_>3)                     //> res1: (List[Int], List[Int]) = (List(4, 5, 78, 9),List(1, 1, 2, 3))
 numery.partition(_<10)                    //> res3: (List[Int], List[Int]) = (List(1, 4, 5, 9, 1, 2, 3),List(78))
 numery.span(_<10)                         //> res2: (List[Int], List[Int]) = (List(1, 4, 5),List(78, 9, 1, 2, 3))
zadanie : stworzyć listę od 0 do 100 i podzielić ją na dwie listy : w jednej mają być potęgi dwójki a w drugiej liście cała reszta.
import math._
val powersOfTwo=List.range(0, 10).map(n=>pow(2, n).toInt).toSet
List.range(0,100).partition(powersOfTwo contains _)

Widok

Tutaj jest różnica z javą.
val numery=List(1,4,5,78,9,1,2,3)         //> numery  : List[Int] = List(1, 4, 5, 78, 9, 1, 2, 3)
 
// zwrocic uwage na SeqViewMMF 
numery.view.map(_+1).map(_+2).filter(_>4) //> res0: scala.collection.SeqView[Int,Seq[_]] = SeqViewMMF(...)

Zipy i Unzipy

val numery=List.range(13, 27)             //> numery  : List[Int] = List(13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  26)
numery.zipWithIndex                       //> res0: List[(Int, Int)] = List((13,0), (14,1), (15,2), (16,3), (17,4), (18,5),
                                                  //|  (19,6), (20,7), (21,8), (22,9), (23,10), (24,11), (25,12), (26,13))                                                 
 
 for{
  (e,i) <- numery.zipWithIndex
  } yield s""" """
                                                  //> res1: List[String] = List( ,  ,  ,  ,  ,  ,  ,
                                                  //|   ,  ,  
                                                  //| ,  ,  ,  ,  )
zadanie : stworzyć listę od 0 do 10 i usunąć co trzeci element
val lista=List.range(1,10)
lista.zipWithIndex.filter{case (_,i) => (i+1)%3!=0}.map(_._1)

Mutable i Immutable

val numery=List(1,2,3,4)                  //> numery  : List[Int] = List(1, 2, 3, 4)
 
 numery.+:(5)                              //> res0: List[Int] = List(5, 1, 2, 3, 4)
 numery.+:(5)                              //> res1: List[Int] = List(5, 1, 2, 3, 4)

 
  import scala.collection.mutable.ListBuffer
  val numeryBuffer=ListBuffer(1,2,3,4)            //> numeryBuffer  : scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 
                                                  //| 3, 4)
  
  numeryBuffer += 5                               //> res2: poligon.poligon.numeryBuffer.type = ListBuffer(1, 2, 3, 4, 5)
  numeryBuffer += 5                               //> res3: poligon.poligon.numeryBuffer.type = ListBuffer(1, 2, 3, 4, 5, 5) 

For

val numbers=Vector(1,2,3,4)
val letters=Vector('a','b','c','d')

for{
  n<-numbers
  l<-letters
 } yield (n,l)

val names = Map("firstname" -> "Roman", "lastname" -> "Kowalski")

for ((k, v) <- names) println(s"key : ${k} , name : ${v}")
eksperymenty :
numbers.map(n=>
    letters.map(e=>s"($n,$e)")
   )                                              //> res1: scala.collection.immutable.Vector[scala.collection.immutable.Vector[St
                                                  //| ring]] = Vector(Vector((1,a), (1,b), (1,c), (1,d)), Vector((2,a), (2,b), (2,
                                                  //| c), (2,d)), Vector((3,a), (3,b), (3,c), (3,d)), Vector((4,a), (4,b), (4,c), 
                                                  //| (4,d)))

   numbers.flatMap(n=>
    letters.map(e=>s"($n,$e)")
   )                                              //> res1: scala.collection.immutable.Vector[String] = Vector((1,a), (1,b), (1,c)
                                                  //| , (1,d), (2,a), (2,b), (2,c), (2,d), (3,a), (3,b), (3,c), (3,d), (4,a), (4,b
                                                  //| ), (4,c), (4,d))