To jest taki pierwszy z brzegu obrazek procesora ściągnięty z netu. Po co on tutaj jest? Ano aby pokazać, że wszystkie dyskusje o paradygmatach programowania sprowadzają się w ostateczności do tego jakie napięcie pojawi się o tam na tych "nuszkach".
Dla procesora te dyskusje z cyklu, który język lepszy itd są niezrozumiałe bo on widzi i tak tylko 10100010111 czy coś podobnego. Zaś dla ludzi sytuacja jest inna. Dla ludzi poczciwych jak ty i ja być może bardziej abstrakcyjne spojrzenie pomoże ogarnąć szersze przestworze cyfrowych konstrukcji. W końcu po to są metafory aby rozumieć coś nowego w kontekście czegoś starego. A jeśli metafora pomoże zmniejszyć ilość spalonych dolarów na produkcji to tym lepiej. To zobaczmy pierwszą metaforę dla Listy - dla czegoś czego używamy od zawsze i zazwyczaj sie kojarzy z przypadkiem "Lista produktów" albo Lista Userów, a teraz inaczej...
Lista jako alternatywa dla...
Option lub Optional lub Maybe w zależności od języka. Dokładnie tak. Bardzo często spotykam się z porównaniem Option do takiego obiektowego-ifa czy czegoś w tym stylu. Raczej mało osób kojarzy wchodzi rozmyślaniami w system typów i konsekwencje sprowadzenia opcjonalności do wyniku obliczeń
Natomiast jest to trochę niecodzienna percepcja Optionala widzieć go jako taką małą Listę - albo w drugą stronę - percepcja Listy jako takiego dłuższego Optionala . Po raz pierwszy się z tym spotkałem w kursie FP in Haskell gdzie Erik Meijer wspomniał, że lubi używać List zamiast Maybe (w haskellu jest Maybe a zamiast Some jest Just - dzięki czemu można stosować polski żart Just(5) - ale Gimby pewnie nie znajo...) bo w Maybe może sobie przechować tylko jedną potencjalną wartość - i to jest dosyć ciekawe Lista jako struktura przechowująca n opcjonalnych wartości.
Taki kodzik sobie zobaczmy :
type HTML=String case class User(login:String, email:String) val extractEmail:User=>String= _.email val displayEmail: String => HTML = email => s""" |<h1> EMAIL </h1> |<span> ${email} </span> """.stripMargin
Jest tam i User (byśmy mogli rzec, iż jakoś "biznesowo" jest) jak i dwie banalne pokazowe funkcje aby zbudować mały ciąg przekształceń. I patrz, i patrz jak ten kod jest do siebie podobny :
//option def findUser(login:String):Option[User]=Some(User("Roman","donjuan17@wp.pl")) findUser("Roman").map(extractEmail).map(displayEmail).getOrElse("""<h1>Nie ma takiego zioma</h1>""") //list def findUserList(login:String):List[User] = List(User("Roman","donjuan17@wp.pl")) findUserList("Roman") .map(extractEmail).map(displayEmail).headOption.getOrElse("""<h1>Nie ma takiego zioma</h1>""")
taka nowa ciekawa perspektywa co nie? Lista jako 0..n opcjonalnych elementów. Ale można na Listę spojrzeć jeszcze z innej strony.
Efekt - ale czego ?!?
Mamy takie dwie proste funkcje :
val addThree:Double=>Double= _ +3 val timesTwo:Double=>Double = _*2
Możemy je zastosować :
W kontekście efektu opcjonalności :
Option(1.0).map(addThree).map(timesTwo)
W kontekście efektu błędu :
Try(1.0).map(addThree).map(timesTwo)
W kontekście efektu czasu :
Future(1.0).map(addThree).map(timesTwo)
I mogę sobie coś takiego zastosować na liście :
List(1.0).map(addThree).map(timesTwo)
Składnia podobno ale czy to jest jakiś efekt ta lista? A jeśli jest to efekt to efekt ku*wa czego?
A wybierz se co chcesz
y=f(x)
Co jest tak niesamowitego w tej linijce powyżej, że ma własne formatowanie i italica? Otóż funkcja aby była funkcją musi mieć jeden wynik. No i zazwyczaj ma - findUser i jest user. albo 2+2=4 ale jaki jest wynik czegos takiego : 0=x^2-4 ?
Math.pow(2,2)-4 Math.pow(-2,2)-4
Nie ma jako takiego "jednego rozwiązania" - gdybyśmy chcieli znaleźć wszystkie możliwe rozwiązania (nie licząc krzywych wymiarów zapętlonie-zespolonych) to kod moglibyśmy napisać tak :
def allSqrts(v:Int)=List(-Math.sqrt(4),Math.sqrt(4)) allSqrts(4) //res12: List[Double] = List(-2.0, 2.0)
I cały dowcip polega na tym, że tam gdzieś jest dobra odpowiedź ale ja nie wiem która - i w sumie przez chwilę nie chcę wiedzieć.
allSqrts(4).map(addThree).map(timesTwo) //List[Double] = List(2.0, 10.0)
Może wezmę pierwszy, może większy, może mniejszy - to zależy od kontekstu (jak zwykle). A te wszystkie metody z rodziny fold czy reduce, które np. liczą sume zysku to tak abstrakcyjnie patrząc wszystkie polegają na tym, aby przejść od stanu nie-determinizmu do pewnej określonej wartości.
Czyli patrząc na coś takiego :
sealed trait SchrodingerCat case object Alive extends SchrodingerCat case object Dead extends SchrodingerCat val catStates=List(Alive,Dead)
Może warto przeredagować paragraf z poprzedniego punktu =>
W kontekście efektu braku determinizmu :
List(1.0,2.0,3.0).map(addThree).map(timesTwo)
Brak komentarzy:
Prześlij komentarz