środa, 14 września 2016

Załamanie czasoprzestrzeni na styku paradygmatów - currying spotyka dziedziczenie

Inspiracją dla tego wpisu jest kilka pierwszych stron niezwykle ciekawego artykułu, który pojawił się w 1990 roku a tytuł jego: Why Functional Programming Matters. Jest to tez praca bardzo praktyczna bo wyśmiewa popularne podejście w dyskusjach, że "X jest lepsze bo nie można w nim robić Y" - "Programowanie funkcyjne jest lepsze bo nie ma przypisań" itd.

It is a logical impossibility to make a language more powerful by omitting features, no matter how bad they may be

Autor natomiast odwołuje się do bardzo ogólnych zasad tworzenia kodu, który dzięki odpowiedniej modularyzacji umożliwia łatwe komponowanie nowych konstrukcji z tychże modułów. Jest to rodzaj kleju, który zlepia kawałki kodu tak, że łatwo tworzyć nowe programy bez rozlewania własnego kleju w postaci dziesiątek pozagnieżdzanych ifów, które drutują dwa moduły ze sobą.

I w zasadzie cały dokument pokazuje na przykładach jak funkcje wyższego rzędu oraz "przetwarzanie leniwe" znacznie ułatwiają kompozycję kawałków kodu bez wspomnianego zewnętrznego kleju. Nie ma sensu tutaj powtarzać wszystkie bo dokument jest dostępny na necie i ma 20coś stron. Dlatego skupimy się na jednej ciekawostce. Co się dzieje gdy spróbujemy przemycić trochę obiektowości Scali do przykładów, które prezentują podejście czysto funkcyjne? Otóż dzieje się ciekawie.

Currying i foldr - dwa bratanki

Albo się zna foldr i nie trzeba tłumaczyć jak to działa albo się nie zna i trzeba doczytać - ale nie tutaj tylko trzeba podążać za strzałką ---> http://perugini.cps.udayton.edu/teaching/books/PL/www/lecture_notes/currying.html . Rysunek jest tez pożyczony z tej strony także może ktoś szybko wizualnie ogarnie.

jak już foldr ogarnięty to najpierw zerkniemy na Haskella bo wspomniany we wstępnie dokument opisuje przykłady w haskellu. Chyba. Bo to jest pismo z 1990 a wtedy z komputerów to były chyba tylko te rosyjskie jajeczka na rynku dostępne.

W każdym razie w samym foldr jedzie się od prawej (dlatego foldr ight) i zwija listę według przepisu podanego w funkcji dwuelementowej.

foldr (\x y->x+y) 0 [1,2,3,4,5]
--15

I własnie na tym polega bajer bo nie ma czegoś jak "funkcja dwuargumentowa". W każdym razie nie ma w Haskellu i chyba w takim czysto funkcyjnym programowaniu też nie ma. Nie jest to jakaś wielka strata bo łatwo można ten sam efekt uzyskać zwracając funkcję jednoelementową, która przyjmie drugi argument. To podejście zagościło już w tytule tej sekcji i nazywa się : currying (i też do doczytania)

I napisane w ten sposób dodawanie będzie wyglądać tak :

add :: Int->Int->Int
add a b = a + b

I nie jest to już tylko "taka ciekawostka" ale zastosowanie tego mechanizmu umożliwia nam zupełnie nowa formę kompozycji dwóch kawałków kodu! No bo zoba :

-- dla wszystkich "Foldable"
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
--i prosciej dla listy
foldr :: (a -> b -> b) -> b -> [a] -> b

To jest sygnatura "foldr" ze standardowej biblioteki Haskella. I cały bajer polega na tej funkcji "dwuargumentowej" , która jest typu a->b->b a zwijana tablica jest typu [a] w związku z tym mogę sobie dowolnie komponować funkcje, które działają na elementach tablicy a->a. W praktyce oznacza to zajebistą, wręcz epicką kompozycję kawałeczków kodu bo mając :

timesTwo :: Int -> Int
timesTwo x = 2 * x

addOne :: Int -> Int
addOne x = x + 1

toString :: Int -> String
toString i = show i

joinString :: String->String->String
joinString s1 s2 = s1 ++ "~" ++s2

Można teraz przetwarzać dowolnie elementy przed zwinięciem :

*Learn> foldr joinString "END" [1,2,3,4,5] 

<interactive>:38:25:
    No instance for (Num String) arising from the literal ‘1’
    ...
-- i szybko naprawiamy konwersją do string
*Learn> foldr (joinString . toString  ) "END" [1,2,3,4,5] 
"1~2~3~4~5~END"

-- a dalej jak tylko typy się zgadzają można łączyć i w lewo i w prawo

*Learn> foldr (joinString . toString . addOne  ) "END" [1,2,3,4,5] 
"2~3~4~5~6~END"
*Learn> foldr (joinString . toString . addOne . timesTwo  ) "END" [1,2,3,4,5] 
"3~5~7~9~11~END"
*Learn> foldr (joinString . toString .  timesTwo  ) "END" [1,2,3,4,5] 
"2~4~6~8~10~END"
*Learn> foldr (joinString . toString .  timesTwo . addOne ) "END" [1,2,3,4,5] 
"4~6~8~10~12~END"

Z foldLeft czy po haskellowemu foldl tak łatwo się chyba nie da bo tam zwijanie idzie od drugiej strony b->a->b

Funkcyjnie ale trochę niefunkcyjnie

A jak to wyjdzie w Scali? Też jest foldRight i jak przystało na język, który ma "wszystko", foldRight jest funkcją wyższego rzędu "na obiekcie". Jest ona funkcją wyższego rzędu, która przyjmują funkcja, która nie jest funkcją bo ma dwa parametry i zaczyna się robić ciekawie.

