Jaki język z rodziny j. funkcyjnych polecacie?

0

Witam,

jak w temacie. Jakim językiem warto się zainteresować, jakiemu wróżycie przyszłość? Jakie są wasze spostrzeżenia na temat tych języków - jaki najlepszy do nauki, jaki może mieć zastosowanie w pracy? Mnie póki co zainteresowały dwa: F# - jako, że na co dzień programuje w .NET i Scala - hobbistycznie robię coś od czasu do czasu na Androida. Zastanawiam się czy warto poświęcać tym językom więcej czasu. Jak uważacie?

0

Pod żadnym pozorem nie interesuj się lispem, scheme czy haskellem. Mają dużo dobrego PR w necie ale są tak naprawdę bezużyteczne, co prawda każdy z trochę innych powodów.
Ze swej strony polecam ci Erlanga - dla problemów wielowątkowych jest idealny. Do napisania całej aplikacji się raczej nie nadaję, ale np. jako serwer czy część licząca jak najbardziej tak.
Dodatkowo erlang używa wielowątkowości opartej o przekazywanie wiadomości, warto pobawić się takimi niekonwencjalnymi sposobami.

0

Przyszłości językom funkcyjnym nie wróżę żadnej – czy też raczej taką, jak jest z nimi teraz: ot, ciekawostka, w której nawet da się napisać coś pożytecznego.
Ale jako ciekawostka, wspaniałe one są.

0

lol
Języki funkcyjne są jedyną możliwą przyszłością -> procesory szybsze nie będą, będzie ich za to więcej... dużo więcej.
Teraz masz 16rdzeniowe... będą i kilku tysięczne.
Czegoś takiego po prostu nie da się wykorzystać pisząc w języku stanowym. A w języku funkcyjnym nie trzeba nawet o tym myśleć, pisząc kod.

0
wojtekstrażak napisał(a)

Języki funkcyjne są jedyną możliwą przyszłością
Historia uczy, że przewidywanie przyszłego stanu wiedzy i techniki nigdy się nie sprawdza.
Wyobraź sobie jakieś stare sajens fikszyn, gdzie już od dawna latamy na Marsa, ale statek ma klapkowo-mechaniczne "wyświetlacze".

procesory szybsze nie będą, będzie ich za to więcej... dużo więcej.
A może będzie coś innego, nie procesor w dzisiejszym znaczeniu?

Teraz masz 16rdzeniowe... będą i kilku tysięczne.
Jednostki w kartach graficznych już pod to podchodzą.

Czegoś takiego po prostu nie da się wykorzystać pisząc w języku stanowym.
To nieprawda.

A w języku funkcyjnym nie trzeba nawet o tym myśleć, pisząc kod.
Jeszcze ktoś pomyśli, że w językach funkcyjnych w ogóle nie trzeba myśleć...

0

Ja polecam Scalę. Nie jest stricte funkcyjny, ale wieloparadygmatowy z dużym ukłonem w kierunku funkcyjności.

Przyszłości językom funkcyjnym nie wróżę żadnej – czy też raczej taką, jak jest z nimi teraz: ot, ciekawostka, w której nawet da się napisać coś pożytecznego.
Ale jako ciekawostka, wspaniałe one są.

Tak samo mówiono kiedyś o OOP. A jeszcze wcześniej o programowaniu strukturalnym.

Być może języki czysto funkcyjne się nie przebiją, tak jak nie przebiły się czysto obiektowe (Smalltalk), ale nie dziwi Cię ten ostatni pęd do dodawania funkcyjnych elementów we wszystkich popularnych językach imperatywnych (PHP, C#, Java, ba nawet w C++)?

0

Ogólnie to idealny język byłby funkcyjny i obiektowy.
Funkcyjność sama z siebie nie nadaje się zupełnie do tworzenia dużych rzeczy (bo organizacja kodu nie różni się specjalnie od kodu strukturalnego; jest on dużo krótszy, ale tylko tyle), ale w połączeniu z dobrym systemem obiektowym wynik byłby fantastyczny.

0

Taki język istnieje i nazywa się Scala ;]
http://www.scala-lang.org/

