Programowanie funkcyjne - praktyka vs teoria

1

Powoli przebijam się przez Category Theory for Programmers Bartosza Milewskiego i mam wrażenie, że omawiane są podstawy teoretyczne dla konstruktów udostępnianych przez funkcyjne języki programowania. Dzięki temu wiemy jak dany konstrukt się zachowuje i dlaczego tak jest.

a) Czy w rozwoju języków funkcyjnych jest tak, że są rozważane jakieś struktury algebraiczne, a na tej podstawie powstają nowe funkcjonalności języka? Innymi słowy, skąd się biorą nowe języki funkcyjne? W OOP raczej nic nowego nie wymyślono (pomijając argumenty padające we flame wars). W przypadku funkcyjnych wydaje mi się, że rozwój teorii matematycznej jest takim czynnikiem warunkującym rozwój języka. Ale może nie jest to prawda i jest zupełnie inaczej?

b) Czy czytając jakiś kod źródłowy macie obserwacje typu 'o k**a, przecież to profunktor i dlatego XYZ nie będzie się komponować z ABC, muszę zrewidować rozwiązanie'? Czy raczej z perspektywy użytkownika języka (ale nie będącego projektantem języka) nie ma miejsca na takie rozważania? ;)

c) Pytanie do osób, które tworzą bibliotek w językach funkcyjnych. Na ile teoria kategorii/algebra jest przydatna, do tworzenia ogólnych rozwiązań? Mam na myśli przejście z rozwiązania z jakiegoś konkretnego problemu (używając analogii z OOP i np. C++ - mamy klasę), do poziomu powiedzmy "średniego", gdzie uogólniamy pewne rzeczy (klasa -> szablon) , na poziom "wyższy", gdzie jest już totalna abstrakcja (klasa -> template -> template of template - tu nie wiem czy C++ wspiera takie pomysły, ale idea mam nadzieję zrozumiała).

1
yarel napisał(a):

a) Czy w rozwoju języków funkcyjnych jest tak, że są rozważane jakieś struktury algebraiczne, a na tej podstawie powstają nowe funkcjonalności języka?

Tak. Sprawdź sobie Curry-Howard correspondence

Innymi słowy, skąd się biorą nowe języki funkcyjne?

Najczęściej ktoś pisze doktorat. Teraz na topie jest Homotopy type theory.

W przypadku funkcyjnych wydaje mi się, że rozwój teorii matematycznej jest takim czynnikiem warunkującym rozwój języka. Ale może nie jest to prawda i jest zupełnie inaczej?

Nie do końca tak jest. Obie dziedziny rozwijają się równocześnie, a po prostu czasami progres w jednej stymuluje drugą. Na przykład funktory, aplikatywy (nie mam pojęcia jak tłumaczy się applicative) i monady pojawiły się w Haskellu zanim ludzie się zorientowali, że te typy mapują się na pojęcia z teorii kategorii i przez to na przykład w starych wersjach Haskella monada nie dziedziczyła z aplikatywy.

b) Czy czytając jakiś kod źródłowy macie obserwacje typu 'o k**a, przecież to profunktor i dlatego XYZ nie będzie się komponować z ABC, muszę zrewidować rozwiązanie'? Czy raczej z perspektywy użytkownika języka (ale nie będącego projektantem języka) nie ma miejsca na takie rozważania? ;)

W skrócie tak. Chociaż częściej to chyba przybiera postać "Dlaczego ten !$%#!$! nie zaimplementował tego jako funktora/monady/Bóg wie czego". ;) Nie ma w tym zresztą nic nadzwyczajnego. Masz po prostu pewien kontrakt na zachowanie konkretnych typów i tyle.

c) Pytanie do osób, które tworzą bibliotek w językach funkcyjnych. Na ile teoria kategorii/algebra jest przydatna, do tworzenia ogólnych rozwiązań? Mam na myśli przejście z rozwiązania z jakiegoś konkretnego problemu (używając analogii z OOP i np. C++ - mamy klasę), do poziomu powiedzmy "średniego", gdzie uogólniamy pewne rzeczy (klasa -> szablon) , na poziom "wyższy", gdzie jest już totalna abstrakcja (klasa -> template -> template of template - tu nie wiem czy C++ wspiera takie pomysły, ale idea mam nadzieję zrozumiała).

Nie pisałem bibliotek w jęz. funkcyjnych, w dodatku nie specjalnie rozumiem pytanie, ale chyba mogę powiedzieć, że jest przydatna. Możesz sobie prześledzić jak powstawała biblioteka Lenses do Haskella i jak typy akcesorów po prostu wyprowadzono z konkretnego układu równań. Nawet Milewski ma o tym blog post. To jednak tyczy się raczej znajdowania użytecznych "interfejsów". Relacja klasa -> template jest na zupełnie innym poziomie.

1
yarel napisał(a):