scala> :t List(1,2,3).foldRight("End") _
((Int, String) => String) => String

W scali Funkcja dwuargumentowa to oczywiście nic innego jak instancja klasy Function2 czyli funkcja jest obiektem i faktycznie jest bardzo hybrydowo. Problem z Function2 jest taki, że tego nie da się łatwo skomponować z inną funkcją bo "andThen" i "compose" są jedynie na Function1, która jak na funkcje przystało jest klasą.

Mamy jednakże do dyspozycji pewien most bo Function2 dostarcza nam metody na obiekcie funkcji (w końcu programowanie funkcyjne) do zmiany wersji dwuargumentowej w "currying" , "skuringowaną" funkcję jednoelementową (czy jak to tam nazwać)

 /** Creates a curried version of this function.
   *
   *  @return   a function `f` such that `f(x1)(x2) == apply(x1, x2)`
   */
  @annotation.unspecialized def curried: T1 => T2 => R = {
    (x1: T1) => (x2: T2) => apply(x1, x2)
  }

I dzięki temu możemy powrócić do świata jedno-argumentowych funkcji dzięki czemu kompozycja znów działa

val joinString:(String,String)=>String=(s1,s2)=>s1+"~"+s2
val asString:Int=>String= _.toString

//joinString compose toString  -- to nie pomalujesz to je Function2

joinString.curried  //String => (String => String) = <function1>

val convertAndJoin=joinString.curried compose asString //Int => (String => String) = <function1>

I prawie jest fajnie ale niestety foldRight w Scali wygląda tak : def foldRight[B](z: B)(op: (A, B) => B): B - czyli znowu trzeba wrócić od wersji jedno-argumentowej do dwuargumentowej. I żeby zachować spójność to tym razem dla odmiany mamy funkcję w "companion object" Function.uncurried

//List(1,2,3,4,5).foldRight("END")(convertAndJoin)  //tez sie nie kompiluje bo jednoelementowa


List(1,2,3,4,5).foldRight("END")(Function.uncurried(convertAndJoin)) 
//res1: String = 1~2~3~4~5~END

No i z odrobiną dodatkowego kleju (ale kleju już gotowego w bibliotece standardowej) możemy uzyskać podobną kompozycję jak ta omawiana we wspomnianym na początku dokumencie

List(1,2,3,4,5).foldRight("END")(uncurried(convertAndJoin compose addOne))
//res2: String = 2~3~4~5~6~END

List(1,2,3,4,5).foldRight("END")(uncurried(convertAndJoin compose addOne compose timesTwo))
//res3: String = 3~5~7~9~11~END

List(1,2,3,4,5).foldRight("END")(uncurried(convertAndJoin compose timesTwo compose addOne))
//res4: String = 4~6~8~10~12~END

Czyli w sumie spoko, trzeba trochę konwersji porobić ale da się ładnie funkcje w foldzie komponować tak? Taki chu... znaczy taka ciekawostka... teraz taka ciekawostka będzie... bo zobaczymy co się dzieje gdy następuje kolizja paradygmatów.

Taka ciekawostka

Ciekawie zaczyna się dziać kiedy dorzuci my do równania tzw. "typy wyższego rzędu" czyli np Listę.

val append:Int=>List[Int]=>List[Int]= i => l=> i ::l

Cały czas spelnia to warunek dla foldr a->b->b gdzie a::Int a b::List[Int] . No czyli tylko trzeba trzasnąć uncurring i działamy tak? To byłoby zbyt proste ponieważ w wyniku wcale nie otrzymamy funkcji dwuargumentowej

Function.uncurried(append)
//res5: (Int, List[Int], Int) => Int = <function3>

Czyżby jakiś BUG? Nie Nie, nic z tych rzeczy - to jak najbardziej FICZER!!! Otóż jak na dobry hybrydowy język przystało scala ma dziedziczenie - a szczególnie w przypadku List oj mamy dużo tego dziedziczenia... I tak List[A] rozszerza Seq[A] , które rozszerza PartialFunction[Int,A] co ostatecznie rozszerza Int=>A

No i ostatecznie List[Int] jest typu Int=>Int czyli nasza funkcja o sygnaturze Int=>List[Int]=>List[Int] może być przekazana tam gdzie spodziewany jest typ Int=>List[Int]=>Int=>Int

Do tego dodajmy kolejny obiektowy super mechanizm do zaciemniania co się naprawdę dzieje zwany "method overloading" i już mamy rozwiązanie tajemnicy

 /** Uncurrying for functions of arity 2. This transforms a unary function
   *  returning another unary function into a function of arity 2.
   */
  def uncurried[a1, a2, b](f: a1 => a2 => b): (a1, a2) => b = {
    (x1, x2) => f(x1)(x2)
  }

  /** Uncurrying for functions of arity 3.
   */
  def uncurried[a1, a2, a3, b](f: a1 => a2 => a3 => b): (a1, a2, a3) => b = {
    (x1, x2, x3) => f(x1)(x2)(x3)
  }

Były takie zasady overloadingu na certyfikat SCJP i zdaje się, że wybierana była bardziej specjalizowana metoda - no cóż sytuacja gdzie jeden typ będący częścią typu funkcji rozszerza kolejną funkcje wydaje zajebistym tematem na kolejne super praktyczne zadania na rozmowie kwalifikacyjnej (zaraz po klasycznym left-joinie i "jak zbudowana jest hashmapa") : #HurraHurraFPplusOOP

Wspomniany efekt dosyć fajnie opisany jest tutaj w tymże wątku : Jakiś wątek na forum z 2015

Największe gunwo

Największe gówno jakie widziałem w scali wygląda niepozornie bardzo :