Swoją drogą, obiektowość i funkcyjność to są raczej ortogonalne kwestie. Możesz mieć język imperatywno-obiektowy jak Smalltalk i funkcyjno-obiektowy jak... funkcyjna część Scali albo OCaml. Napisałem "funkcyjna część", bo Scala jest bardzo wieloparadygmatowa i można WYGODNIE pisać zarówno czysto imperatywnie jak i czysto funkcyjnie, jak i łączyć te sposoby.

0

Placa Ci za te ochy i achy nad Scala?

0

@u - Jeśli nie zauważyłeś - to jest forum i każdy ma prawo do własnych poglądów. Oraz, o zgrozo, wyrażania ich.

0

Jakie ochy i achy?
Po prostu mi się dobrze w tym pisze, oraz pasuje pod to co wojtekstrazak nazwał językiem łaczącym obiektowość i funkcyjność. Ale nie oznacza to, że język jest bez wad. Parę rzeczy mi w nim nadal przeszkadza, ale wiem, że pracują nad tym. Znając ich dotychczasową bezkompromisowość i innowacyjność spodziewam się, że rozwiązanie jak zwykle przejdzie moje oczekiwania.

A co do Haskella, to na pewno warto go choć trochę poznać. Jest ortodoksyjny, ale taki miał być. Na pewno dużo można się nauczyć.

0