a) Czy w rozwoju języków funkcyjnych jest tak, że są rozważane jakieś struktury algebraiczne, a na tej podstawie powstają nowe funkcjonalności języka?

Tak i nie. Często jest to stosowanie już istniejących struktur do tego by dosłownie - zabronić więcej. Przez to ograniczamy ilość potencjalnych możliwych programów, ale jednocześnie ograniczamy ilość potencjalnych błędów.

Innymi słowy, skąd się biorą nowe języki funkcyjne?

Bo ktoś ma nowy koncept czy zastosowanie (Agda, Coq), chce zaimplementować programowanie funkcyjne na innej platformie (Elm), czy chce rozwinąć istniejącą platformę o funkcjonalności czy składnię (Idris, Elixir, Gleam).

W OOP raczej nic nowego nie wymyślono (pomijając argumenty padające we flame wars).

Jak w większości programowania, po prostu niektórzy znajdują nowe zastosowania lub podejścia.

W przypadku funkcyjnych wydaje mi się, że rozwój teorii matematycznej jest takim czynnikiem warunkującym rozwój języka. Ale może nie jest to prawda i jest zupełnie inaczej?

Nie do końca, bo masz kontr przykłady jak Lispy (Scheme, Clojure, CL, LFE), Erlang, Elixir, czy Pony. One nie opierają się na rozwoju teorii matematycznej, a dalej są językami funkcyjnymi.

b) Czy czytając jakiś kod źródłowy macie obserwacje typu 'o k**a, przecież to profunktor i dlatego XYZ nie będzie się komponować z ABC, muszę zrewidować rozwiązanie'?

Czasami trochę tak, ale programowanie funkcyjne nie wymaga silnych typów, więc nie zawsze.

Czy raczej z perspektywy użytkownika języka (ale nie będącego projektantem języka) nie ma miejsca na takie rozważania? ;)

Różnie bywa i zależy od tego co to za język i co to za projekt. Czasami warto więcej czasu spędzić na dobrym rozplanowaniu typów, ale nie zawsze jest to ekonomiczne czasowo.

c) Pytanie do osób, które tworzą bibliotek w językach funkcyjnych. Na ile teoria kategorii/algebra jest przydatna, do tworzenia ogólnych rozwiązań?

Różnie. Jak mówiłem, zależy od języka i projektu.

Mam na myśli przejście z rozwiązania z jakiegoś konkretnego problemu (używając analogii z OOP i np. C++ - mamy klasę), do poziomu powiedzmy "średniego", gdzie uogólniamy pewne rzeczy (klasa -> szablon) , na poziom "wyższy", gdzie jest już totalna abstrakcja (klasa -> template -> template of template - tu nie wiem czy C++ wspiera takie pomysły, ale idea mam nadzieję zrozumiała).

Nie za bardzo rozumiem o co Ci tutaj chodzi, w takim Haskellu typeclass (czyli wszystkie Functor, Monad, Comonad, etc.) to ekwiwalent interfejsów w OOP (w pewnym skrócie), więc nie za bardzo wiem gdzie tutaj "wyższość". To samo masz w OOP w postaci wzorców projektowych.

0
hauleth napisał(a):

...

Mam na myśli przejście z rozwiązania z jakiegoś konkretnego problemu (używając analogii z OOP i np. C++ - mamy klasę), do poziomu powiedzmy "średniego", gdzie uogólniamy pewne rzeczy (klasa -> szablon) , na poziom "wyższy", gdzie jest już totalna abstrakcja (klasa -> template -> template of template - tu nie wiem czy C++ wspiera takie pomysły, ale idea mam nadzieję zrozumiała).

Nie za bardzo rozumiem o co Ci tutaj chodzi, w takim Haskellu typeclass (czyli wszystkie Functor, Monad, Comonad, etc.) to ekwiwalent interfejsów w OOP (w pewnym skrócie), więc nie za bardzo wiem gdzie tutaj "wyższość". To samo masz w OOP w postaci wzorców projektowych.

Chodziło mi o stopniowe uogólnianie rozwiązania:

  1. oryginalny problem: dodawanie dwóch liczb całkowitych ( Int -> Int -> Int )
  2. uogólniamy 1) do szablonu, tj. dodawania dwóch liczb określonego typu
  3. uogólniamy 2) do wywołania operatora dwuargumentowego operującego na jakimś typie T

I tego na ile teoria jest tu przydatna i czy na pewnym etapie stwierdzamy, że właściwie coś takiego "bardzo ogólnego" już istnieje i nie musimy odkrywać koła na nowo, tylko zauważyć, że tak naprawdę jest to koło ;) Nie mam doświadczenia ani odpowiedniej podstawy teoretycznej, żeby to lepiej sformułować. Podejrzewam, że z czasem będzie to oczywiste, albo padną jakieś pytania doprecyzowujące, które pozwolą mi sobie znaleźć odpowiedź.

Dzięki za podjęcie tematu.

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