Krolik napisał(a)
Kolejna bzdura. Języki funkcyjne dzielą się na języki z wczesną ewaluacją i z leniwą ewaluacją. To, że Haskell ma leniwą, nie oznacza, że to obowiązuje w całym świecie FP. Inna kwestia, że w obu typach daje się programy zrównoleglać.
Nie zrozumiałeś - każdy kod czysto funkcyjny może być traktowany jako leniwy, co oznacza, że leniwość jest inherentną cechą języków funkcyjnych, nawet jeśli implementacja tego nie wspiera. To takie trudne?
Nic nie szkodzi Ci w Scali napisać mapP albo reduceP, działających równolegle, dokładnie tak samo jak w Haskellu.
Ale tu chodzi oto, że program sam sobie dynamicznie dzieli pracę na wątki. Dodatkowo haskell potrafi dzielić tak zwykły kod, ale już trochę gorzej.
[..] Dlatego sensowne jest pozostawienie tego programiście.
Tak, niech programista pisze kod wykrywający liczbę rdzeni i mówi programowi jak się ma dzielić i gdzie... co za absurd.
To jest niemożliwe przy kilku tysiącach rdzeni, poza programami napisanymi jak w erlangu, a i wtedy to będzie bardzo nieefektywne.
Kod może mieć 100 wirtualnych procesów i będzie najlepiej działał na 100 rdzeniach... dodasz 1000 i nic się nie zmieni. Kod musi być dzielony przez runtime, NIE MOŻE być dzielony przez programistę.
To ten sam problem z manualnym zarządzaniem pamięcią jak w c++ -> to się zawsze skaluje gorzej od GC.
Ewentualnie maszynie wirtualnej, która ma więcej informacji niż statyczny kompilator jak GHC - ale to by pokazywało, że przy odpowiednim wsparciu ze strony JVM, Scala ma większe szanse na sensowne automatyczne zrównoleglanie, niż Haskell :P
Dobrze, ale to implementacja, a nie język.
W poprzednim poście zadałem Ci pytanie o konkretny powód w czym przeszkadzają zmienne, których NIE UŻYJĘ? Jak jesteś taki teoretyk, to udowodnij swoje twierdzenie. To co przedstawiłeś, to nie jest dowód.
To że TY nie wiesz jak to zrobić, nie oznacza, że tego się nie da zrobić.
Ale to właśnie jest dowód... nie rozumiesz do końca problemu.
- Jak to nie wiadomo? Kompilator bardzo dobrze WIE, gdzie ta referencja jest przekazywana. Wystarczy popatrzeć na wywołania.
Czyli "wystarczy sprawdzić wszystkie możliwe stany w których może wystąpić obiekt", co, jak napisałem, jest praktycznie niemożliwe. W kilku małych przypadkach jest i da jakąś optymalizację. Ale dla całego programu NIE DA SIĘ.
Symbole są lokalne ale obiekty są GLOBALNE.
Dokładnie taką optymalizację próbuje robić sbcl, żeby zmniejszyć kopiowanie... i bardzo kiepsko to działa, właśnie dlatego. To jest to Twoje "escape analysis" w praktyce.
- Jeśli tablica jest strukturą niemutowalną, to nic nie ma szans jej zmienić. Struktury niemutowalne w Scali są w powszechnym użyciu.
Ale wtedy czysto funkcyjny jest kod ze specyficznymi argumentami, a nie sam kod, przecież możesz przekazać mutowalną tablicę jako niemutowalną, bo to znaczy tylko tyle co "const". Podczas działania programu, przy każdym wywołaniu funkcji vm musi sprawdzać, czy z tym argumentem kod można zrównoleglić. To ma jakiś sens ale nieznacznie spowalnia i mocno utrudnia.
Dwie bzdury w jednym:
- Nie musi. Wystarczy, że obiekty są wszędzie zadeklarowane jako stałe i są niemutowalnej klasy.
- Sugerujesz, że nie da się określić maksymalnego zasięgu danego obiektu - da się i takie coś jest już robione przez JVMy - nazywa się Escape Analysis. Ale robi się to w zupełnie innym celu - tutaj to byłby overkill.
Ponieważ i tak nie wiesz, jak to zrobić, to rozwiązanie jest tu:
http://jnordenberg.blogspot.com/2008/10/functional-purity-in-scala.html
LOL
wojtekstrazak napisał(a)
[..]Ręczne oznaczanie jest jedynym wyjściem.[..]
strona napisał(a)
Now you can mark a function/method as pure:
@Pure def pureFunc(x : Int, y : Int) = x + y
To stwierdzić bardzo trudno - przecież już zwykła tablica jest przekazywana przez referencję i nigdy nie wiadomo, czy przypadkiem nie trafi do czegoś, co ją zmieni.
Automatycznie (nie posiadająca efektów ubocznych) funkcja dostająca taką tablicę w argumencie nie mogłaby być wykonana niesekwencyjnie.
"Invariant" is a term I borrowed from D, it's basically a deeply immutable value, i.e. it's an immutable value that only contain invariant values :). For example, List[Int] is invariant, but List[Array[Int]] is not invariant even though List[Array[Int]] is an immutable type.
Smutne, gość się zgadza ze mną w 100%, podobieństwo jest wręcz dosłowne. A ty mi to pokazujesz jako "rozwiązanie którego nie wymyślę", eh. Ręce opadają.
(btw - punkt 1 jest oczywiście bez sensu, nie rozumiem jak mogłeś to napisać po przeczytaniu jego postu? Wyraźnie odróżnia niemutowalność od inwariancji...)
BTW: Zmuszanie programistów do użycia tylko kodu czysto funkcyjnego to jest jeden z powodów, dla których języki czysto funkcyjne IMHO nie mają szans. Po prostu to jest zbyt duża rewolucja w myśleniu
Programowanie i tak jest tak banalne, że dałoby się tego nauczyć małpy, a małpy nie są od decydowania. Może to i rewolucja w "myśleniu" dla obecnych programistów, uczelnie/kursy wyszkolą nowych, a starych się wyrzuci. Programowanie wymaga podobnych umiejętności co układanie cegieł na budowie, z tą różnicą że trzeba nauczyć się składni na pamięć.
A projektowanie oprogramowania będzie dużo łatwiejsze w obiektowych językach funkcyjnych (o wiele mniej zależności), więc ci nieliczni ludzie od myślenia odetchną z ulgą.