O co chodzi z Haskellem? [Zamknięte]


109

Znam kilku programistów, którzy ciągle rozmawiają o Haskellu, kiedy są między sobą, a tutaj na SO wszyscy wydają się kochać ten język. Bycie dobrym w Haskell wydaje się być znakiem rozpoznawczym genialnego programisty.

Czy ktoś może podać kilka przykładów Haskella, które pokazują, dlaczego jest tak elegancki / lepszy?

Odpowiedzi:


134

Sposób, w jaki mi się to podoba, i to, co myślę, jest prawdą po tym, jak przez miesiąc pracowałem nad nauką Haskell, to fakt, że programowanie funkcjonalne zmienia twój mózg w interesujący sposób: zmusza cię do myślenia o znanych problemach na różne sposoby : zamiast pętli, myśl w mapach, fałdach i filtrach, itp. Ogólnie rzecz biorąc, jeśli masz więcej niż jedną perspektywę na problem, dzięki temu będziesz lepiej rozumieć ten problem i zmieniać punkty widzenia w razie potrzeby.

Inną fajną rzeczą w Haskell jest jego system typów. Jest ściśle wpisany na maszynie, ale silnik wnioskowania o typie sprawia, że ​​czuje się jak program w Pythonie, który w magiczny sposób mówi ci, kiedy popełniłeś głupi błąd związany z typem. Komunikatów o błędach Haskella w tym względzie brakuje nieco, ale gdy lepiej zaznajomisz się z językiem, powiesz sobie: tak powinno być pisanie!


47
Należy zauważyć, że nie brakuje komunikatów o błędach Haskella, a ghc. Standard Haskell nie określa, w jaki sposób wyświetlane są komunikaty o błędach.
PyRulez,

Dla plebsu takich jak ja, GHC oznacza Glasgow Haskell Compiler. en.wikipedia.org/wiki/Glasgow_Haskell_Compiler
Lorem Ipsum

137

To przykład, który przekonał mnie, aby dowiedzieć się, Haskell (chłopiec i jestem zadowolony, zrobiłem).

-- program to copy a file --
import System.Environment

main = do
         --read command-line arguments
         [file1, file2] <- getArgs

         --copy file contents
         str <- readFile file1
         writeFile file2 str

OK, to krótki, czytelny program. W tym sensie jest lepszy niż program w C. Ale czym to się tak różni od (powiedzmy) programu w Pythonie o bardzo podobnej strukturze?

Odpowiedź to leniwa ocena. W większości języków (nawet niektórych funkcjonalnych) program o strukturze podobnej do powyższego spowodowałby załadowanie całego pliku do pamięci, a następnie ponowne zapisanie pod nową nazwą.

Haskell jest „leniwy”. Nie oblicza rzeczy, dopóki nie musi, a co za tym idzie , nie oblicza rzeczy, których nigdy nie potrzebuje. Na przykład, gdybyś usunął tę writeFilelinię, Haskell nie zawracałby sobie głowy czytaniem czegokolwiek z pliku.

W obecnej sytuacji Haskell zdaje sobie sprawę, że writeFilezależy od readFile, więc jest w stanie zoptymalizować tę ścieżkę danych.

Chociaż wyniki są zależne od kompilatora, to zwykle dzieje się po uruchomieniu powyższego programu: program wczytuje blok (powiedzmy 8KB) pierwszego pliku, następnie zapisuje go w drugim pliku, a następnie odczytuje kolejny blok z pierwszego plik i zapisuje go w drugim pliku i tak dalej. (Spróbuj stracena nim biegać !)

... co wygląda bardzo podobnie do tego, co zrobiłaby wydajna implementacja kopii pliku w C.

Tak więc Haskell umożliwia pisanie zwartych, czytelnych programów - często bez poświęcania dużej wydajności.

Kolejną rzeczą, którą muszę dodać, jest to, że Haskell po prostu utrudnia pisanie błędnych programów. Niesamowity system typów, brak efektów ubocznych i oczywiście zwartość kodu Haskell redukuje błędy z co najmniej trzech powodów:

  1. Lepszy projekt programu. Mniejsza złożoność prowadzi do mniejszej liczby błędów logicznych.

  2. Kompaktowy kod. Mniej wierszy, w których mogą występować błędy.

  3. Błędy kompilacji. Wiele błędów po prostu nie jest poprawnych Haskell .