List(1,2,2,3).toSet()
Ale co tutaj jest dziwnego? "toSet()" na liście pewnie wyrzuci powtarzające się elementy i skończmy z Set(1,2,3). Otóż nie bo wynik tej operacji to... false. Otóż toSet() na liście zwróci false -hmmm czy ta metoda nie powinna nazywać się "isSet()" ? kurwa. Nie w tym rzecz bo tam są tak naprawdę dwie metody.

//co się dzieje
val set: Set[Any] =List(1,2,2,3).toSet
set.apply(())
set.apply ()
set apply ()
set()
//teraz zastap set wyrazeniem
List(1,2,2,3).toSet.apply(())
List(1,2,2,3).toSet.apply ()
List(1,2,2,3).toSet apply ()
List(1,2,2,3).toSet ()
List(1,2,2,3).toSet()

Dokładne wytłumaczenie jest w scala-puzzlersie : http://scalapuzzlers.com/#pzzlr-040 . To jest zło. Była kiedyś taka gra "Świątynia pierwotnego zła" - to była gra gdzie magiem i druidką walczyło się własnie z takimi konstrukcjami kodu.



środa, 31 sierpnia 2016

Program jako funkcja całkowita - czyli kiedy naprawdę nie ma błędów

"Litr czystej poproszę" - to zdanie wypowiedziane w całodobowym sklepie branżowym niesie ze sobą wystarczająca ilość informacji aby było wiadomo "o co chodzi". A wiadomo o co chodzi gdyż kontekst już definiuje pewne pojęcia jak to, że chodzi o napój w tym sklepie, że ma się to odbyć teraz i że popłynie hajs. Te rzeczy są oczywiste.

Wynik 2+2=4 także jest oczywisty. Jeśli zapytamy o rezultat porównania 2 + 2 == 4 to czytelnik o ponadprzeciętnej podejrzliwości może szukać podstępu w definicji operatora + (tak, w niektórych językach można go zdefiniować jak się chce) albo widzi spisek w tym jak operator == porównuje wartości, a może jest tu jakiś "Boxing" a może porównuje referencje? Raczej mało komu przyjdzie do głowy, że może mechanika działa prawidłowo ale 2+2 to wcale nie 4? eeee ... że co? Brzmi to z pozoru absurdalnie ale jednak są ludzie, którym oczywistość nie wystarcza i wyprowadzili wcale niebanalny dowód że 2+2 to faktycznie 4

Teraz inna sytuacja - słyszysz zdanie : "W moim programie jest Błąd" a jeśli ma być światowo to "W moim programie jest Bug" i zazwyczaj rzeczą, na której koncentruje się sytuacja jest ten Bug. Raczej nie usłyszysz w odpowiedzi "W twoim programie jest Bug, a jak zdefiniowałeś pojęcie programu?" albo "zdefiniuj warunki wymagane do stwierdzenia jest Błąd" czy wyjątek to błąd , czy sam fakt, że może polecieć wyjątek ale nie poleciał to błąd? Czy jeśli nie leci wyjątek a jest zły rezultat to jest to błąd? No i w końcu czy jeśli wszystko działa to w programie jest błąd czy go nie ma?

Po tym dowodzie na dwie strony, że 2+2=4 ostatnie zdanie nie powinno być takie oczywiste - Czy jeśli program działa i zwraca poprawny wynik to jest w nim błąd?.Zagłębmy się w ten ciekawy problem i zdefiniujmy "Program" i "Błąd".

Korzenie

Powyższy obrazek jest skopiowany ze strony na wikipedii : Human Computer. Bo oto okazuje się, że komputery istniały jeszcze zanim powstały ...komputery.

The term "computer", in use from the early 17th century (the first known written reference dates from 1613), meant "one who computes": a person performing mathematical calculations, before electronic computers became commercially available.

Ha! Czyli nie dość, że trzeba zdefiniować co to jest program to jeszcze nie jest jasne co to jets ten "komputer". I teraz sformułowanie "Babcia kupiła mi na komunie komputer" może nawet zakończyć się sprawą w sądzie na skutek uprawiania niezgodnego z prawem niewolnictwa!

Ciekawy jest fragment o tym co oni robili "a person performing mathematical calculations". Pewnie nie jeden profesorek rzuci teraz we mnie cyrklem ale teoretycznie coś takiego :


fun czyProstokątny(a:Integer,b:Integer,c:Integer):Boolean = pow(a,2)+pow(b,2) == pow(c,2)

to też jest jakieś tam "mathematical calculations" i przy założeniu, że "a" i "b" to przyprostokątne a "c" to zawsze przeciwprostokątna to ta funkcja sprawdza czy trójkąt jest prostokątny. Ale czy sprawdza? Tego nie wiadomo. Bo co robi funkcja "pow"? Być może wydaje z głośnika na płycie głównej dźwięki "POWPOW" ?

Przydałoby się tutaj jakieś twierdzenie w stylu Dla każdego x należącego do zbioru liczb całkowitych pow(x,2)=x * x . Oczywiście można tak jechać dalej ze znaczkiem "*" ale tutaj założymy, że to mnożenie, które działa "dobrze" bez wnikania w definicję "dobrze" bo to jest tylko wstep do ciekawszych rzeczy.

Czy coś może pójść nie tak?

Wydaje się, że pierwszym problemem może być przekroczenie zakresu Integera. Jaki jest zakres Integera ? "Ja wiem!Ja wiem! W javadocu jest , że 2 31 - 1"

I to jest właśnie takie myślenie językiem programowania. Czasem niestety jednocześnie pierwszym i jedynym jaki dana osoba napotka w życiu. No bo zobacz - taki Haskell wydaje się mieć trochę na zakresy wyjebane :

Dowód na czytanie z pliku?

Ciekawie zaczyna się robić kiedy zaczniemy drążyć "skąd dokładnie brane jest a,b i c"? Niechże to będzie plik CSV z jedną linią by było prościej

