czwartek, 28 lipca 2016

Scala 2.12 - laborki

Scala 2.12 ma wyjść jakoś już zaraz niedługo niebawem i jak tu nie lepiej posprawdzać co nas czeka jak nie pokompilować sobie próbek kodu. Tak jak na laborkach w technikum. Tyle, że tam sprawozdanie było od razu gotowe, wnioski spisane i w zasadzie mierzyło się do czasu aż wykres namalował się zgodnie z oczekiwaniem. A tak to w zasadzie podobnie.

Sprawdzimy faktycznie co się dzieje kompilując sobie scalę 2.12-M5 , którą to wersję można sobie ściągnąć tutaj --> http://www.scala-lang.org/download/2.12.0-M5.html oraz javap i innych takich narzędzi do roz-kompilowywania kodu.

Świat po kompilacji - Prosta Klasa z Funkcją

Według zapowiedzi kod ma być kompilowany do BAJTkodu javy 8 czyli funkcje już nie powinny zamieniać się w klasy anonimowe.

Mamy taki prosty kod w Scali

class Lib{
  val fun:Int=>Int=_+1
}

Scala 2.11

Po kompilacji mamy takie oto wygenerowane pliki.Uwagę zapewne przyciąga to takie długie coś - o tym za chwilkę

ls
Lib$$anonfun$1.class  Lib.class  lib.scala

Najpierw : Lib.class - tutaj w sumie nic nadzwyczajnego.
javap -private  Lib.class 
Compiled from "lib.scala"
public class Lib {
  private final scala.Function1<java.lang.Object, java.lang.Object> fun;
  public scala.Function1<java.lang.Object, java.lang.Object> fun();
  public Lib();
}



Scala 2.11 kompiluje się do bytecodu Javy 6 a jako, że w Javie 6 lambd nie ma to mamy dodatkowa tę oto wcześniej wspomnianą dziwną klasę

Lib$$anonfun$1.class
javap Lib\$\$anonfun\$1.class 
Compiled from "lib.scala"
public final class Lib$$anonfun$1 extends scala.runtime.AbstractFunction1$mcII$sp implements scala.Serializable {
  public static final long serialVersionUID;
  public final int apply(int);
  public int apply$mcII$sp(int);
  public final java.lang.Object apply(java.lang.Object);
  public Lib$$anonfun$1(Lib);
}

Przykład jest prosty i dostaliśmy tylko jedną anonimową klasę ale jak polecimy z nim trochę dalej i trochę mocniej

class Lib{
  val fun:Int=>Int=_+1
  val fun2:Int=>Int=_+1
  val fun3:Int=>Int=_+1
  val fun4:Int=>Int=_+1
  val fun5:String => BigDecimal = s => BigDecimal(s)
}

To generuje się tego wincyj i wincyj

ls
Lib$$anonfun$1.class  Lib$$anonfun$2.class  Lib$$anonfun$3.class  
Lib$$anonfun$4.class  Lib$$anonfun$5.class  Lib.class  lib.scala

A klasa bohaterka wygląda ostatecznie tak :

public class Lib {
  private final scala.Function1<java.lang.Object, java.lang.Object> fun;
  private final scala.Function1<java.lang.Object, java.lang.Object> fun2;
  private final scala.Function1<java.lang.Object, java.lang.Object> fun3;
  private final scala.Function1<java.lang.Object, java.lang.Object> fun4;
  private final scala.Function1<java.lang.String, scala.math.BigDecimal> fun5;
  public scala.Function1<java.lang.Object, java.lang.Object> fun();
  public scala.Function1<java.lang.Object, java.lang.Object> fun2();
  public scala.Function1<java.lang.Object, java.lang.Object> fun3();
  public scala.Function1<java.lang.Object, java.lang.Object> fun4();
  public scala.Function1<java.lang.String, scala.math.BigDecimal> fun5();
  public Lib();
}

No dobra ale to już pomału odchodzi w przeszłość bo oto (pamparampampam):

Scala 2.12

Klas anonimowych w 2.12 nima :

ls
Lib.class  lib.scala

Klasa z funkcjami wygląda podobnie do 2.11