Haskell nie jest dla każdego. Ale każdy powinien spróbować.


Jak dokładnie zmieniłbyś stałą 8KB (lub cokolwiek to jest)? Bo założę się, że implementacja Haskella byłaby wolniejsza niż wersja C w innym przypadku, zwłaszcza bez wstępnego pobierania ...
user541686

1
@Mehrdad Możesz zmienić rozmiar bufora za pomocą hSetBuffering handle (BlockBuffering (Just bufferSize)).
David

3
To niesamowite, że ta odpowiedź ma 116 głosów pozytywnych, ale to, co w niej jest, jest po prostu złe. Ten program będzie czytać całego pliku, chyba że używasz leniwe Bytestrings (co można zrobić z Data.Bytestring.Lazy.readFile), które nie mają nic wspólnego z Haskell jest leniwy (nie surowe) język. Monady sekwencjonują - oznacza to z grubsza, że ​​„wszystkie skutki uboczne są wykonywane, gdy usuniesz wynik”. A jeśli chodzi o magię „leniwego Bajtestru”: jest to niebezpieczne i możesz to zrobić z podobną lub prostszą składnią w większości innych języków.
Jo So,

14
Nudny stary standard readFilerównież wykonuje leniwe IO w ten sam sposób Data.ByteString.Lazy.readFile. Zatem odpowiedź nie jest zła i nie jest to jedynie optymalizacja kompilatora. Rzeczywiście, jest to część specyfikacji dla Haskell : „ readFileFunkcja czyta plik i zwraca zawartość pliku jako łańcuch. Plik jest czytany leniwie na żądanie, tak jak w przypadku getContents.”
Daniel Wagner

1
Myślę, że inne odpowiedzi wskazują na rzeczy, które są bardziej wyjątkowe w Haskellu. Wiele języków / środowisk mają strumieni, można zrobić coś podobnego w węźle: const fs = require('fs'); const [file1, file2] = process.argv.slice(2); fs.createReadStream(file1).pipe(fs.createWriteStream(file2)). Bash ma też coś podobnego:cat $1 > $2
Max Heiber

64

Zadajesz złe pytanie.

Haskell nie jest językiem, w którym patrzysz na kilka fajnych przykładów i mówisz "aha, teraz rozumiem, to jest to , co czyni go dobrym!"

To bardziej tak, że mamy wszystkie te inne języki programowania i wszystkie są mniej więcej podobne, a następnie jest Haskell, który jest zupełnie inny i zwariowany w sposób, który jest całkowicie niesamowity, gdy przyzwyczaisz się do tego zwariowania. Ale problem polega na tym, że przyzwyczajenie się do tego dziwactwa zajmuje trochę czasu. Rzeczy, które odróżniają Haskella od prawie każdego innego, nawet pół-głównego języka:

  • Leniwa ocena
  • Brak efektów ubocznych (wszystko jest czyste, IO / etc dzieje się przez monady)
  • Niesamowicie ekspresyjny system typów statycznych

a także kilka innych aspektów, które różnią się od wielu języków głównego nurtu (ale są wspólne dla niektórych):

  • funkcjonalny
  • znaczące spacje
  • wywnioskowany typ

Jak odpowiedziały niektóre inne plakaty, połączenie wszystkich tych cech oznacza, że ​​myślisz o programowaniu w zupełnie inny sposób. Dlatego trudno jest znaleźć przykład (lub zestaw przykładów), który odpowiednio zakomunikowałby to Joe-mainstreamowi-programista. To rzecz empiryczna. (Aby dokonać analogii, mogę pokazać zdjęcia z mojej podróży do Chin w 1970 roku, ale po obejrzeniu zdjęć nadal nie będziesz wiedział, jak to było mieszkać tam w tym czasie. Podobnie mogę pokazać Ci Haskell „quicksort”, ale nadal nie będziesz wiedział, co to znaczy być Haskellerem).


17
Nie zgadzam się z twoim pierwszym zdaniem. Na początku byłem pod wrażeniem kilku przykładów kodu Haskella i tym, co naprawdę przekonało mnie, że warto się tego nauczyć, był ten artykuł: cs.dartmouth.edu/~doug/powser.html Ale oczywiście jest to interesujące dla matematyka / fizyka. Programista zaglądający w rzeczywisty świat uznałby ten przykład za śmieszny.
Rafael S. Calsaverini

