Curry — gdy programowanie funkcyjne spotyka logike

Definicja pojęcia Curry — gdy programowanie funkcyjne spotyka logike

Czym jest Curry i dlaczego warto go poznać

Curry to funkcyjno-logiczny język programowania, który łączy dwa paradygmaty: programowanie funkcyjne (znane z Haskella czy ML) i programowanie logiczne (znane z Prolog). Nazwa nie jest przypadkowa — to hołd dla Haskella Curry’ego, logika i matematyka, którego prace stanowiły fundament rachunku lambda i teorii obliczalności.

Wyobraźcie sobie język, w którym możecie pisać funkcje jak w Haskellu, ale też definiować relacje i pozwalać komputerowi na „zgadywanie” brakujących wartości — jak w Prolog. Chcecie znaleźć wszystkie x, dla których f(x) = 42? W Curry to naturalne pytanie, a nie hack z odwracaniem programu.

Curry jest szczególnie interesujący w kontekście współczesnych dyskusji o probabilistycznym programowaniu, programowaniu z ograniczeniami (constraint programming) i symbolicznym rozwiązywaniu problemów. Wiele z tego, co dziś robimy za pomocą bibliotek i frameworków, Curry oferuje natywnie w samym języku.

Krótka historia i ewolucja

Curry powstał w latach 90. jako efekt współpracy kilku grup badawczych, głównie Michaela Hanusa z University of Kiel i Sergio Antoy z Portland State University. Celem było stworzenie zunifikowanego języka, który integruje programowanie funkcyjne i logiczne w spójny sposób, bez sztucznego rozdzielania tych paradygmatów.

Pierwsze wersje Curry bazowały na wcześniejszych próbach integracji tych paradygmatów — językach takich jak ALF, Babel i LPG. Curry wyróżniał się czystszym designem i lepszą semantyką operacyjną. Kluczowym wkładem była strategia obliczeniowa zwana needed narrowing, która łączyła leniwe wartościowanie z poszukiwaniem logicznym w sposób optymalny.

W 2016 roku powstał PAKCS (Portland Aachen Kiel Curry System) jako główna implementacja, a później KiCS2 (Kiel Curry System) bazujący na Haskellu jako języku implementacji. Dziś Curry jest aktywnie rozwijany, z regularnymi wydaniami i rosnącą bazą bibliotek. Społeczność jest mała, ale oddana i skupiona wokół corocznych warsztatów Curry Workshop.

Kluczowe cechy i filozofia

Curry jest leniwym, funkcyjno-logicznym językiem z silnym systemem typów zapożyczonym z Haskella. Składnia jest niemal identyczna z Haskell — jeśli znacie Haskella, poczujecie się jak w domu. Ale semantyka jest znacznie bogatsza.

Nondeterminizm to serce Curry. Funkcja w Curry może zwracać wiele wyników — nie przez zwracanie listy, ale przez nondeterministyczne „rozgałęzianie” obliczeń. Na przykład:

coin = 0
coin = 1

Ta definicja mówi, że coin to 0 lub 1. Wywołanie coin w kontekście, który tego wymaga, przeszuka obie możliwości. To nie jest pattern matching ani lista — to fundamentalna cecha semantyki języka.

Free variables (wolne zmienne) to kolejny kluczowy koncept. W Curry możecie mieć zmienne, które nie są jeszcze związane z żadną wartością, i pozwalać systemowi na znalezienie odpowiednich podstawień. To jest dokładnie to, co robi Prolog, ale w Curry jest zintegrowane z funkcyjnym systemem typów.

Encapsulated search pozwala na kontrolowanie nondeterminizmu — możecie „zamknąć” przeszukiwanie w określonym kontekście i zebrać wyniki jako listę, drzewo decyzyjne albo inną strukturę. To daje programiście kontrolę nad tym, jak i gdzie odbywa się poszukiwanie.

Funkcje wyższego rzędu działają w Curry tak samo jak w Haskellu — możecie mapować, foldować i komponować funkcje. Ale te same operacje mogą działać na nondeterministycznych wartościach, co otwiera zupełnie nowe możliwości.

Gdzie Curry sprawdza się najlepiej

Curry błyszczy wszędzie tam, gdzie trzeba rozwiązywać problemy z przestrzenią poszukiwań: planowanie, harmonogramowanie, rozwiązywanie zagadek logicznych, weryfikacja własności programów. Tam, gdzie w Haskellu musielibyście ręcznie implementować backtracking albo używać monady Logic, w Curry dostajecie to za darmo.