public class Lib {
  public scala.Function1<java.lang.Object, java.lang.Object> fun();
  public scala.Function1<java.lang.Object, java.lang.Object> fun2();
  public scala.Function1<java.lang.Object, java.lang.Object> fun3();
  public scala.Function1<java.lang.Object, java.lang.Object> fun4();
  public scala.Function1<java.lang.String, scala.math.BigDecimal> fun5();
  public static final int $anonfun$fun$1(int);
  public static final int $anonfun$fun2$1(int);
  public static final int $anonfun$fun3$1(int);
  public static final int $anonfun$fun4$1(int);
  public static final scala.math.BigDecimal $anonfun$fun5$1(java.lang.String);
  public Lib();
}

I to wszystko! Jak to możliwe zapytacie!?! Ano

javap -c Lib.class

public Lib();
    Code:
       0: aload_0
       1: invokespecial #67                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: invokedynamic #87,  0             // InvokeDynamic #0:apply$mcII$sp:()Lscala/runtime/java8/JFunction1$mcII$sp;
      10: putfield      #24                 // Field fun:Lscala/Function1;
      13: aload_0
      14: invokedynamic #91,  0             // InvokeDynamic #1:apply$mcII$sp:()Lscala/runtime/java8/JFunction1$mcII$sp;
      19: putfield      #28                 // Field fun2:Lscala/Function1;
      22: aload_0
      23: invokedynamic #95,  0             // InvokeDynamic #2:apply$mcII$sp:()Lscala/runtime/java8/JFunction1$mcII$sp;
      28: putfield      #30                 // Field fun3:Lscala/Function1;

Ten byte kod należy czytać "costam costam InvokeDynamic costam costam" czyli oto lambdy z java 8 właśnie widzimy. No czyli jest ładnie i działa i nie ma sensu się dalej rozpisywać bo jest ładnie i działa.

Traity i interfejsy

Od Javy 8 interfejsy mogą mieć metody statyczne oraz domyślne implementacje zwykłych metod. W ciekawym artykule o Scali 2.12 http://www.scala-lang.org/blog/2016/07/08/trait-method-performance.html możemy wyczytać, że :

"2.12.0-M5: trait method bodies are emitted in static methods in the interface classfile. The default methods forward to the static methods."

Kod wyjściowy w Scali

trait A{
  def m()
}

trait B{
  def m()

  def m2(arg:Int) :Int = arg+20
}

trait C{

  val field=15

  def m()
  def m2(arg:Int) :Int = arg+field
}

Scala 2.11

Poza traitem A, który ma tylko deklaracje metody - resztę traitów nie da się przedstawić przy pomocy zwykłego interfejsu z Javy6 dlatego też generowane są dodatkowe klasy z implementacjami metod

ls
A.class  B.class  B$class.class  C.class  C$class.class  lib.scala

Scala 2.12

Tutaj wygenerowanych plików jest mniej właśnie dzięki nowym patentom w interfejsie Javy8

ls
A.class  B.class  C.class  lib.scala

I nawet to C z definicją wartości w polu traitu da się przedstawić jako interfejs w Javie8

javap  C.class 
Compiled from "lib.scala"
public interface C {
  public abstract void C$_setter_$field_$eq(int);
  public abstract int field();
  public abstract void m();
  public static int m2$(C, int);
  public int m2(int);
  public static void $init$(C);
}


Wołanie javy 8

Scala Lambdy Jako Java Lambdy

wywołajmy sobie Streamy z Javy8

object Main extends App{
java.util.Arrays.asList(1,2,3,4,5).stream().map(e=>e+1).forEach(i=>println(s"INFO : $i"))
}


I wynik

scala2.12 lib.scala 
INFO : 2
INFO : 3
INFO : 4
INFO : 5
INFO : 6



Czyli poooszło ładnie!

Funkcje Scali zamiast Lambd

object Main extends App{
  val increment:Int=>Int=e=>e+1
  val display: Int=>Unit = i=>println(s"INFO : $i")

  java.util.Arrays.asList(1,2,3,4,5).stream().map(increment).forEach(display)
}



To niestety nie zadziałało. Być może trzeba jakieś konwersje zaimportować.

scala2.12 lib.scala 
/home/pawel/projects/scalaeksperymenty/212/212/lib.scala:14: error: type mismatch;
 found   : Int => Int
 required: java.util.function.Function[_ >: Int, _]
  java.util.Arrays.asList(1,2,3,4,5).stream().map(increment).forEach(display)
                                                  ^