Nie pisałem w Scali, ale patrząc na książkę (http://programming-scala.labs.oreilly.com/) -

  • zmienne są zmienne - wtf?
  • nie ma automatycznej wielowątkowości (skalowania).
    To nie jest język funkcyjny, raczej taki lisp z inną składnią i systemem obiektowym... może i ładny, ale po co to komu?
0

zmienne są zmienne - wtf?

LOL, tak jakby obecność zmiennych zaprzeczała "funkcyjności" języka.
Co najwyżej świadczy o tym, że nie jest "czysto funkcyjny". Ale sam wczęśniej pisałeś że czysto funkcyjne języki to zabawki akademickie, a nie do pisania poważnych programów [diabel]

Języki (niekoniecznie czysto-) funkcyjne to te, w których wygodnie można programować bez mutowalnego stanu. Scala tę definicję wypełnia w 100%. A nie jakieś pierdułki o tym, co konkretnie jest w języku, a czego nie ma. Na tej zasadzie, to i można powiedzieć, że ani SML, ani Ocaml, ani Erlang, ani F#, ani Scheme nie są funkcyjne. Ba, w Haskellu jak się uprzesz, też masz zmienne.

Scala jest językiem wieloparadygmatowym. Takim, w którym jeden paradygmat nie jest ofiarą innego. Jak byś chciał wygodnie programować imperatywnie bez zmiennych?

nie ma automatycznej wielowątkowości (skalowania).

Jest. Actors library, ala Erlang.

0

Obecność zmiennych wyklucza sensowne skalowanie. Można z góry powiedzieć, że Scala powszechnie i długo używana nie będzie, właśnie dlatego. To jest język na teraz tylko, przeminie,

A co do aktorów - to jest model projektowy, a nie jakaś wielowątkowość. Model nie ma prawa mieć jakichkolwiek związków z implementacją, a jeśli ma, to jest błąd w założeniach języka.
Dobrze jest to zrobione w erlangu, gdzie wirtualnych wątków można mieć kilkaset tysięcy, a vm samo dzieli sobie robotę na rzeczywiste procesory.
Podobnie jest w haskellu, chociaż io domyślnie jest jednowątkowe, ale można już pisać to forkIO jak trzeba.
Ręczna kontrola równoległego przetwarzania to ślepa uliczka i ślepą uliczką jest też scala, chyba, że można jakoś zaznaczyć kod funkcyjny i kompilator sam go podzieli.
W przeciwnym przypadku nie ma żadnych zalet nad javą, do której też są przecież biblioteki do przetwarzania równoległego.
Pisze się trochę łatwiej i szybciej... za to java jest najbardziej rozpowszechnionym językiem na świecie, potrafi to samo, w dodatku programiści są tańsi... po co używać scali?

To nie znaczy, że to zły język. Ale nazywanie tego językiem funkcyjnym to tak samo jakby nazwać c++ funkcyjnym, bo boost daje trochę takich elementów...

Nie ma obecnie funkcyjnego języka obiektowego -> nie jest tak bardzo potrzebny. Ale będzie, bo nie ma innego wyjścia.

@Azarien: nie da się wykorzystać w stanowym języku, bo żeby to miało sens, trzeba by stworzyć własne vm własnego czysto funkcyjnego języka...
A jednostki gpu są na razie dość prymitywne, może za te kilka generacji...

0

Zmartwię Cię, ale były już robione wielokrotnie próby z automatycznym skalowaniem programów czysto funkcyjnych na wiele rdzeni (np. jako implementacje systemów DataFlow, które można podciągnąć pod FP). I to nie działa tak dobrze jak myślisz. Do tego stopnia, że nadal nie istnieje język, w którym działałoby sensowne zrównoleglanie kodu bez pomocy programisty.

I obecność zmiennych w języku nie jest najmniejszym problemem. Nie musisz ich w Sali używać. Kompilator nie jest głupi i bez problemu może stwierdzić, które funkcje są "pure" a które nie. Nie wiem, skąd wziąłeś (mylne) założenie, że programista musi ten kod oznaczyć. Kompilatory robią to od dawna i to dla takich niefunkcyjnych języków jak C. Problemem jest to, że pewne algorytmy lepiej się zrównoleglają, a inne gorzej - i nie ma na razie kompilatora, który byłby w stanie przekształcić nieskalujacy się algorytm na skalujący. Ba, nie ma na razie nawet kompilatora, który na podstawie kiepskiej implementacji bubble-sorta zauważyłby że chodzi o sortowanie i przepisał na quick-sorta.

Cały myk z programowaniem funkcyjnym i zrównoleglaniem polega na tym, że programy funkcyjne zwykle dają się zrównoleglić mniejszym wysiłkiem niż imperatywne, bo łatwiej wnioskować o ich poprawności. Scala to perfekcyjnie umożliwia, dokładnie tak samo jak Erlang czy Haskell.

Co do modelu actorów w Scali, to nie ma powodów, aby nie skalował się do wielu milionów aktorów tak samo jak w Erlangu. Podajesz cały czas argumenty z niewiedzy (albo z "osobistego niedowiarstwa").

I cały czas mylisz pojęcia. Istotą programowania funkcyjnego nie jest równoległość, ani kwestia tego, co w języku jest a czego nie ma. To jest inny sposób postrzegania programów - jako przekształcenia funkcyjne niemutowalnego stanu, zamiast sekwencji modyfikacji stanu mutowalnego. I tylko tyle, ale też aż tyle. Scala to umożliwia w równie wygodny sposób jak Erlang/Haskell/Scheme i multum innych języków funkcyjnych, jest więc językiem funkcyjnym. C++ i z lambdami będzie od tego daleko.

Obecność zmiennych wyklucza sensowne skalowanie

Dawno takiej bzdury nie słyszałem. Jakieś argumenty na poparcie tej tezy?
Jak zmienne, których nie użyję w programie napisanym funkcyjnie w Scali, mogą coś popsuć ze skalowalnością? Jak monady, których nie użyjesz w Haskellu, mogą popsuć coś ze skalowalnością?

0
Krolik napisał(a)

Zmartwię Cię, ale były już robione wielokrotnie próby z automatycznym skalowaniem programów czysto funkcyjnych na wiele rdzeni (np. jako implementacje systemów DataFlow, które można podciągnąć pod FP).

Haskell to ma od dawna, oprócz io?
Problemem jest trochę kiepska integracja w języku, bo o ile dobrze pamiętam, zamiast map trzeba pisać mapP itd. Ale przy jednym wątku obie wersje działają tak samo szybko.
Z kolei normalny kod potrafi dobrze zwektoryzować, przynajmniej zazwyczaj.

Problemem jest to, że pewne algorytmy lepiej się zrównoleglają, a inne gorzej - i nie ma na razie kompilatora, który byłby w stanie przekształcić nieskalujacy się algorytm na skalujący. Ba, nie ma na razie nawet kompilatora, który na podstawie kiepskiej implementacji bubble-sorta zauważyłby że chodzi o sortowanie i przepisał na quick-sorta.

Ale kompilator nie ma przekształcać, ma tylko uruchomić kod równolegle, bo

Istotą programowania funkcyjnego nie jest równoległość, ani kwestia tego, co w języku jest a czego nie ma.

Istotą programów funkcyjnych jest równoległość (leniwość), co za tym idzie kompilator nie musi nic zrównoleglać (może, ale to optymalizacja).
Tzn. kod może się w danej implementacji wykonywać sekwencyjnie i nie posiadać leniwości. Ale dodanie leniwości nie wymaga zmiany kodu, bo wynik będzie ten sam.
(pomijając programy, które działają w nieskonczoność bez leniwości).

Wielowątkowość można też zrealizować w najprotszy możliwy sposób -> każde wywołanie funkcji to nowy wątek. Nie potrzeba żadnej analizy, drzew obliczeń (jak w leniwości), itd...

Problem najwyżej w dzieleniu zbyt dużej liczby wątków na zbyt małą liczbę fizycznych procesorów. Dlatego bez podejścia funkcyjnego przy zrównoległaniu można się teraz obejść, bo zyski i tak byłyby małe. Ale przy kilku tysiącach małych rdzeni dzielenie przestanie być takim problemem...

I obecność zmiennych w języku nie jest najmniejszym problemem. Nie musisz ich w Sali używać. Kompilator nie jest głupi i bez problemu może stwierdzić, które funkcje są "pure" a które nie.

Ale żeby wektoryzować kod, kod nie może w żaden sposób polegać na wartości czegokolwiek, co jest niefunkcyjne. O ile wiem kompilatory są w stanie stwierdzić, czy funkcja zmienia stan czegoś, a nie, czy zależy od czegoś co jest gdzieś zmieniane.

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.
Żeby kompilator mógł oznaczyć wszystkie obiekty, musiałby sprawdzić wszystkie możliwe stany programu, żeby określić maksymalny zasięg danego obiektu.
Element tablicy też może być referencją do czegoś mutowalnego...
Nie da się tego po prostu obliczyć. Ręczne oznaczanie jest jedynym wyjściem. Ewentualnie wymuszenie kopiowania argumentów, o ile tak się da, ale to może kod mocno spowolnić.

, że programy funkcyjne zwykle dają się zrównoleglić mniejszym wysiłkiem niż imperatywne, bo łatwiej wnioskować o ich poprawności.

Rzeczywiście łatwiej wnioskować o poprawności, ale programy funkcyjne są równoległe same z siebie i jest to cecha niezależna.

Co do modelu actorów w Scali, to nie ma powodów, aby nie skalował się do wielu milionów aktorów tak samo jak w Erlangu. Podajesz cały czas argumenty z niewiedzy (albo z "osobistego niedowiarstwa").

Chyba nie do końca zrozumiałeś o co mi chodzi. Wirtualny wątek (aktor) nie powinien mieć przełożenia na rzeczywisty wątek, tylko vm sam powinien dzielić cały program na najodpowiedniejszą liczbę wątków. A z punktu widzenia vm aktor powinien być takim samym kodem jak każdy inny.
(zauważ, że model aktorów spokojnie można zrealizować w leniwym języku bez wątków -> to tylko model projektowy)

To jest inny sposób postrzegania programów - jako przekształcenia funkcyjne niemutowalnego stanu, zamiast sekwencji modyfikacji stanu mutowalnego.

Ale programowanie imperatywne ujęte w ten sposób to po prostu funkcja po czasie, "iterate f (0, stanpoczatkowy)". Postrzeganie jest takie same, tylko w funkcyjnym łatwiej się pisze.

Jak monady, których nie użyjesz w Haskellu, mogą popsuć coś ze skalowalnością?

Monady? Masz na myśli chyba tylko IO?

PS http://www.comlab.ox.ac.uk/ralf.hinze/WG2.8//26/slides/manuel.pdf slajdy do jakiegoś projektu automatycznego zrównoleglania na gpu kodu w haskellu (pod koniec o tym mówią). Chyba jeszcze nic takiego nie wyszło, ale teoretycznie jak najbardziej można to zrobić. Mając modyfikowalne zmienne nie.
I dlatego języki czysto funkcyjne muszą wygrać.
Swoją drogą gdyby haskell dostał teraz coś takiego jako automatyczne zrównoleglanie obliczeń nagle każdy by tego używał...

0

Dziękuję wszystkim za odpowiedzi.
Ogólnie poczułem się zachęcony do Scali, jak znajdę czas pobawię się nią na Androidzie, ciekawe co z tego wyjdzie. Stronę: http://programming-scala.labs.oreilly.com/ można traktować jako dobry wstęp?
A F# ktoś się bawił, ma jakieś spostrzeżenia?
Co do Erlanga - wydaje się faktycznie ciekawy z tego co poczytałem, jednak póki co nie widzę dla niego zastosowania(w sensie gdzie sam mógłbym go praktycznie wykorzystać), a na naukę dla przyjemności nie zawsze niestety można znaleźć czas.

0

Istotą programów funkcyjnych jest równoległość (leniwość), co za tym idzie kompilator nie musi nic zrównoleglać (może, ale to optymalizacja).

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ć.

Nic nie szkodzi Ci w Scali napisać mapP albo reduceP, działających równolegle, dokładnie tak samo jak w Haskellu. Natomiast kompilator ma zwykle za mało informacji, aby móc pewnie zadecydować, która wersja będzie lepsza. Po pierwsze wersja równoległa na jednordzeniowym kompie będzie zawsze wolniejsza, po drugie wersja równoległa nawet na wielordzeniowym kompie będzie wolniejsza dla małych zbiorów danych. Dlatego sensowne jest pozostawienie tego programiście. 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

Chyba jeszcze nic takiego nie wyszło, ale teoretycznie jak najbardziej można to zrobić. Mając modyfikowalne zmienne nie.

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ć.

Parę dziur w Twoim "dowodzie":

przecież już zwykła tablica jest przekazywana przez referencję i nigdy nie wiadomo, czy przypadkiem nie trafi do czegoś, co ją zmieni.

  1. Jak to nie wiadomo? Kompilator bardzo dobrze WIE, gdzie ta referencja jest przekazywana. Wystarczy popatrzeć na wywołania.
  2. Jeśli tablica jest strukturą niemutowalną, to nic nie ma szans jej zmienić. Struktury niemutowalne w Scali są w powszechnym użyciu.

Żeby kompilator mógł oznaczyć wszystkie obiekty, musiałby sprawdzić wszystkie możliwe stany programu, żeby określić maksymalny zasięg danego obiektu.

Dwie bzdury w jednym:

  1. Nie musi. Wystarczy, że obiekty są wszędzie zadeklarowane jako stałe i są niemutowalnej klasy.
  2. 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

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. Tak samo jak języki czysto OOP się nie przyjęły. Przyszłość leży w językach, które potrafią w miarę bezboleśnie połączyć różne paradygmaty. Scala i F# IMHO rokują najlepiej w tej kwestii (choć F# znam tylko z opisów, a w Scali ostatnio dużo programuję)

0

A ktoś pokusi się o porównanie IDE do F# z IDE do Scali?

0

Ja mogę napisać tylko, że do Scali są co najmniej 3 IDE. Używałem 2 z nich: Eclipse jest beznadziejny, lepiej nie tykaj, dopóki nie wyjdzie stabilna wtyczka do Scala 2.8, za to IntelliJ IDEA wymiata (jak na wczesne IDE). Nie ma poważnych bugów, działa podpowiadanie i sprawdzanie składni, działa nawigacja Ctrl-click, działa tłumaczenie kodu Javy na Scalę oraz działa nawet podstawowy refactoring (z tego co się niedawno orientowałem, to VS nawet dla C# bez resharpera nie posiada takiej funkcjonalności; notabene resharper jest napisany też przez IntelliJ).

A, jeszcze co do porównania F# do Scali, to F# poszedł na całość jeśli chodzi o inferencję typów (pełna inferencja Hindleya-Milnera). Co oznacza, że dziedziczy pewne problemy z inferencją z języków rodziny ML (F# jest w sumie jednym z nich). Problemy te objawiają się tym, że błędy typów potrafią objawiać się w bardzo daleko od miejsca wystąpienia oraz ponieważ programista nie musi specyfikować praktycznie nigdzie typów, to programy prawie nie zawierają tych specyfikacji i trudniej się je czyta (choć za to łatwiej pisze).

Scala za to celowo wymusza specyfikowanie typów argumentów metod. Z jednej strony nieco bardziej rozgadany kod, ale czytelniejszy i łatwiej poprawić błędy ze zgodnością typów. Ot, zależy kto co lubi.

Podejrzewam, że ludzie z dużym doświadczeniem w językach dynamicznych będą woleli system typów F#, a przyzwyczajeni do statycznych typów, system typów Scali.

0
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.

  1. 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.

  1. 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:

  1. Nie musi. Wystarczy, że obiekty są wszędzie zadeklarowane jako stałe i są niemutowalnej klasy.
  2. 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ą.

0

Programowanie i tak jest tak banalne, że dałoby się tego nauczyć małpy, a małpy nie są od decydowania. (...) Programowanie wymaga podobnych umiejętności co układanie cegieł na budowie, z tą różnicą że trzeba nauczyć się składni na pamięć.

Jesteś pewien? No cóż, jeśli dla ciebie programowanie to w 100% składnia to nie mam słów...

0

Spokojnie, to podejście zapatrzonego w siebie teoretyka, który nie skalał by się pracą nad czymść użytecznym, bo za dużo tam hacków i zdrowego rozsądku.

0

@wojtekstrazak: chyba nie przeczytałeś zbyt dokładnie tego arta co Ci podlinkowałem.

Wiesz po co chcą dodać adnotację @pure do języka?
Nie po to, żeby powiedzieć kompilatorowi, że ten kawałek kodu jest "pure", tylko po to, żeby kompilator zweryfikował założenie programisty, że ten kawałek kodu jest "pure". Innymi słowy piszę kawałek kodu, który ma być czysto funkcyjny i oznaczam go jako "pure", ale kompilator sprawdza prawdziwość mojego założenia. Jeśli dostawię pure do kodu, którego kompilator nie jest w stanie udowodnić, że jest funkcyjny, to się nie skompiluje i mi o tym powie. Inaczej taka adnotacja narobiłaby więcej szkody niż pożytku - bo na pewno ktoś oznaczyłby jako "pure" kod posiadający efekty uboczne. Co oznacza tyle, że sam kompilator jest w stanie wygenerować adnotacje "pure" tam gdzie trzeba.

To jest dokładnie tak samo jak z adnotacją @tro, mówiącą, że wywołanie rekurencyjne ma być wywołaniem ogonowym. Bez tej adnotacji kompilator i tak wykona optymalizację, jeśli jest możliwa. Jednak z tą adnotacją, jeśli optymalizacja nie jest możliwa, kod się nie skompiluje. I programista zauważy, że jego założenie było nieprawidłowe.

Poza tym nigdzie nie pisałem, że programista ma "ręcznie" dzielić kod na równoległy. Pisałem, że musi nieznacznie pomóc, choćby przez sam sposób napisania algorytmu albo przez oznaczenie fragmentów adekwatnych do optymalizacji (np. na tej samej zasadzie co działa @tro), szczególnie w tych miejscach gdzie może to zmienić semantykę (np. reduceP ma inną semantykę niż reduce, jeśli nie zrobisz dodatkowych założeń). W każdym razie takie cuda, że wrzucasz program, który nie był pisany z myślą o równoległości i kompilator magicznie Ci go zrównolegla, można włożyć między bajki.

Masz tu kod, który wkleiłem gdzieś indziej na forum. Kod sprawdza poprawność nawiasów. Powiedz, jak kompilator miałby zrobić z tego wersję równoległą (a istnieje wersja równoległa dla tego problemu!)? Kod jest czysto funkcyjny:

val close2open = Map('(' -> ')', '[' -> ']', '{' -> '}', '<' -> '>') map (_.swap)
val open = Set() ++ bracketPairs.values

def check(s: Seq[Char], stack: Stack[Char] = Stack()): Boolean =
    s match {
      case Seq(b, rest @ _*) if open contains b =>
        check(rest, stack push b)
      case Seq(b, rest @ _*) if close2open contains b =>
        !stack.isEmpty && stack.top == close2open(b) && check(rest, stack.pop)
      case Seq() =>
        stack.isEmpty
    }

A kwestie przełożenia tego na wątki systemu, to zgadzam się, że jest domeną runtime'u, ale programista musi mieć na to wpływ. Nie zawsze wykorzystanie wszystkich rdzeni jest celem. Nie wiem skąd też wytrzasnąłeś założenie, że jeden actor w Scali = jeden wątek. W Scali możesz zrobić milion wirtualnych wątków na jednym wątku systemowym, dokładnie jak w Erlangu.

Co do automatycznego zarządzania pamięcią, to nie będę komentował... Ręce opadają.

a małpy nie są od decydowania

Oj, Ty chyba naprawdę nigdzie nie pracowałeś...

1 użytkowników online, w tym zalogowanych: 0, gości: 1