Jednym z najciekawszych zastosowań Curry jest generatywne testowanie — możecie zdefiniować specyfikację funkcji i pozwolić Curry na znalezienie wejść, które ją łamią. To jest jak QuickCheck, ale z wbudowanym solverem zamiast losowego generowania.

Curry bywa też używany do budowania systemów ekspertowych i prototypowania algorytmów, gdzie nondeterminizm jest naturalnym modelem problemu. W edukacji służy do nauczania studentów o związkach między logiką a programowaniem.

Jak wygląda kod w Curry

Klasyczny przykład — rozwiązywanie zagadki SEND + MORE = MONEY:

solve s e n d m o r y =
    s * 1000 + e * 100 + n * 10 + d
    + m * 1000 + o * 100 + r * 10 + e
    =:= m * 10000 + o * 1000 + n * 100 + e * 10 + y
    where
        digits = [0..9]
        s =:= digit digits
        e =:= digit (digits \ [s])
        -- itd. (reszta zmiennych z ograniczeniami)

Operator =:= to unifikacja — Curry próbuje znaleźć wartości zmiennych, które spełniają równanie. To nie jest porównanie ani przypisanie — to logiczna koniunkcja ograniczeń.

A oto prostszy przykład z nondeterminizmem:

insert x []     = [x]
insert x (y:ys) = x : y : ys
insert x (y:ys) = y : insert x ys

perm []     = []
perm (x:xs) = insert x (perm xs)

Funkcja perm generuje wszystkie permutacje listy — nie przez iterację, ale przez nondeterministyczne wstawianie elementów. Każde wywołanie insert „rozgałęzia się”, tworząc wiele możliwych wyników.

Ekosystem i narzędzia

Głównym środowiskiem Curry jest PAKCS — kompletny system z interpreterem, kompilatorem (do Prolog/SICStus), REPL-em i zestawem narzędzi. KiCS2 to alternatywna implementacja kompilująca do Haskella, oferująca lepszą wydajność dla pewnych klas programów.

Biblioteka standardowa Curry jest zaskakująco bogata — obejmuje moduły do pracy z listami, I/O, siecią, bazami danych i GUI. Curry Package Manager (CPM) pozwala na instalację pakietów zewnętrznych. Istnieją też biblioteki do programowania z ograniczeniami (CLP), parsowania i testowania.

Dla edytorów: istnieje wsparcie dla Curry w Emacsie (curry-mode), Vimie i VS Code (rozszerzenie Curry Language Server). Dokumentacja jest dostępna na stronie curry-lang.org i obejmuje zarówno tutorial, jak i raport językowy.

Mocne i słabe strony

Największą siłą Curry jest elegancka integracja paradygmatów. Nondeterminizm i unifikacja nie są „dodane” do języka funkcyjnego — są z nim organicznie splecione. System typów Haskella zapewnia bezpieczeństwo, a mechanizmy logiczne zapewniają ekspresyjność. To naprawdę działa.

Słabą stroną jest wydajność. Przeszukiwanie nondeterministyczne ma swoją cenę, i dla dużych przestrzeni poszukiwań Curry może być wolniejszy niż dedykowane solvery (jak Z3 czy MiniSat). Nie jest to język do budowania systemów produkcyjnych o wysokiej wydajności.

Społeczność i ekosystem to kolejne ograniczenia. Curry ma aktywną, ale małą bazę użytkowników. Znalezienie pracy w Curry jest niemal niemożliwe, a większość projektów to badania akademickie lub hobby.

Podsumowanie

Curry to język dla tych, którzy czują się ograniczeni jednym paradygmatem. Jeśli kochacie Haskella, ale czasem marzycie o Prolog — Curry jest mostem między tymi światami. Nondeterminizm jako natywna cecha języka to potężna idea, która zasługuje na szersze uznanie.

Dla badaczy, studentów i ciekawskich programistów Curry oferuje unikalną perspektywę na to, czym może być programowanie. Kto wie — może za kilka lat nondeterminizm i unifikacja trafią do mainstreamu, tak jak monady i type classes trafiły z Haskella do innych języków.

Pozostałe definicje

Curry to funkcyjno-logiczny jezyk programowania laczacy paradygmat funkcyjny z programowaniem logicznym jak w Prologu. Poznaj idee i zastosowania.
Scroll to Top
Framework
Języki Programowania
Stanowisko
Co to jest