2011-02-06 12 views
11

[Pytanie to jest motywowane rozdziale 9 w "Real World Haskell"]

Oto prosta funkcja (wycięte do zasadniczych):

saferOpenFile path = handle (\_ -> return Nothing) $ do 
     h <- openFile path ReadMode 
     return (Just h) 

Dlaczego muszę że $?

Jeśli drugi argument do obsługi nie jest blokiem do, to nie jest potrzebny. Poniższe działa dobrze:

handle (\_ -> putStrLn "Error calculating result") (print x) 

Kiedy próbowałem usunięcie kompilację $ powiodło się. mogę zmusić go do pracy, jeśli jawnie dodać nawiasów, czyli

saferOpenFile path = handle (\_ -> return Nothing) (do 
     h <- openFile path ReadMode 
     return (Just h)) 

mogę zrozumieć, ale myślę, że oczekuję, że gdy Haskell uderza do powinien myśleć „Zaczynam blok” i nie powinniśmy musieć jawnie umieszczać tam $, aby rozbijać rzeczy.

Myślałem także o popychanie bloku zrobić, aby w następnej linii, tak:

saferOpenFile path = handle (\_ -> return Nothing) 
    do 
    h <- openFile path ReadMode 
    return (Just h) 

ale to nie zadziała bez parens albo. Że myli mnie, bo następujących prac:

add a b = a + b 

addSeven b = add 7 
    b 

jestem pewien, że będę po prostu dotrzeć do punktu, w którym ja przyjmuję je jako „to, jak piszesz idiomatyczne Haskell”, ale czy ktoś ma żadnych perspektyw, aby dać ? Z góry dziękuję.

+0

Przepraszam, ludzie, nie wiem, stackoverflow bardzo dobrze. Kod jest po prostu kosza. (Dlaczego nie ma "pytania do podglądu"). Spróbuję ponownie go opublikować. – Jonathan

+0

wystarczy nacisnąć przycisk "kod" na pasku narzędzi edytora (wygląda jak para nawiasów klamrowych). Również podgląd znajduje się tuż pod tym, gdzie piszesz, aktualizujesz w czasie rzeczywistym ... – Jon

+1

Ah, NoScript potrzebował zezwolenia na jeszcze jedną domenę! Teraz wygląda dobrze. Dzięki, Jon! – Jonathan

Odpowiedz

6

Według Haskell 2010 report, do desugars tak:

do {e;stmts} = e >>= do {stmts}

do {p <- e; stmts} = let ok p = do {stmts} 
         ok _ = fail "..." 
        in e >>= ok 

W drugim przypadku, to samo napisać to desugaring tak (i ​​łatwiejsze do celów demonstracyjnych):

do {p <- e; stmts} = e >>= (\p -> do stmts)

Więc załóżmy, że piszę to:

-- to make these examples shorter 
saferOpenFile = f 
o = openFile 

f p = handle (\_ -> return Nothing) do 
    h <- o p ReadMode 
    return (Just h) 

To desugars do tego:

f p = handle (\_ -> return Nothing) (o p ReadMode) >>= (\h -> return (Just h)) 

który jest taki sam jak ten:

f p = (handle (\_ -> return Nothing) (o p ReadMode)) >>= (\h -> return (Just h)) 

Choć prawdopodobnie przeznaczony na to oznaczać to:

handle (\_ -> return Nothing) ((o p ReadMode) >>= (\h -> return (Just h))) 

tl; dr - kiedy skreśla się składnię, nie zawiera w nawiasie całego stwierdzenia, jak byś myślał (Haskell 2010).

Ta odpowiedź jest tylko AFAIK i

  1. może nie być prawidłowe
  2. mogą być nieaktualne kiedyś, czy jest poprawny

Uwaga: jeśli mówisz do mnie „, ale rzeczywiste zastosowania desugaring "let", które powinno zgrupować e >>= ok, prawda? ", wtedy odpowiedziałbym tak samo, jak tl; dr dla instrukcji do zrobienia: nie nawiasyzuje/grupuje, jak myślisz, że robi.

+0