2
@Rafael: To nasuwa pytanie "czym byłby pod wrażeniem programista zaglądający do świata rzeczywistego"?
JD

Dobre pytanie! Nie jestem programistą z „prawdziwego świata”, więc nie wiem, co im się podoba. hahaha ... Wiem, co lubią fizycy i matematycy. : P
Rafael S. Calsaverini

27

To, co naprawdę wyróżnia Haskell, to wysiłek włożony w jego projekt, aby wymusić programowanie funkcjonalne. Możesz programować w funkcjonalnym stylu w prawie każdym języku, ale zbyt łatwo jest porzucić przy pierwszej dogodności. Haskell nie pozwala na porzucenie programowania funkcjonalnego, więc musisz doprowadzić to do logicznego wniosku, który jest ostatecznym programem, o którym łatwiej jest myśleć i omija całą klasę najbardziej drażliwych typów błędów.

Jeśli chodzi o pisanie programu do użytku w świecie rzeczywistym, może się okazać, że Haskellowi brakuje pewnych praktycznych aspektów, ale ostateczne rozwiązanie będzie lepsze, jeśli najpierw poznałeś Haskella. Zdecydowanie jeszcze mnie tam nie ma, ale jak dotąd nauka Haskella była dużo bardziej pouczająca niż powiedzenie, że Lisp był na studiach.


1
Cóż, zawsze istnieje możliwość zawsze i tylko korzystania z monady ST i / lub unsafePerformIOdla ludzi, którzy chcą tylko patrzeć, jak płonie świat;)
sara

22

Część zamieszania polega na tym, że czystość i statyczne typowanie umożliwiają równoległość w połączeniu z agresywnymi optymalizacjami. Języki równoległe są teraz popularne, a wielordzeniowy jest nieco uciążliwy.

Haskell oferuje więcej opcji równoległości niż prawie jakikolwiek inny język ogólnego przeznaczenia, wraz z szybkim, natywnym kompilatorem kodu. Naprawdę nie ma konkurencji z tego rodzaju obsługą stylów równoległych:

Jeśli więc zależy ci na tym, aby praca wielordzeniowa działała, Haskell ma coś do powiedzenia. Świetnym miejscem do rozpoczęcia jest samouczek Simona Peytona Jonesa dotyczący programowania równoległego i współbieżnego w Haskell .


„wraz z szybkim, natywnym kompilatorem kodu”?
JD

Uważam, że dons ma na myśli GHCI.
Gregory Higley

3
@Jon: shootout.alioth.debian.org/u32/… Na przykład Haskell całkiem nieźle radzi sobie w strzelaninie.
Peaker

4
@Jon: Kod strzelanin jest bardzo stary i pochodzi z odległej przeszłości, kiedy GHC był mniej optymalizującym kompilatorem. Mimo to udowadnia, że ​​kod Haskella może w razie potrzeby obniżyć poziom, aby uzyskać wydajność. Nowsze rozwiązania w strzelaninie są bardziej idiomatyczne i wciąż szybkie.
Peaker

1
@GregoryHigley Istnieje różnica między GHCI a GHC.
Jeremy List


18

Spędziłem ostatni rok ucząc się Haskella i pisząc w nim dość duży i złożony projekt. (Projekt jest zautomatyzowanym systemem handlu opcjami, a wszystko, od algorytmów handlowych po analizę i obsługę niskopoziomowych, szybkich danych rynkowych, odbywa się w Haskell.) Jest znacznie bardziej zwięzły i łatwiejszy do zrozumienia (dla osób z odpowiednie tło) niż wersja Java, a także wyjątkowo solidne.

Prawdopodobnie największą wygraną dla mnie była możliwość modularyzacji przepływu sterowania przez takie rzeczy, jak monoidy, monady i tak dalej. Bardzo prostym przykładem może być monoid zamawiania; w wyrażeniu takim jak

c1 `mappend` c2 `mappend` c3