one error found


Także ogólnie jeśli nie będzie żadnego globalnego kataklizmu to przyszłość programowania zapowiada się wspaniale.

środa, 6 lipca 2016

Einstellung Effect

"The difficulty lies, not in the new ideas, but in escaping from the old ones, which ramify ... into every corner of our minds."

To był taki krótki, fajny i wymowny cytat na wstęp a teraz jeszcze jeden dłuższy ale równie fajny i przepełniony niebagatelną informacją :

The Einstellung Effect refers to the human tendency to repeat a known solution, even if it no longer is the optimium solution. It represents the negative value of experience. The Einstellung Effect kicks in when new solutions have developed or become available but we do not look for them or adopt them even if we know of them. This can happen because we believe we already know the best solution, hence do not fully evaluate the new ways. Or simply because we are set in our ways and do not wish to undertake the “pain” of learning new ways unless forced to by others or market events.

Jeszcze metaforyczny rysunek i można zaczynać.

"Don't be blind,"

Na wzmianka o tym efekcie natrafiłem w fajnym kursie o tym "jak się uczyć" - Learning How to Learn, który jest w tej chwili dostępny na courserze. Efekt ten jest tam jest przedstawiony jako jedna z większych przeszkód w nabieraniu nowych umiejętności - no bo jeśli wciąż powtarzamy te same schematy to trudno zaadoptować się do nowych podejść.

Żeby było bardziej naukowo wspomnimy o jednym z eksperymentów, który zademonstrował ów efekt. Generalnie była to scena ze szklanej pułapki 3 gdzie trzeba było odpowiednio przelewać wodę z jednego pojemnika do drugiego i do trzeciego.



Były dwie serie ćwiczeń gdzie jedna grupa rozwiązywała tylko drugą serię, w której istniało szybkie rozwiązywanie "równania" z użyciem tylko 2 z 3 zbiorników lub dłuższe z użyciem wszystkich trzech. Ta grupa bez problemów znalazła efektywne rozwiązanie z dwoma zbiornikami.

Druga grupa wcześniej robiła jeszcze inne ćwiczenie gdzie trzeba zawsze było użyć wszystkich trzech zbiorników i oni niestety w drugiej części eksperymentu zawsze używali wszystkich trzech zbiorników chociaż prostsze rozwiązanie z dwoma zbiornikami było w zasięgu reki.

Więcej info i dokładniejszy opis znajduje się w linku wikipedi poniżej - jest tam również wzmianka, iż uwaga od prowadzącego "nie bądźcie ślepi" pomagała znacznej ilość uczestników z grupy "z doświadczeniem" znajdować bardziej efektywne rozwiązanie.

Na końcu będzie więcej linków ale pewnie i tak nikt ich nie przeczyta także tutaj jest dobre miejsce na jeden najbardziej wyczerpujący :


I Kilka fajnych zeń cytatów bo jak mówiłem pewnie i tak nikt tych linków czytać nie będzie :

"When people test a theory, they look for evidence that is consistent with what they already believe rather than objectively assessing any evidence even if it might disconfirm their previously held belief"

"Einstellung refers to a person's predisposition to solve a given problem in a specific manner even though better or more appropriate methods of solving the problem exist. The Einstellung effect is the negative effect of previous experience when solving new problems. "

"The most famous example (...) is the Luchins' water jar experiment, in which subjects were asked to solve a series of water jar problems. After solving many problems which had the same solution, subjects applied the same solution to later problems even though a simpler solution existed"

ObjectAbstractFactoryBean

(a aktor nazywa się Sean Bean - czajcie jaki zajebisty się dowcip udał!!!)

Taka sytuacja - ktoś robi w Javie 6 od 5 lat. Ma masę przyzwyczajeń i wytrenowany mózg przez konkretną sytuację. Narysujmy sobie jak to może wyglądać koncepcyjnie na poziomie neuronów. Oczywiście słowo "koncepcyjnie" jest tu ważne i użyłem go po to aby uratować się od potrzeby dokładnego trzymania się jak te rzeczy wyglądają w rzeczywistości bo tego sam nie wiem - ale koncepcyjnie może wyglądać to tak :