3,4,5
I teraz np mamy dwie dodatkowe funkcje
fun czytaj(plik:File):String
fun parsuj(linia:String):(a:Integer,b:Integer,c:Integer)
Ba i nawet teraz można zrobić kompozycję bezpośrednio z pliku w wynik sprawdzania czy trójkąt jest prostokątny
czytaj andThen parsuj andThen czyProstokątny

- Łooołooołooołoo łooooo chwila chwila chwillllla a co jak się wyjebie?"
- ale co?
- no to czytanie z pliku?
- ale ... ale jak?
- no jak pliku nie będzie?
- no.. no.. to może załóżmy, że zawsze jest...

Wydaje mi się, że powyższy problem jest przedstawiony bardziej fachowo (i bez słowa "wyjebie" które może zgorszyć niejednego przedstawiciela zblazowanej klasy średniej) w poniższym abstrakcie.

Otóż takie teoretyczne rozważania rypią się gdy mamy do czynienia z czymś co dosyć trudno przedstawić "matematycznie" jak na przykład czytanie z bazy czy przesyłanie danych po sieci - no bo jak , no jak wygląda teoretycznie poprawna funkcja "zaciągnijLegalnieFilmZTorrenta". No i dopiero wyjście z tej sytuacji znalazł Eugenio Moggi a smaczku dodaje, że abstrakt pochodzi z jego pracy zatytułowanej Notions of computation and monads (moooooooooooooooonads!!!)

Definicja BUGa jako funkcji

Tak dla przypomnienia - funkcja całkowita to na przykład dodawanie bo co byśmy tam nie podali to i tak zadziała. Funkcja częściowa to dzielenie bo dla argumentu gdzie dzielimy przez zero nie jest ona określona. Jeśli zastosujemy dzielenie z założeniem, ze zadziała dla każdej liczby - wtedy mamy błąd.

funkcja FormularzRejestracyjny=>User jest określona dla każdego poprawnie wypełnionego formularza ale nieokreślona dla Formularza, który w polu wiek ma wartość "DaaaajKamienia"

funkcja Plik=>String jest określona jedynie dla zbioru poprawnie zdefiniowanych i plików.

funkcja IdZamowienia=>Zamowienie jest określona jedynie dla istniejących zamówień.

funkcja OdpytanieZewnętrznegoAPI => Wynik też jest częściowa bo określona tylko dla sytuacji kiedy otrzymamy jakiś wynik.

Nie wiem czy ktoś z was podchodizł do tego od tej strony - ja kiedyś nie. Zazwyczaj po prostu chodziło o obsługę wyjątków. Tymczasem na obsługę wyjątków można trochę tak patrzeć jak na taki zewnętrzny hak, który umożliwia nam używanie funkcji częściowych tak jakby one były całkowite. Czyli poza standardowym znaczeniem BUGa kiedy to funkcja zwraca po prostu zły wynik (f(a,b)=a+b+1 - funkcja czysta ale zjebana) dochodzi dodatkowy rodzaj Błędu.

Błąd - sytuacja w której, funkcja częściowa jest używana tak jakby była całkowita. Np takie coś

 sprawdzLinie:String=>Boolean = parsujLinie and Then czyProstokątny
można zdefiniować jako błąd gdyż funkcja parsujLinie w kontekście naszego przykładu jest zdefiniowana jedynie dla takiej formy gdzie w linii jesta,b,c i to nawet może przejść wszystkie testy i chodzić na produkcji 5 lat. Cały czas kwalifikuje się to jako błąd.

Jak sprawdzić czy funkcja jest całkowita? - scalacheck.

Wizualizacja funkcji częściowej. Czerwona Kulka może symbolizować zapytanie do bazy danych, które zakończyło się niepowodzeniem.

Kompozycje i efekty

Cały problem z wyjątkami polega na tym, że przeszkadzają one w kompozycji a kompozycja to "Reusage (Rejusedż)" czyli święta gral programowania. No bo jak mam funkcję String=>(Int,Int,Int), która mówi mi, że zwraca trzy elementowego Tupla (z polskeigo krotkę) a tak naprawde zwraca tego Tupla albo ch.. wie jaki wyjątek to ja nie moge sobie prosto tej funkcji skomponować z (Int,Int,Int) => Boolean bo na wejściu wcale nie muszą pojawić się trzy inty!!!

No dobra ponarzekalimy ale gdzie jest SOLUSZYN? Zamiast udawać, że funkcja częściowa to funkcja całkowita i try-catchem łapać pokemony może da rade zamienić jakoś te funkcję częściową w całkowitą? I to zaproponował właśnie Eugenio Moggi - chyba to zaproponował bo połowy jego pracy nie rozumiem ale poznaje rysunki, podobne do tych które są na stronie wiki o monadach (te takie ze strzałkami).

Klasycznie jest coś takiego :

val divide:(Int,Int)=>Int = (a,b) => a/b

try{
    divide(4,0)
}catch{
    case e:ArithmeticException => println("Kalwi & Remi - EXPLOSION")
}
I naprawdę ważne jest aby zrozumieć, że tutaj to try, jest takim zewnętrznym klejem który utrudnia kompozycję.

Dalej mamy dedykowany typ dla funkcji częściowej:

val partialDivide : PartialFunction[(Int,Int),Int] = {
    case (a,b) if b!=0 => a/b
}

println(partialDivide.isDefinedAt(4,0))  //false
Tutaj już plus jest taki, że nie udajemy, iż każdy argument ma jakiś wynik.

I teraz następuje zamiana funkcji częściowej na całkowitą

val totalDivide: ((Int, Int)) => Option[Int] =partialDivide.lift

println(totalDivide(4,0))  //None
println(totalDivide(4,2))  //Some(2)

