2012-12-07 19 views
14

Wolę trzymać się jak najbliżej funkcjonalnego paradygmatu, ściskając tak blisko, jak to tylko możliwe, do czysto funkcjonalnego, gdy mój mózg jest gotowy na wyzwanie. Używam F #, kiedy to możliwe. Zazwyczaj utknąłem z VB.NET lub C# (lub VBA, kiedy mam naprawdę pecha). Tak więc moje języki pozwalają mi odejść dość daleko od podejścia funkcjonalnego.Logowanie w ramach paradygmatu programowania funkcjonalnego

Historycznie ignorowałem logowanie i komunikowanie się z użytkownikiem, dopóki nie uzyskałem wyniku - po prostu pozwól użytkownikowi czekać. Teraz próbuję zaimplementować rejestrowanie i/lub aktualizację pasków stanu. To proste, ponieważ moje języki pozwalają mi pisać na standardowe wyjście, kiedy tylko chcę. Ale czy z czysto funkcjonalnego punktu widzenia chodzi o wyciek informacji o tym, co dzieje się wewnątrz swojej funkcji na zewnątrz? Czy logowanie lub komunikowanie się z użytkownikiem podczas obliczeń jest po prostu sprzeczne z czysto funkcjonalnym podejściem?

Jestem pewien, że w Haskell można by użyć Monady. A co powiesz na używanie innych języków?

Dzięki.

+0

'Wolę trzymać się jak najbliżej funkcjonalnego paradygmatu, ściskając jak blisko jak mogę dostać się do czysto funkcjonalnego, gdy mój mózg jest gotowy na wyzwanie "użyj go, gdy daje ci to jakąś korzyść. Czysty kod funkcjonalny często może przyczyniać się do przypadkowej złożoności systemu. –

+0

To chyba dobra rada ... –

Odpowiedz

8

Rzućmy okiem na rozwiązania monadycznej Haskell'a. Logika polega na tym, że nasze obliczenia mają dodatkową metodę, która zapisuje wiadomość gdzieś "na zewnątrz". Istnieje wiele sposobów, w jaki sposób do reprezentowania tych obliczeń, ale jednym z najbardziej ogólnym jest, aby monady:

class (Monad m) => MonadWriter w m | m -> w where 
    tell :: w -> m() 

Rodzaj w reprezentuje wiadomości i funkcja tell jest co „wysyła” wiadomość do monadycznego (wywołujących efekty pełne) obliczenia.

Uwagi:

  • Haskell'a MonadWriter faktycznie bogatszy, zawiera funkcje, które pozwalają badać i modyfikować w, ale trzymajmy że na razie na bok.
  • Część | m -> w nie jest tak ważna dla wyjaśnienia, po prostu oznacza, że ​​w jest ustalone dla danego m.

Najczęściej używaną implementacją jest Writer, czyli w zasadzie tylko para.Jednym z jego elementów jest wynik obliczeń, a drugim elementem jest ciąg pisemnych komunikatów. (Właściwie to nie jest sekwencja, jest bardziej ogólna - monoid, która definiuje operacje łączenia wielu wiadomości w jeden.) Możesz zbadać rozwiązanie Haskella, patrząc na Writer module. Jednak jest napisany bardziej ogólnie, przy użyciu transformatora monadowego WriterT, więc jeśli nie jesteś fanem monady, może to być dość trudne do odczytania. To samo można zrobić w innych językach funkcjonalnych, na przykład patrz this example in Scala.

Istnieją jednak inne możliwe, bardziej zorientowane na efekty (nadal funkcjonalne) implementacje powyższej klasy typów. Możemy zdefiniować tell emitować komunikaty do jakiegoś zewnętrznego zlewu, jak na standardowe wyjście do pliku, itd. Na przykład:

{-# LANGUAGE FunctionalDependencies, TypeSynonymInstances, FlexibleInstances #-} 

instance MonadWriter String IO where 
    tell = putStrLn 

Tutaj mówimy, że IO może być używany jako obiekt rejestrowania że pisze String s do stdout . (Jest to tylko uproszczony przykład, pełna implementacja prawdopodobnie zawierałaby transformator monad, który dodawałby funkcję tell do dowolnej monady opartej na).

+0

Klasy i przedmioty? Whaaat? To nie jest funkcjonalny paradygmat programowania, to tylko OOP w FP. –

+4

Nie widzę punktu, klasy typu w Haskell są zupełnie inne od klas OO. –

+0

Dziękuję. Być może też nie widzę tego. W tej odpowiedzi nie ma przypadku użycia klasy. Skoncentruj się na stronie logowania użytkownika zamiast biblioteki. Dziękuję Ci! –

3

Jestem nowym do programowania funkcjonalnego, ale tutaj jest próbą w Scala:

object FunctionalLogging { 

    type Result = Int 

    class ResultWithLogging(val log: List[String], val result: Result) {} 

    def functionWithLogging(log: List[String], arg: String): ResultWithLogging = { 
    def function(arg: String): Result = arg.length 

    new ResultWithLogging(log :+ ("Calling function(" + arg +")"), function(arg)) 
    } 

    val result = functionWithLogging(List(), "Hello world!") 

    // -- Pure functional code ends here -- 
    println("Result = " + result.result) 
    println("Log = " + result.log) 
} 

Jest funkcjonalny w tym że nie ma żadnych skutków ubocznych, ale oczywiście dziennika jest częścią argumentów funkcji i powrotu, tak to nie jest zbyt eleganckie ani praktyczne.

Wydaje mi się, że logowanie jest pożądanym efektem ubocznym z definicji, więc jeśli zgadzasz się z moją definicją, pytanie brzmi: jak wyizolować niefunkcjonalny z kodu funkcjonalnego. W praktyce prawdopodobnie zacznę od obiektu Scala (prawdopodobnie zbyt wiele jak singleton - cecha jest prawdopodobnie lepsza Scala) lub aktora do gromadzenia komunikatów logowania i robienia wszystkiego, co trzeba z nimi zrobić.

To jest bardziej pragmatyczny pogląd: Logging in Scala

Edytuj

to pytanie mówi o Haskell monady i IO: What other ways can state be handled in a pure functional language besides with Monads?

+0

Podoba mi się twój proces myślenia tutaj. W moim przypadku funkcja była jednak dość długa i chciałem zgłosić ją jako przetworzoną, zamiast czekać na jej powrót, aby zgłosić swój dziennik. Niemniej jednak, jeśli nikt nie odpowie na moje pytanie, prawdopodobnie oznaczy to jako odpowiedź, ponieważ podczas gdy odpowiedź Haskell jest świetna, moje pytanie dotyczyło raczej podejścia nie-Monad. Dzięki! –

Powiązane problemy