gdzie c1i tak po powrocie LT, EQalbo GT, c1wracając EQpowoduje ekspresję kontynuować, oceny c2; jeśli c2zwraca LTlub GTjest to wartość całości, ac3 nie jest obliczana. Tego rodzaju rzeczy stają się znacznie bardziej wyrafinowane i złożone w rzeczach takich jak monadyczne generatory wiadomości i parsery, w których mogę przenosić różne typy stanów, mieć różne warunki przerwania lub mogę chcieć być w stanie zdecydować dla dowolnego konkretnego połączenia, czy przerwanie naprawdę oznacza „brak dalszego przetwarzania” lub oznacza „zwracanie błędu na końcu, ale kontynuowanie przetwarzania w celu zebrania dalszych komunikatów o błędach”.

To wszystko jest rzeczy, których nauczenie się zajmuje trochę czasu i prawdopodobnie sporo wysiłku, dlatego może być trudno przedstawić przekonujący argument na jego rzecz dla tych, którzy jeszcze nie znają tych technik. Myślę, że All About Monads samouczek daje całkiem imponującą demonstrację jednego aspektu tego, ale nie spodziewałbym się, że ktoś, kto nie jest zaznajomiony z materiałem, już go „zrozumie” przy pierwszym, a nawet trzecim, uważnym czytaniu.

W każdym razie w Haskell jest też wiele innych dobrych rzeczy, ale jest to główny, o którym nie wspominam tak często, prawdopodobnie dlatego, że jest dość złożony.


2
Bardzo interesujące! Ile linijek kodu Haskell trafiło w sumie do twojego automatycznego systemu handlowego? Jak poradziłeś sobie z odpornością na błędy i jakie wyniki osiągnąłeś? Ostatnio myślałem, że Haskell może być dobry w programowaniu z małymi opóźnieniami ...
JD

12

Ciekawy przykład można znaleźć pod adresem : http://en.literateprograms.org/Quicksort_(Haskell)

Co ciekawe, przyjrzyjmy się implementacji w różnych językach.

To, co sprawia, że ​​Haskell jest tak interesujący, podobnie jak inne języki funkcjonalne, to fakt, że musisz inaczej myśleć o programowaniu. Na przykład generalnie nie będziesz używać pętli for ani while, ale użyjesz rekurencji.

Jak wspomniano powyżej, Haskell i inne języki funkcjonalne przodują w przetwarzaniu równoległym i pisaniu aplikacji do pracy na wielu rdzeniach.


2
rekurencja jest bombą. to i dopasowanie wzorców.
Ellery Newcomer

1
Pozbycie się pętli for i while jest dla mnie najtrudniejszą częścią podczas pisania w języku funkcjonalnym. :)
James Black

4
Nauka myślenia w rekurencji zamiast w pętlach była dla mnie najtrudniejsza. Kiedy w końcu dotarło, było to jedno z największych objawień programistycznych, jakie kiedykolwiek miałem.
Chris Connett

8
Tyle tylko, że działający programista Haskell rzadko używa pierwotnej rekurencji; przeważnie używasz funkcji bibliotecznych, takich jak map i foldr.
Paul Johnson

18
Uważam, że bardziej interesujące jest to, że oryginalny algorytm szybkiego sortowania Hoare'a został zdemontowany w tej nie-miejscowej formie opartej na liście, najwyraźniej po to, aby bezużyteczne nieefektywne implementacje można było napisać „elegancko” w języku Haskell. Jeśli spróbujesz napisać prawdziwy (na miejscu) quicksort w Haskell, okaże się, że jest on brzydki jak diabli. Jeśli spróbujesz napisać konkurencyjny ogólny quicksort w Haskell, okaże się, że jest to faktycznie niemożliwe z powodu długotrwałych błędów w module odśmiecania pamięci GHC. Chwalenie quicksorta jako dobry przykład wiary żebraków Haskella, IMHO.
JD

8

Nie mogłem ci podać przykładu, jestem facetem od OCaml, ale kiedy jestem w takiej sytuacji jak ty, ciekawość po prostu się ogarnia i muszę pobrać kompilator / interpreter i spróbować. W ten sposób prawdopodobnie dowiesz się znacznie więcej o mocnych i słabych stronach danego języka funkcjonalnego.


1
Nie zapomnij przeczytać kodu źródłowego kompilatora. To również dostarczy Ci wielu cennych informacji.
JD

7