"totalDivide" jest funkcją całkowitą i dla każdego argumentu zwraca ona rezultat. Tam gdzie dzielenie ma sens będzie to Some tam gdzie nie ma będzie None ale będzie! Może powstać pytanie ale co z tego jak w wyniku dostajemy jakiś "twór"? Ów twór jest tzw. Typem wyższego rzędu (po javowemu "klasa z generykiem") i obsługuje się go przy pomocy funkcji wyższego rzędu oraz pattern matchingu. Drugiego mechanizmu w Javie nie ma a pierwszy jest od niedawna dlatego w półswiatku javowym owe podejście może wydawać się bardzo egzotyczne.

Co z kompozycją? Jest cała teoria opisująca jak to się komponuje i ze zwykłymi funkcjami jak i z innymi tworami. Jedno popularne nazewnictwo używa na twory słów Funktor, Monada itd ale też spotkałem się z podejściem do tego jako do Efektu i np. Option to Efekt braku czegoś a Future Efekt czasu w obliczeniach.

Owy efekt spełnia pewne prawa i aby się przekonać, że na pewno je spełnia możemy użyć scalacheck. Ba! ScalaZ ma nawet gotowy zestaw testów Scalachecka by sprawdzić czy aby na pewno wiemy z czym mamy do czynienia : Functor Laws

No i jak nasz efekt spełnia prawa Funktora to możemy śmiało pisać z założeniem, Option(x).map(f).map(g) to to samo co Option(x).map(f andThen g)

Tyle w temacie

Celem tego wpisu nie jest brnięcie dalej ale własnie zatrzymanie się w tym miejscu. Temat jest jak rzeka a za węgłem czekają Funktory,Monady, Monoidy, Foldable,Kleisli, Transformatory Monad czy inne potężne narzędzia kompozycji efektów. Zazwyczaj na co dzień nie mamy czasu/ochoty zatrzymać się na chwilę i poddać kontemplacji nad tym co my właściwie robimy. Jednak należy pamiętać, że nasze obecne wnioski budowane są na dogmatycznych założeniach - jeśli czasem im się bliżej nie przyjrzymy to wiele ciekawych dróg i perspektyw może być dla nas na zawsze zamkniętych :(

Anegdota o .Necie

Zazwyczaj na początku nauki instaluje się jakiś kompilator , pisze hello world i dalej jakieś tam proste przykłady. Ponieważ bez ogólnych podstaw teoretycznych mechanizmy konkretnego języka staną się jedynymi ilustracjami tychże mechanizmów to istnieje niebezpieczeństwo, że ludzie zaczną utożsamiać implementacje z koncepcjami. W javie to będzie na przykład "polimorfizm - aha czyli dziedziczenie klas" albo powtarzanie bez głębszego zastanowienia, że wszystkie pola w klasie (bo przecież nie może być poza klasą nie?) muszą być private bo..bo.. bo enkapsulacja i ch*j.

No i raz było w Łodzi spotkanie grupy devs@lodz, która wykształciła się z grupy dot netowej. No i jest to spotkanie i gostek gada o MVC ale tak gada jakby używał cały czas słowa samochód ale chodziło mu o jedynie o Poloneza. Od słowa do słowa zrozumiałem, że Microsoft im tam potworzył po jednej implementacji wszystkiego i MVC to po prostu nazwa frameworka i też w moim odczuciu, trochę zdziwili się, że jak w Javie chcesz mieć MVC to myślisz o koncepcji i wybierasz sobie dopiero jedno z narzędzi, które to realizuje.

Innym analogicznym przykładem rozmowa o systemie operacyjnym gdy znasz tylko windowsa - "dobra znajomość systemu operacyjnego wymaga płynnej obsługi menu start" - czy coś w tym stylu.

Studiowanie to gorący temat w IT zwłaszcza, że w jakimś tam rankingu ostatnio dwie polskie uczelnie były w 5 setce a żadna Łódzka się nawet nie załapała ale polecam pogrzebać na edx czy courserze i można znaleźć naprawdę ciekawe kursy online dostarczane przez uczelnie z samej czołówki. Do tego wiele z tych uczelni ma swoje własne portale jak http://ocw.mit.edu/index.htm



czwartek, 18 sierpnia 2016

Historia Kulki Animowanej

Jest to wpis o historii pewnej Kulki. Nie jest to jednak zwykła kulka bo to historia kulki animowanej i to animowanej javascriptem. I nawet ona spada według jakichś tam praw fizyki i nawet za sobą zostawia ślad (rysowany też pośrednio javascriptem). Jest to kulka w javascripcie ale bez pisania w javascripcie.

Podążając za poprzednim dającym (na opewno mnie) nadzieję na łeba wpisem "Scala.js taka piękna" cały javascript jest wygenerowany przy pomocy Scala.js. I (jak dla mnie) kod jest fajnie. Bo są typy. I Jest fajnie.

Kino 8D

Czcionka na gifach jest mała dlatego trzeba w nie kliknąć albo skrolem powiększyć. Na pierwszym przeźroczu widać, że wielkość kulki można konfigurować i ona tak rośnie i maleje wtedy od razu. Do tego na bieżąco liczona jest masa tejże kulki z założeniem, że gęstość to średnia gęstość meteorytu czyli 3.4 g/cm^3

Jest tam także validacja pola gdzie można wprowadzić promień kulki. A, że rrrr to nie jest najlepsza wartość dla promienia dlatego warto ją sprawdzić. Tutaj dla podkreślenia możliwości Scali.js do validacji została użyta Monada. Jest to tylko Try ale zawsze Monoada®© Zawsze można się (nie)znajomością teorii kategorii popisać przed kolegami od front-endu.

No i w końcu "leć Adam leć". Zwróćcie uwagę, że wraz z czasem odległość pomiędzy czerwonymi kropkami się zwiększa a to dlatego, że działa przyspieszenie i składowa y prędkość się także powiększa z czasem co obrazuje wydłużająca się pionowa strzałka symbolizująca pionowa składową wektora prędkości.Dokładnie tak. W symulacji użyte jest przyśpieszenie księżyca 1.6 m/s aby kulka za szybko nie spadła i aby spektakl się za szybko nie zakończył.

"Z życia programisty front-endu"

Cały eksperyment miał za zadanie sprawdzenie "Jak się w tym pisze". Pierwsza ważna rzecz na jaką należy zwrócić uwagę przy zasiadaniu do nowej technologii to czy na StackOverflow są już odpowiedzi do wszystkich zadań

Generalnie te strzałki w symulacji moje nie są jeśli za "moje" uznać napisane własnoręcznie. Po wpisaniu w google arrow canvas javascript mamy ten link :

I na przykład tak to wygląda w javascript (kim czym - jawaskrypcie?)

//Javascript
function canvas_arrow(context, fromx, fromy, tox, toy){
    var headlen = 10;   // length of head in pixels
    var angle = Math.atan2(toy-fromy,tox-fromx);
    context.moveTo(fromx, fromy);
    context.lineTo(tox, toy);
    context.lineTo(tox-headlen*Math.cos(angle-Math.PI/6),
      toy-headlen*Math.sin(angle-Math.PI/6));
    context.moveTo(tox, toy);
    context.lineTo(tox-headlen*Math.cos(angle+Math.PI/6),
     toy-headlen*Math.sin(angle+Math.PI/6));
}

i naprawdę niewiele trzeba by ten kod zadziałał w Scala.js

// Scala.js
def canvas_arrow(fromx:Int, fromy:Int, tox:Int, toy:Int){
      val headlen = 10   // length of head in pixels
      val angle = Math.atan2(toy-fromy,tox-fromx)
      ctx.moveTo(fromx, fromy)
      ctx.lineTo(tox, toy)
      ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/6),
       toy-headlen*Math.sin(angle-Math.PI/6))
      ctx.moveTo(tox, toy)
      ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/6),
        toy-headlen*Math.sin(angle+Math.PI/6))
    }