I te trzy kuleczki mogą być siecią neuronową* (* - czytałem ostatnio, że nie neurony są ważne a komórki gleiste - czy jakoś tak - ale to wszystko co tutaj opisuję jest "koncepcyjne" także bez znaczenia), która została zbudowana przez wspomniane 5 lat programowania w Javie. I teraz Np. mamy sytuację, że trzeba zaimplementować zakup w sklepie. Siec neuronowa od razu podsunie ścieżkę rozwiązania "jak nazwać ten obiekt" - bo oczywiście TO musi być obiekt. A jak do tego używa się FRAMEWORKA to musi to być (Sean) Bean.

Jak z poziomu rozwiązywania problemów przejdziemy do bardziej ogólnego problemy nauki to zauważymy, EINsztellung stwarza wiele problemów bo mamy relatywnie ograniczone możliwości łączenia się nowych neuronów ze starą strukturą. I tak np. w konkretnym przykładzie uczymy się nowej koncepcji jaką jest Stream w Javie8 oraz jego zastosowania. Ponieważ nasz "arsenał poznawczy" składa się głównie z "Seana Beana Framework, Hibernate i Obiektów" to w ich kontekście będziemy definiować nowe pojęcie.

połączenie (koncepcyjne) jest jedno i jest słabe. Jest jedno bo mamy jedną (obiektowo-beanową) wizję świata. Tutaj pojawiają się takie koncepcje jak pamięć krótkotrwała, długotrwała itd ale w uproszczeniu z punktu widzenia nauki to jedno słabe połączenie może szybko zaniknąć przez co zapomnimy jak tego Streama używać i nauka pójdzie w las.A z punktu widzenia zastosowania praktycznego widzimy Stream jako nową "Kolekcję Obiektów" i tylko w tym kontekście nasz mózg będzie generował rozwiązania. Widziałem już kilka prezentacji ludzi z Oracla gdzie napominają by traktować Streamy jako "Strumienie wartości" a nie "Kolekcje obiektów".

Teraz bliżej przyjrzymy się tym trzem zielonym neuronom gdyż tworzą one pewien spójny zbiór uzupełniających się informacji - taki zbiór pojęciowy w naszej głowie nazywamy Chunkiem.

Chunk

Czym jest chunk : https://www.quora.com/What-is-chunk-or-chunking-in-learning

Chunk - Zbiór informacji, który tworzy jakąś pojęciową całość. Nasze koncepcyjne neurony wyhodowane przez 5 lat Javy stworzyły takiego właśnie pięknego Chunka. Obiekt,Klasa,Enkapsulacja,Getter,DAO - to wszystko w ramach tego chunka jest spójne i gotowe do obrony swoich granic (co często widać na forach). I teraz pytanie co jeśli mam inne chunki do których TAKŻE mogę odnieść nowe informacje? Czy to dobrze czy źle? Czy to pomoże czy przez zamieszanie przeszkodzi?

Z tego jak zrozumiałem ten cały mechanizm to jeśli jakąś nową koncepcję można połączyć z dwoma lub więcej niezależnymi chunkami to bardzo bardzo dobrze!. Raz, że powstaje więcej połączeń od nowej idei (ta niebieska kuleczka) a dwa, że następuje połączenie pomiędzy dwoma chunkami jako takimi, które były niezależne ponieważ okazuje się, że maja teraz ze sobą coś wspólnego. I wracając do przykładu ze Stream'ami - można je jednocześnie widzieć jako leniwa kolekcję obiektów jak i z drugiej strony jako abstrakcyjną formułę danych (w większości języków funkcyjnych podawany jest przykład z obliczeniem ciągu Fibonacciego przy pomocy streamów, które po prostu opisują w sposób jasny formułę obliczeń kolejnych elementów i to od użytkownika zależy gdzie w tej formule się zatrzyma). I teraz te koncepcje mogą zacząć się ze sobą łączyć. Czy Stream obiektów to Stream danych? Co musi się stać by obiekty nazwać Danymi? Same takie rozważania będą efektywnie utrwalać informacje w pamięci a do tego rozszerzać pole potencjalnych interpretacji nowych wiadomości.