Jedną z rzeczy, które uważam za bardzo fajne, gdy mam do czynienia z algorytmami lub problemami matematycznymi, jest nieodłączna leniwa ocena obliczeń przez Haskella, która jest możliwa tylko dzięki jej ściśle funkcjonalnej naturze.

Na przykład, jeśli chcesz obliczyć wszystkie liczby pierwsze, możesz użyć

primes = sieve [2..]
    where sieve (p:xs) = p : sieve [x | x<-xs, x `mod` p /= 0]

a rezultatem jest nieskończona lista. Ale Haskell oceni to od lewej do prawej, więc dopóki nie spróbujesz zrobić czegoś, co wymaga całej listy, nadal możesz go używać bez utknięcia programu w nieskończoności, na przykład:

foo = sum $ takeWhile (<100) primes

która sumuje wszystkie liczby pierwsze mniejsze niż 100. Jest to dobre z kilku powodów. Przede wszystkim muszę napisać tylko jedną funkcję pierwszą, która generuje wszystkie liczby pierwsze, a potem jestem prawie gotowy do pracy z liczbami pierwszymi. W języku programowania zorientowanego obiektowo potrzebowałbym jakiegoś sposobu, aby powiedzieć funkcji, ile liczb pierwszych powinna obliczyć przed zwróceniem, lub emulować zachowanie nieskończonej listy z obiektem. Inną rzeczą jest to, że generalnie piszesz kod, który wyraża to, co chcesz obliczyć, a nie w jakiej kolejności oceniać rzeczy - zamiast tego kompilator robi to za Ciebie.

Jest to przydatne nie tylko w przypadku nieskończonych list, w rzeczywistości jest używane bez Twojej wiedzy przez cały czas, gdy nie ma potrzeby oceniania więcej niż to konieczne.


2
To nie jest do końca prawdą; z zachowaniem zwrotu zysku w języku C # (język zorientowany obiektowo) można również deklarować nieskończone listy, które są oceniane na żądanie.
Jeff Yates

2
Słuszna uwaga. Masz rację i powinienem unikać stwierdzenia, co można, a czego nie można zrobić w innych językach tak kategorycznie. Myślę, że mój przykład był wadliwy, ale nadal sądzę, że można coś zyskać dzięki leniwemu ocenianiu Haskella: tak naprawdę jest tam domyślnie i bez żadnego wysiłku ze strony programisty. A to, jak sądzę, wynika z jego funkcjonalnego charakteru i braku skutków ubocznych.
jemiołuszka

8
Możesz być zainteresowany, aby przeczytać, dlaczego „sito” nie jest sitem Eratostenesa: lambda-the-ultimate.org/node/3127
Chris Conway

@Chris: Dzięki, to był właściwie całkiem interesujący artykuł! Powyższa funkcja liczb pierwszych nie jest tą, której używałem do własnych obliczeń, ponieważ jest boleśnie powolna. Niemniej jednak artykuł porusza dobrą sprawę, że sprawdzanie wszystkich liczb dla modów to tak naprawdę inny algorytm.
jemiołuszka

6

Zgadzam się z innymi, że zobaczenie kilku małych przykładów nie jest najlepszym sposobem na pochwalenie się Haskellem. Ale i tak dam trochę. Oto błyskawiczne rozwiązanie problemów 18 i 67 Projektu Euler , które proszą Cię o znalezienie ścieżki o maksymalnej sumie od podstawy do wierzchołka trójkąta:

bottomUp :: (Ord a, Num a) => [[a]] -> a
bottomUp = head . bu
  where bu [bottom]     = bottom
        bu (row : base) = merge row $ bu base
        merge [] [_] = []
        merge (x:xs) (y1:y2:ys) = x + max y1 y2 : merge xs (y2:ys)

Oto pełna implementacja algorytmu BubbleSearch do wielokrotnego użytku autorstwa Lesh i Mitzenmacher. Użyłem go do spakowania dużych plików multimedialnych do archiwizacji na DVD bez odpadów:

data BubbleResult i o = BubbleResult { bestResult :: o
                                     , result :: o
                                     , leftoverRandoms :: [Double]
                                     }
bubbleSearch :: (Ord result) =>
                ([a] -> result) ->       -- greedy search algorithm
                Double ->                -- probability
                [a] ->                   -- list of items to be searched
                [Double] ->              -- list of random numbers
                [BubbleResult a result]  -- monotone list of results