Jestem w DOMu

Generalnie jedyna niedogodność do jakiej można się doczepić, to że używając "zwykłego DOM" pobieramy generyczne elementy HTML, które trzeba mapowac na konkretne Typy

//Scala.js
//jest troche więcej rzutowania przy pobieraniu elementów
val canvas=document.getElementById("canvas").asInstanceOf[Canvas]
val context=canvas.getContext("2d").asInstanceOf[dom.CanvasRenderingContext2D]

//ale dalej jest już w zasadzie to samo
context.fillStyle="#FF0000"
context.fillRect(0,0,150,75)

Ktoś przyzwyczajony do Javascript może narzekać, że to jakiś dodatkowy wysiłek ale to jest inwestycja, która zwraca się w dalszej części pracy z tymi elementami kiedy zabezpieczają nas te właśnie typy typy. (No chyba, że ktoś źle zrzutuje wtedy boli)

//Javascript 

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0,0,150,75);

Generalnie można pisać w Scala.js korzystając z dokumentacji do javascript. Ot choćby mechanizmy z tej stronki działają w Scali jak najbardziej : http://www.w3schools.com/html/html5_canvas.asp

Troski o jednostki

Na razie Scala.js goni javascript ale czy może zaoferować coś czego tam nie ma? (ewentualnie jest ale ja nie wiem, że jest). Otóż jak już ogarniemy przyciski i inputy to jest pewien problem poniżej bo trzeba nam np. wyliczyć masę ze wzoru m=ρ * (4/3) * π * r3 gdzie ρ - to gęstość. I teraz weź to wszystko licz przy pomocy double gdy gęstość to g/cm3 a promień jest w metrach. Czy też licz drogę w kilometrach gdy prędkość metr/sekunda a czas symulacji jest w milisekundach by się to jakoś odświeżało.

I w scali np moge stworzyć sobie odpowiednie typy, które będą kontrolowały jednostki. To poniżej nie jest jakimś szczytem modelowania domenowego bo Centy,Mili,Kilo trzeba powtarzać dla każdej jednostki ale generalnie chodzi o eksperyment jak to się sprawdzi w Scala.js (No i dla porównania zmienię formatowanie kodu bo nie mogę się zdecydować czy lepsze jest jaśniejsze czy ciemniejsze.)

object Distance{

  case class Centimeter(v:Double) extends AnyVal {
    def meters = Meter(v/100)
  }

  case class Meter(v:Double) extends AnyVal{
    def centimeters=Centimeter(v*100.0)
  }
  case class KiloMeter(v:Double) extends AnyVal{
    def meters=Meter(v*1000.0)
  }

  implicit def meterToDouble(m:Meter):Double=m.v
}

Pierwsza rzecz to kontrola przy liczeniu objętości kuli. Chcemy utrzymać czyste jednostki bez żadnego przedrostka.

case class Volume(v:Double) extends AnyVal

def sphereVolume(r:Meter):Volume=Volume(4.0/3.0 * PI * pow(r,3.0))

No i drogi czytelniku/czytelniczko mam nadzieję, że zauważyłeś/aś tam ciekawą rzecz, że podajemy bezpośrednio promień który przecież jest typu "Meter" do Math.pow. A to dlatego, że zaimportowaliśmy konwersję z modułu Distance

implicit def meterToDouble(m:Meter):Double=m.v

No i jeszcze jedna rzecz - AnyVal. W Scali kompilator rozpakuje prymitywa i w runtime nie ma żadnego narzutu a w trakcie kompilacji typy zabezpieczają. (to tak jak wypić wódkę i mieć wódkę czy coś takiego)