Inny bardzo ciekawy przykład. Z mojego doświadczenia programiści C# często dużo szybciej łapią scalę niż programiści maintenance Javy 7,6,5. W C# lambdy mieli od dawna. Jak pokazywałem flatMapy to często słyszałem "a tak tak, u nas jest takie LINQ bardzo podobne do tego". Tymczasem gdy programista Javy widzi po raz pierwszy w życiu jak metoda przyjmuje funkcje i zwraca funkcje to czasami mu pęka mózg (w sensie głowa robi się bardzo czerwona i tak drga nerwowo dolna warga czyli chyba jakiś wylew)

W kontekście nauki po raz kolejny dobra wydaje się formuła CR (CR to Code Retreat a nie Chrystian Ronaldo) gdzie po prostu zabrania się rozwiązywania zadań w pewien sposób aby uczestnicy poznali przez to nowe sposoby podejść. I nie jest to prosty process bo nie raz uczestnicy fakt - że nie zdążyli z implementacją - odbierali bardzo osobiście i po to usuwa się ten kod aby ludzie odczepili ego od implementacji.

Aby nie było tak bardzo abstrakcyjnie poniżej kilka pomysłów na rozbudowę niezależnych programistycznych chunków

"Rozbuduj swą sieć z paleta języków"

Podobnie do Code Retreat niektóre języki usuwając pewne mechanizmy nadają ciekawe ograniczenia, które mogą nas zmusić do szukania nowych podejść.

Ocaml

Niedługo startuje kurs online: Ocaml Mooc. Jest to druga sesja i z tego co zrozumiałem z pierwszej to w Ocaml mamy do czynienia z pewnym negatywem Javy.

Koncepcyjnie w OCamlu kod wygląda tak

kod immutable
[DANGER DANGER DANGER ZONE]
kod mutable
[KONIEC DANGER ZONE]


W Javie w zasadzie zmiana wartości wszystkiego dookoła (nawet enkapsulowanego) jest (niekwestionowaną przez niektórych) codziennością także to by bardziej wyglądało w ten sposób :
[DANGER ZONE]
poczatek programu
[SAFE ZONE]
Zaimplementowane 15 mechanizmów z "Effective Java"
[KONIEC SAFE ZONE]
koniec programu
[DANGER ZONE]

No i jest zabawa "zaimplementuj rozwiązanie w sytuacji gdy kosztowne rytuały w kodzie wiążą się z mutowalnością".

Go

Podobno nie ma generyków. Jest to bardzo ciekawe dlaczego w 2016 jedna z przodujących firm IT na świecie reklamuje język bez generyków. Czy programowanie Go wygląda jak programowanie w Javie 4? A może poza javą ta koncepcja sprawdziła się lepiej?

Kotlin

Żeby nie było, że tylko programiści Javy mogą się czegoś nauczyć z innych języków. Jak chcesz w Scali pokazać na poziomie typów, że czegoś może nie być to Option,Option,Option... które i tak samo może być nullem. Tymczasem Kotlin ma bardzo wygodne zabezpieczenie przed nullem na poziomie typów bez wchodzenia w Monady i inne takie tam.

Haskell

Tu nima obiektów i wielu rzeczy nima - Ultimate Code Retreat dla programistów Javy.

Na jesieni ma być kurs : https://www.futurelearn.com/courses/functional-programming-haskell

myślenie lateralne

Przy okazji tego artykułu można poruszyć ten temat. Autorem koncepcji jest Bono ale nie ten z U2 bo Edward de Bono. Generalnie tutaj kompletnie zrywamy z rutyną mechanicznego myślenia. Np:

  • Jest problem z deploymentem
  • Jak byś go rozwiązał przy pomocy gabki, mikrofali i wody? (dokładnie tak)
  • Gabka coś wchłania i potem to gdzies można wycisnąć - może coś rozlazłego da się zebrać w kupę i przenieść w inne miejsce.
  • Mikrofala zwieksza temeperaturę i wysyła fale, zmienia naturę tego co w środku. Może kawałek aplikacji przenieść w miejsce gdzie będzie lepiej monitorowane
  • Woda zmywa brud. Może warto niepotrzebną cześć aplikacji usunąć przed deploymentem?
  • itd.
https://pl.wikipedia.org/wiki/My%C5%9Blenie_lateralne

Linki