bubbleSearch search p startOrder rs = bubble startOrder rs
    where bubble order rs = BubbleResult answer answer rs : walk tries
            where answer = search order
                  tries  = perturbations p order rs
                  walk ((order, rs) : rest) =
                      if result > answer then bubble order rs
                      else BubbleResult answer result rs : walk rest
                    where result = search order

perturbations :: Double -> [a] -> [Double] -> [([a], [Double])]
perturbations p xs rs = xr' : perturbations p xs (snd xr')
    where xr' = perturb xs rs
          perturb :: [a] -> [Double] -> ([a], [Double])
          perturb xs rs = shift_all p [] xs rs

shift_all p new' [] rs = (reverse new', rs)
shift_all p new' old rs = shift_one new' old rs (shift_all p)
  where shift_one :: [a] -> [a] -> [Double] -> ([a]->[a]->[Double]->b) -> b
        shift_one new' xs rs k = shift new' [] xs rs
          where shift new' prev' [x] rs = k (x:new') (reverse prev') rs
                shift new' prev' (x:xs) (r:rs) 
                    | r <= p    = k (x:new') (prev' `revApp` xs) rs
                    | otherwise = shift new' (x:prev') xs rs
                revApp xs ys = foldl (flip (:)) ys xs

Jestem pewien, że ten kod wygląda jak przypadkowy bełkot. Ale jeśli przeczytasz wpis na blogu Mitzenmachera i zrozumiesz algorytm, , że można spakować algorytm do kodu, nie mówiąc nic o tym, czego szukasz.

Podając kilka przykładów, o które prosiłeś, powiem, że najlepszym sposobem, aby zacząć doceniać Haskella, jest przeczytanie artykułu, w którym dostałem pomysły potrzebne do napisania pakowacza DVD: Why Functional Programming Matters autorstwa Johna Hughesa. Artykuł faktycznie poprzedza Haskell, ale znakomicie wyjaśnia niektóre pomysły, które sprawiają, że ludzie lubią Haskella.


5

Dla mnie atrakcją Haskella jest obietnica gwarantowanej przez kompilator poprawności. Nawet jeśli dotyczy to czystych części kodu.

Napisałem dużo kodu do symulacji naukowych i wiele razy zastanawiałem się , czy w moich poprzednich kodach był błąd, który mógłby unieważnić wiele bieżących prac.


6
W jaki sposób gwarantuje poprawność?
Jonathan Fischoff

Czyste części kodu są znacznie bezpieczniejsze niż nieczyste. Poziom zaufania / włożony wysiłek jest znacznie wyższy.
rpg

1
Co sprawiło ci takie wrażenie?
JD

5

Uważam, że w przypadku niektórych zadań jestem niesamowicie produktywny z Haskellem.

Powodem jest zwięzła składnia i łatwość testowania.

Oto jak wygląda składnia deklaracji funkcji:

foo a = a + 5

To najprostszy sposób, w jaki mogę wymyślić definicję funkcji.

Jeśli napiszę odwrotność

inverseFoo a = a - 5

Pisząc, mogę sprawdzić, czy jest to odwrotność dowolnego losowego wejścia

prop_IsInverse :: Double -> Bool
prop_IsInverse a = a == (inverseFoo $ foo a)

I dzwoniąc z linii poleceń

jonny @ ubuntu: runhaskell quickCheck + nazwy fooFileName.hs

Który sprawdzi, czy wszystkie właściwości w moim pliku są zachowane, poprzez losowe testowanie danych wejściowych sto razy.

Nie sądzę, aby Haskell był idealnym językiem do wszystkiego, ale jeśli chodzi o pisanie małych funkcji i testowanie, nie widziałem nic lepszego. Jeśli twoje programowanie zawiera element matematyczny, jest to bardzo ważne.


Jakie problemy rozwiązujesz i jakich innych języków próbowałeś?
JD

1
Grafika 3D w czasie rzeczywistym na telefon komórkowy i iPada.
Jonathan Fischoff

3

Jeśli potrafisz owinąć głowę wokół systemu czcionek w Haskell, myślę, że sam w sobie jest to spore osiągnięcie.