Cóż, myślę, że raport sprawia, że ​​ta odpowiedź jest ostateczna. Sądzę, że po prostu trudniej przyzwyczaić się do aplikacji funkcji stylu lambda w stylu lambda, o której myślałem. – Jonathan

+0

To naprawdę połączenie tej odpowiedzi i Callum, która wyciąga kompletną odpowiedź na twoje pytanie. Ten wyjaśnia, dlaczego '$' jest potrzebny, a drugi wyjaśnia, co robi. –

+2

To nie jest ta odpowiedź. Odsiaranie zawsze działa na gotowych abstrakcyjnych drzewach składniowych, co oznacza, że ​​wymaga "w pełni nawiasowego" wejścia i generuje "w pełni nawiasowy" wynik. Nie zmienia definicji tego, jak poprawne dane wejściowe wyglądają lub kiedy potrzebujesz nawiasów. (Jest to bardziej podobne do makr Lispa niż podobne do makr czytnika Lispa lub makr sekwencji tokenów C). –

10

Jest to spowodowane kolejnością operacji Haskella. Zastosowanie funkcji wiąże się najmocniej, co może być źródłem nieporozumień. Na przykład:

add a b = a + b 
x = add 1 add 2 3 

Haskell interpretuje to jako: funkcja dodana do 1, a funkcja dodaj. Prawdopodobnie nie jest to zamierzone przez programistę. Haskell narzeka, że ​​spodziewa się Numa za drugi argument, ale zamiast tego otrzymuje funkcję.

są dwa rozwiązania:

1) ekspresji można nawiasach:

x = add 1 (add 2 3) 

które Haskell interpretuje jako: Funkcja ADD stosowane do 1, to wartość dodać 2 3.

Ale jeśli zagnieżdżenie jest zbyt głębokie, może to być mylące, stąd drugie rozwiązanie.

2) $ Operator:

x = add 1 $ add 2 3 

$ jest operatorem, który stosuje funkcję do swoich argumentów. Haskell odczytuje to jako: funkcja (dodaj 1) zastosowana do wartości add 2 3. Pamiętaj, że w Haskell funkcje mogą być częściowo zastosowane, więc (dodaj 1) jest doskonale prawidłową funkcją jednego argumentu.

Operator $ można stosować wielokrotnie:

x = add 1 $ add 2 $ add 3 4 

Które rozwiązanie wybrać zostaną określone przez który waszym zdaniem jest bardziej czytelny w określonym kontekście.

+3

Dzięki, Callum. Domyślam się, że ($) dotyczyło głównie składni, gdy próbowałem napisać jej definicję i wymyśliłem ($) f x = f x. Z moich ograniczonych źródeł wydaje się, że Haskellers naprawdę lubią unikać paren, o ile to możliwe. Sądzę, że to po prostu ciężko pochodzić z innych języków, gdzie jestem dobry w czytaniu add (1, add (3, 4)). Zwłaszcza, gdy używane są kombinatory – Jonathan

10

To właściwie nie jest specyficzne dla do -notacja. Nie możesz również pisać takich rzeczy, jak print if x then "Yes" else "No" lub print let x = 1+1 in x*x.

Można to sprawdzić z definicji gramatyki na początku chapter 3 of the Haskell Report: wyrażenia do lub warunkowego lub wyrażenia let jest lexp, ale argument z zastosowania funkcji jest aexp, i lexp jest generalnie niepoprawny jako na wyrostek.

To nie wyjaśnia, dlaczego wybór został dokonany w Raporcie, oczywiście. Gdybym miał zgadywać, mógłbym przypuszczać, że chodzi o rozszerzenie użytecznej reguły "funkcja aplikacji wiąże się mocniej niż cokolwiek innego" z wiązaniem między, powiedzmy, do i jego blokiem.Ale nie wiem, czy to była pierwotna motywacja. (Uważam, że odrzucone formularze, takie jak print if x then ..., są trudne do odczytania, ale może to być spowodowane tym, że jestem przyzwyczajony do czytania kodu, który Haskell akceptuje.)

Powiązane problemy