No i z tego kodu już po kompilacji do js wynika, że to też działa bo bez AnyVal jest wersja gdzie wartość z promienia trzeba dopiero wydobyć (czyli to tak jak mieć ciastko i zjeść ciastko albo tak jak nie płacić abonamentu a i tak oglądać telewizję).

//Z valueclass
$c_Lcom_wlodar_physics_Geometry$.prototype.sphereVolume__D__D = (function(r) {
  return (4.1887902047863905 * $uD($g.Math.pow(r, 3.0)))
});

//bez value class
$c_Lcom_wlodar_physics_Geometry$.prototype.sphereVolume__Lcom_wlodar_physics_Distance$Meter__D = (function(r) {
  var a = r.v$1;
  return (4.1887902047863905 * $uD($g.Math.pow(a, 3.0)))
});

Rzeczy nowe

Przy okazji takiego programu pojawia się kilka ciekawych wyzwań, których w zwykłych CRUDach nie ma. No bo tam jest taki stan, który wydaje sie wygodny czyli np. aktualne położenie kulki i jakoś monady mnie się tu nie widzą. Można by zawczasu "obliczyć" sumylację i później ją odpalić wtedy niejako kolejne klatki byłyby kolejną wersją niemutowalnego stanu na stosie czy czymś takim i w sumie tak historia ruchu (te czerwone kropki) jest wyliczana.

Ale z drugiej strony na courserze przy okazji zeszłorocznego kursu o Reactive Application było coś o sygnałach i "Functional Reactive Programming". Także też ciekawe.

Testowanie

Tę część z jednostkami to nawet i zwykłym ScalaTestem da się pojechać bo to zwykła taka Scala-Scala. Można Scalachecka użyć do Property Based Testing no i są specjalne narzędzia które testy tłumacza na javascript i odpalają je już na takim "RuntimeDom"

Kulka

Jest pod adresem :

adres kulki.

Niestety jak wstawiłem iframe to blogger mi powiedział, że nie ma tego ficzeru. Generalnie chyba najfajniej wygląda jak wpisze się 100 w promień i 150 w prędkość poziomą. Mozna tez sobie potestować podawanie złych wartości - Try daje radę przy podawaniu liter zamiast cyfr a dalej już nie testowałem.

Jak na kogoś kto ostatni raz javascripta robił w 2013 i teraz taka kulka... no czuję się taki szcześliwy...

czwartek, 4 sierpnia 2016

I nawet dla ciebie jest nadzieja w webie

Już na samym początku - aby uniknąć niepotrzebnych dyskusji o tym, który młotek jest najlepszy - przywołamy magiczne słowo "warunkowe". Z matematki taki zapis : P(B|A)=... , określa, że będziemy liczyć wystąpienie prawdopodobieństwa B pod warunkiem, że zaszło zdarzenie A. I dalej idąc w bardzo luźną analogie rozpatrzymy dzisiaj sytuację : U(scala.js|scala) czyli

U - szansa, że uda ci się coś zrobić w scala.js jeśli już robisz w scali 
i nie masz czasu co trzy godziny uczyć się nowego frameworka javascriptowego.

Czas i (mała) przestrzeń

Czasem w niektórych firmach istnieje niewidzialny klej, który przytwierdza ludzi do jednego projektu na całe ich życie (a w kontrakcie jest zapisane, że po śmierci ich prace będą kontynuować ich dzieci bo nikt inny nie skuma tego kodu). Często poletko, którym się zajmują ma 1 metr kwadratowy i pomija wiele ciekawych aspektów programowania. I tak jak ktoś bijący się z przeglądarką może znać wątki ale tylko te z mody na sukces. A ktoś działający na back-endzie widzi javascript jako doskonałe narzędzie do wyświetlania alertów.

Ponieważ sam ostatnie półtora roku miałem takie, że główny kontakt z webem był gdy dokumentacje do sparka otwierałem w przeglądarce także jeśli istnieje szansa na robienie weba przy pomocy ekosystemu, który jako tako znam to zamiast ogarniać całą osobną gałąź przemysłu od początku (jakieś grunty srunty) to takiej możliwości warto się przyjrzeć.

To nie jest tutorial bo tutorial już jest : http://www.lihaoyi.com/hands-on-scala-js/. Chodzi bardziej o wrażenia. Wrażenia i odczucia a może nawet i uczucia. To nieprawda, że programiści nie mają uczuć. ja np. często czuję, że coś nie zadziała.

robić Łeba

Bardzo dobry tutorial na wstęp jest tutaj : https://www.scala-js.org/tutorial/basic/ i jak to tutorial - działa dopóki robi się wszystko według tutoriala. Ale warto trochę zbaczać i psuć bo wtedy można zrozumieć to co się robi w szerszym kontekście.

Ten tutorial tworzy prostą stronkę z javascriptem. Może i warto na początek za dużo nie kombinować i go po prostu zrobić ale dalszą ciekawsza wariacją będzie próba takiego para-produkcyjnego użycia scala.js w aplikacji webowej. Do tworzenia wara użyjemy scalatry. Scalatra jest bardzo przyjemnym narzędziem bo już chyba ku*wa prostszego frameworku do tworzenia aplikacji webowej wymyślić nie można.

Dodatkowy zysk edukacyjny pracy na scalatrze jest taki, że łatwiej zrozumieć jak współdziałają te wszystkie pluginy SBT. W Playu wszystko jest takie od razu scalone - "which is good" - ale można przez tę fasadę nie dojrzeć wielu szczegółów.

Scala.js ma swój plugin, który kompiluje do "targetu" a Scalatra siedzi na XSBT-WEB, który zbiera javascript z katalogu "src/main/webapp". Jest kilka szablonów na necie, które pokazują jak zgrać prawidłowo te dwa narzędzia (na przykład : ) zazwyczaj w postaci dwóch niezależnych modułów.