1
Co tu jest do zdobycia? Jeśli musisz, pomyśl "dane" == "klasa" i "typeclass" = "interfejs" / "rola" / "cecha". To nie może być prostsze. (Nie ma nawet „null”, które mogłoby cię zepsuć. Null to koncepcja, którą możesz samodzielnie wbudować w swój typ).
jrockway

8
Jest dużo do zdobycia, jrockway. Podczas gdy ty i ja uważamy to za stosunkowo proste, wiele osób - nawet wielu programistów - uważa, że ​​pewne rodzaje abstrakcji są bardzo trudne do zrozumienia. Znam wielu programistów, którzy wciąż nie do końca rozumieją ideę wskaźników i referencji w bardziej popularnych językach, mimo że używają ich na co dzień.
Gregory Higley

2

nie ma konstrukcji pętli. niewiele języków ma tę cechę.


17
ghci>: m + Control.Monad ghci> forM_ [1..3] print 1 2 3
sastanin

1

Zgadzam się z tymi, którzy mówili, że programowanie funkcjonalne przekręca twój mózg, aby patrzeć na programowanie z innej perspektywy. Używałem go tylko jako hobbysta, ale myślę, że zasadniczo zmieniło to sposób, w jaki podchodzę do problemu. Nie sądzę, żebym był prawie tak skuteczny z LINQ bez kontaktu z Haskellem (i używania generatorów i list składanych w Pythonie).


-1

Mówiąc inaczej: Steve Yegge pisze, że językom Hindely-Milner brakuje elastyczności wymaganej do pisania dobrych systemów :

HM jest bardzo ładny, w całkowicie bezużytecznym formalnym matematycznym sensie. Bardzo dobrze obsługuje kilka konstrukcji obliczeniowych; funkcja dopasowania do wzorca dostępna w Haskell, SML i OCaml jest szczególnie przydatna. Nic dziwnego, że radzi sobie z niektórymi innymi popularnymi i wysoce pożądanymi konstrukcjami w najlepszym razie niezręcznie, ale wyjaśniają te scenariusze, mówiąc, że się mylisz, że tak naprawdę ich nie chcesz. Wiesz, rzeczy takie jak, och, ustawianie zmiennych.

Haskella warto się nauczyć, ale ma on swoje słabości.


5
Chociaż z pewnością prawdą jest, że systemy typu silnego generalnie wymagają od ciebie przestrzegania ich (to właśnie sprawia, że ​​ich siła jest przydatna), jest również tak, że wiele (większość?) Istniejących systemów opartych na HM ma w rzeczywistości jakiś rodzaj '' właz ratunkowy ”zgodnie z opisem w linku (weź Obj.magic w O'Caml jako przykład, chociaż nigdy nie używałem go, z wyjątkiem włamania); w praktyce jednak do wielu rodzajów programów takie urządzenie nigdy nie jest potrzebne.
Zach Snow

3
Kwestia, czy ustawienie zmiennych jest „pożądane”, zależy od tego, ile bólu powoduje użycie alternatywnych konstruktów w porównaniu z tym, ile bólu powoduje użycie zmiennych. Nie oznacza to odrzucenia całej argumentacji, ale raczej wskazanie, że przyjęcie stwierdzenia „zmienne są wysoce pożądaną konstrukcją” jako aksjomatu nie jest podstawą przekonującego argumentu. Tak się składa, że ​​większość ludzi uczy się programowania.
gtd

5
-1: Oświadczenia Steve'a są częściowo nieaktualne, ale w większości są po prostu całkowicie błędne. Rozluźnione ograniczenie wartości OCamla i system typów .NET to oczywiste przykłady przeciwstawne do jego stwierdzeń.
JD

4
Steve Yegge ma w swojej masce nierozsądną pszczołę na temat statycznego pisania i nie tylko większość tego, co mówi o tym, jest błędna, ale także powtarza to przy każdej nadarzającej się okazji (a nawet przy niektórych niedostępnych). Dobrze by było, gdybyś zaufał tylko własnemu doświadczeniu w tym względzie.
ShreevatsaR

3
Chociaż nie zgadzam się z Yegge na temat pisania statycznego i dynamicznego, Haskell ma typ Data.Dynamic. Jeśli chcesz dynamicznego pisania, możesz to mieć!
jrockway
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.