Na potrzeby bloga wybiorę prostsze podejście czyli kopiowanie plików na pałę.

val kopiujNaPałęJavascript: TaskKey[String] =taskKey[String]("compileJavascript")

kopiujNaPałęJavascript := {
  val jsHome = sourceDirectory.value / "main" / "webapp" / "js"
  val dependenciesFileName = "scalatra-js-jsdeps.js"

  val generatedFile: File =(fastOptJS in Compile).value.data
  val generatedDepFile=new File(generatedFile.getParent,dependenciesFileName)

  val destinationFile = jsHome / generatedFile.getName
  val dependenciesFile = jsHome / dependenciesFileName

  IO.copyFile(generatedFile,destinationFile)
  IO.copyFile(generatedDepFile,dependenciesFile)

  jsHome.getAbsolutePath
}

"scalatra-js-jsdeps.js" to plik z wszyskimi zależnościami takimi jak jquery. A no bo właśnie - można sobie zainstalować fasadę na jquery i mieć takie "w-pół" statyczne typowanie w tym jakże wygodnym frameworku.

jQuery("#demoButton").click(processInput _)

def processInput():Unit={
....   
}

No i to też się do testowania przyda później. Apropo JQuery to jeszcze taka dygresja

Dygresja o rzeczach niefizycznych

W arcy ciekawej książce Antifragile Nicholas Taleb opisuje koncepcję żywotności rzeczy niematerialnych, która wybiega trochę na przeciw naszej intuicji. Dzieje się tak gdyż w przypadku rzeczy materialnych im dłużej dana rzecz istnieje tym prawdopodobnie będzie istnieć krócej. I tak np. mamy 1 letni i 10 letni samochód tej samej marki - intuicja nam podpowiada, że - wyłączając zdarzenia losowe - ten starszy szybciej pójdzie na złom.

Teraz weźmy rzecz niematerialną jak choćby język programowania. Mamy dwa języki - jeden wydany rok temu a drugi przed 10ciu laty. Pytanie, który wcześniej się rozpłynie w nicości. No i tutaj może być trudniej to ogarnąć bo generalnie język jako koncept nie rdzewieje, nie wpieprzają go korniki ani nic takiego. żywotność konceptu jest zależna tylko od nośnika i jeśli jakieś nośniki od 10 lat przenoszą ten koncept to oznacza, że z jakiegoś powodu wygrał w ruletce "selekcji naturalnej" języków programowania. A ten co ma rok czy nawet miesiąc - nie wiadomo.

No i mamy takie JQuery, które już ma 10 latek i przetrwało selekcje a 10 lat temu był w użyciu też inny wynalazek - prototype.js. Na przełomie 2006/2007 był jednym z niewielu szczepionek na popularną w webdeveloperce chorobę zwaną IE6 (gimby nie znajo i ich kurwa szczęście...). A teraz według wiki używa go 2,2% stron czyli pewnie twórca, jego sąsiad i kilka korpo z miód-maintenecem. Na wiki możecie sobie poczytać co poszło nie tak.

Także jak z dnia na dzień zobaczysz jakiś super nowy hiper frejmowrk weź wdech, dokończ robote i w wolnej chwili na spokojnie się temu przyjrzysz.

Moje pole

Celem laboratorium będzie walidacja jednego pola po stronie klienta, wysłanie go na serwer , ponowna validacja i odesłanie jakiejś odpowiedzi.

Nie ma sensu wklejać całego kodu bo to luźno zwisający HTML i kilka instrukcji scali także skoncentrujemy się na najważniejszych fragmentach. No i tak na poniższym wycinku rejestrujemy "onklika" , po którym zgarniemy wartość z pola i przy pomocy scalowego (a jakże) pattern matchingu sparsujemy wartość i podążymy jedną z dwóch ścieżek : błędu lub szczęścia.

jQuery("#demoButton").click(processInput _)

def processInput():Unit={
    jQuery("#wynik").remove()
    val mojePole: Dynamic =jQuery("#mojePole").value()

    Try(mojePole.toString.trim.toInt) match {
      case Success(v) => validInput(v)
      case Failure(e) => wrongInput(e)
    }
}

Jak sie popsuło to będzie na czerwono :

 def wrongInput(e: Throwable)= {
    jQuery("body").prepend(s"""
         |<div class="alert alert-danger" role="alert" id="wynik">ERROR : ${e}</div>
         """.stripMargin)
  }

Do serwera

I oto dwa kawałki kodu napisane w jednym i tym samym silnie typowanym języku siedzące w dwóch różnych miejscach internetu (w teorii bo w praktyce w tym przykładzie oba są na moim kompie) komunikują się ze sobą!

//scala.js - browser
Ajax.post(s"/validate?value=$v").onComplete{
      case Success(xhr) => prependResponse(xhr.responseText)
      case Failure(e) => prependResponse(s"zjebało się : $e")
}


//scalatra-serwer
post("/validate"){
    Try(params("value")).map(v=>
      Ok(s"z serwera $v")
    ) getOrElse halt(BadRequest())  //400
}

Scalatra jak mówiłem prosta i przyjemna także taki prosty kodzik obsługuje post. No i wynik :

I działa

Błędy w runtime

W runtime działa już sobie javascript, który w teorii powinien być dobry jeśli tylko został wygenerowany z kompilującej się scali (plus pewnie bliżej nieokreślone N nieprzewidzianych bugów). Natomiast nic się nie dzieje gdy wyszukuję nieistniejący element na stronie i coś tam działam - w sensie nie dzieje się na chromie bo w testach ładnie się wywala. A testy pisze się np. w tym : https://github.com/lihaoyi/utest czyli narzędzia są.

Wygląda obiecująco

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