Dlaczego składnia języka funkcjonalnego nie jest bardziej zbliżona do języka ludzkiego?

14

Interesuję się programowaniem funkcjonalnym i postanowiłem zmierzyć się z Haskellem. Boli mnie głowa ... ale w końcu to dostanę ... Mam jednak jedną ciekawość, dlaczego składnia jest tak tajemnicza (bez innego słowa)?

Czy istnieje powód, dla którego nie jest on bardziej wyrazisty , bliższy ludzkiemu językowi?

Rozumiem, że FP jest dobra w modelowaniu pojęć matematycznych i zapożyczyła niektóre ze swoich zwięzłych środków wyrazu, ale wciąż nie jest to matematyka ... to język.

JohnDoDo
źródło
4
@ratchet freak: :) czy jest taki zaprojektowany przez faceta, który nienawidzi polskiej notacji?
JohnDoDo
10
@ratchetfreak W jaki sposób składnia Haskell przypomina polską notację? Operatory Haskell są niepoprawne, podobnie jak większość operatorów innych języków (a wywołania funkcji są prefiksami - podobnie jak wywołania funkcji większości innych języków).
sepp2k,
9
To dość duże założenie / skok, że „bliższy ludzkiemu językowi” oznacza „bardziej ekspresyjny”.
Matt Ball
7
Słyszałeś kiedyś o Cobolu i Fortranie?
ott--
7
Z tego samego powodu, dla którego matematyki zastąpili długie wyrażenia łacińskie wyrażeniami algebraicznymi. Przy odrobinie praktyki bardziej zwarta notacja jest o wiele bardziej produktywna.
kevin cline

Odpowiedzi:

14

Języki funkcjonalne, takie jak Haskell i jego poprzedniczka Miranda, wyrosły z matematycznej koncepcji rachunku lambda . Ze strony Wikipedii:

Rachunek lambda (zapisywany również jako rachunek λ lub zwany „rachunkiem lambda”) jest formalnym systemem logiki matematycznej do wyrażania obliczeń poprzez zmienne wiązanie i podstawienie.

...

Rachunek Lambda odegrał ważną rolę w rozwoju teorii języków programowania. Najważniejszymi odpowiednikami rachunku lambda w informatyce są funkcjonalne języki programowania, które zasadniczo implementują rachunek (uzupełniony o niektóre stałe i typy danych). Oprócz języków programowania rachunek lambda ma również wiele zastosowań w teorii dowodów. Głównym tego przykładem jest korespondencja Curry'ego-Howarda, która daje zgodność między różnymi systemami typowanego rachunku lambda i systemami logiki formalnej.

Z powodu tej historii na składnie tych języków funkcjonalnych (i elementów funkcjonalnych bardziej imperatywnych języków) duży wpływ ma notacja matematyczna stosowana w rachunku lambda.

Dokładność, z jaką zarówno zapisy matematyczne, jak i języki komputerowe mogą opisywać wymagania, oraz precyzja, z jaką komputery wymagają, aby ich instrukcje były pisane, dobrze ze sobą korelują. Niedokładność języka naturalnego stanowi jednak ogromną barierę w jego używaniu do programowania komputerów. Nawet (prawdopodobnie) najbardziej udane środowiska programowania w języku naturalnym, takie jak Wolfram Alpha, wymagają znacznego doświadczenia w dziedzinie, aby mogły być skutecznie wykorzystywane.

Mark Booth
źródło
29

Składnia Haskella jest zbliżona do ludzkiego języka. Jest specjalnie zaprojektowany, aby przypominać notację matematyczną, która jest językiem zaprojektowanym przez ludzi (a więc ludzkim językiem ), aby dokładnie wyrażać te pojęcia, na których zbudowany jest Haskell.

Możesz pokazać kawałek kodu Haskell każdemu matematykowi lub logikowi, a on będzie w stanie go zrozumieć, nawet jeśli nigdy nie słyszał o Haskell i nie wie absolutnie nic o programowaniu, informatyce, a nawet komputerach.

Z drugiej strony, możesz pokazać każdemu matematykowi lub logikowi taki kod:

x = x + 1

i będzie całkowicie zdezorientowany, mówiąc: „To nie ma sensu, nie ma xtakiego, który xrównałby się xplus 1”.

To, czy jakiś konkretny zapis jest „tajemniczy”, czy nie, jest kwestią opinii i znajomości.

Jörg W Mittag
źródło
8
Jest tylko jeden problem z tą odpowiedzią: nie jestem matematykiem.
JohnDoDo
6
@ sepp2k: Nie zgadzam się. Dla większości osób bez wcześniejszego kontaktu z językami programowania i podstawowymi podstawami matematycznymi składnia Haskella będzie znacznie łatwiejsza do zrozumienia niż typowy kod proceduralny ze wszystkimi jego skutkami ubocznymi i wykonywaniem skomplikowanych instrukcji środowiska. Składnia taka jak wherepozwala na zagęszczenie treści funkcja w jednym wierszu.
Benjamin Bannier
17
-1: ta odpowiedź wydaje się w dużej mierze pedantyczna. Jestem pewien, że OP oznaczało język mówiony.
Steven Evers,
6
Właściwie jestem całkiem pewien, że x = infinityto rzeczywiście rozwiązuje równanie.
DeadMG,
6
Cóż, języki programowania są również zaprojektowane przez ludzi ... Tak więc z twojej definicji są to języki ludzkie.
marco-fiset
13

Myślę, że jedną rzeczą, na którą prawdopodobnie natrafisz, jest coś, na co wpadłem również podczas nauki programowania funkcjonalnego, to znaczy, że dzięki programowaniu funkcjonalnemu możesz (i prawie musisz) myśleć / pracować na wyższym poziomie niż w przypadku programowania imperatywnego.

Wydaje mi się, że to, co wydaje się mniej wyraziste, jest w rzeczywistości bardziej wyraziste: nie musisz opisywać każdego najmniejszego szczegółu i możesz zrobić więcej przy mniejszym kodowaniu w programowaniu funkcjonalnym - masz więcej mocy do pisania.

Na przykład, mógłbym napisać koniecznie:

for each (Person person in people)
    print(person.name)

który jest całkowicie czytelny jako angielski.

Wersja Haskell może być (i to nie jest prawidłowy Haskell, ale służy jedynie do porównania składniowego):

map (print . name) people

co wymaga mniej kodu i mniej szczegółowego przekręcania - nie muszę rozkładać rzeczy na pętlę i jej zmienne () zmienne for each (...), mapfunkcja zajmuje się tym za mnie.

Praca na tym poziomie może zająć trochę czasu. Jeśli to pomoże, Haskell był prawdopodobnie najtrudniejszym momentem w nauce nowego języka, odkąd zacząłem programować, i znam> 10 języków (w tym Lisp). Warto było się jednak nauczyć.

Paweł
źródło
8

Obecnie zajmuję się Scalą, więc mogę współczuć. Przynajmniej ze Scalą mogę wrócić do imperatywnego stylu, gdy sytuacja staje się zbyt trudna.

Myślę, że gra tu kilka sił:

  • Projektanci języków funkcjonalnych z konieczności mają silne zaplecze matematyczne, więc zapożyczają zapis matematyczny, który jest dla nich naturalny.

  • Większa ekspresja (tj. Siła wypowiedzi, a nie bliskość języka angielskiego) dowolnego języka ma (IMO) odpowiednią cenę pod względem nachylenia krzywej uczenia się. Terseness to bardzo ceniona jakość w Scali, a wiele rzeczy jest opcjonalnych.

  • Języki funkcjonalne po prostu robią wszystko inaczej, więc podstawowe operacje nie są odwzorowywane na konstrukcje imperatywne.

Richard Close
źródło
3
Dodałbym również, że często funkcjonalne języki mogą wyrażać pojęcia wyższego rzędu, które są bardziej abstrakcyjne, trudniej jest je nazwać sensownie
jk.
1
@jk, tak, zgodził się! Jednocześnie dlatego mają stromą krzywą uczenia się dla programistów z niezbędnym doświadczeniem i małą matematyką (jak ja) i dlaczego są tego warte.
Richard Close
3

Jeśli dobrze zrozumiałem twój ostatni komentarz, twoje pytanie brzmi: dlaczego Haskell często używa operatorów symbolicznych w miejscach, w których równie dobrze mógłby używać alfanumerycznych słów kluczowych (lub nazw funkcji)?

Jeśli chodzi o słowa kluczowe, jedną z odpowiedzi byłoby to, że słowa kluczowe uniemożliwiają używanie niektórych słów jako nazw zmiennych. Na przykład ja zawsze irytowało, gdy chcę zadzwonić moje zmienne fromi tow ciągu językach, gdzie jeden z nich jest to słowa kluczowe - jak w Pythonie.

Innym powodem dla operatorów symbolicznych w ogólności jest zwięzłość. To tak naprawdę nie dotyczy twoich konkretnych przykładów list ( <-nie jest krótszy niż in), ale w wielu przypadkach tak jest.

Jeszcze innym powodem jest czytelność. Może się to wydawać sprzeczne z intuicją, ponieważ operatory symboliczne są często trudniejsze do nauczenia się, ponieważ ich „nazwy” tak naprawdę nie mówią nic o tym, co robią (podczas gdy nazwa funkcji zwykle tak robi), ale gdy już wiesz, co robi dany operator, wyrażenia z operatorami symbolicznymi w postaci infixów są często łatwiejsze do analizy dla człowieka niż w przypadku wielu wywołań funkcji prefiksu alfanumerycznego, ponieważ operatory łatwiej wizualnie odróżniają się od operandów w ten sposób. Na przykład większość ludzi zgodzi się, że 2 * 3 + 4jest łatwiejsza do odczytania niż add (mult 2 3) 4lub add(mult(2, 3), 4).

sepp2k
źródło
@PeterTaylor Nie, tylko ja jestem głupia ;-) Naprawiono teraz.
sepp2k,
3

Imperatywne języki komputerowe w większości nie są bliżej języków naturalnych niż, powiedzmy, Haskell.

Jednak niektóre są zaprojektowane tak, aby bardziej przypominały angielski: na przykład Cobol i Hypercard. Lekcje, które wyciągnę z tych przykładów, to:

  • podobieństwo do języków naturalnych nie wydaje się przynosić bezpośredniej poprawy użyteczności
  • bezpośrednia emulacja języków naturalnych może przynieść języki komputerowe, które są pełne i niejasne

Podobno język komputerowy Perla został zaprojektowany z myślą o bardziej subtelnych aspektach języka naturalnego . Oceniłbym, że Perl radzi sobie lepiej niż Cobol czy Hypercard pod względem ogólnej użyteczności, ale ludzie mają różne opinie na temat Perla.

nadchodząca burza
źródło
3

Myślę, że Haskell staje się tak podobny do ludzkiego języka, jak to tylko możliwe, bez wprowadzania szalonej składni. Naprawdę robi to bardzo długo, na przykład z whereklauzulą ​​odkładania definicji i ze składnią rozumienia listy, która jest dokładnie tym, co napisałbym na tablicy. Unika także obcych postaci, takich jak aparat ortodontyczny, i ma swobodne stosowanie wcięć. Typowym przykładem jest quicksort:

qsort [] = []
qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater
               where lesser = [x | x <- xs, x <= p] 
                     greater = [x | x <- xs, x > p]

Wprawdzie jest to prosty przykład, ale nie znam żadnego innego języka, który uczyniłby go tak czytelnym.

Robienie bardziej skomplikowanych rzeczy może być trudne do odczytania w Haskell, ale ma to związek z systemem typów, ograniczonym IO, a nie ze składnią w ogóle.

W ogóle powiedziałbym, że Haskell poświęcił prostotę składniową, aby dostosować się do składni bardziej zbliżonej do języka ludzkiego. Schemat podobny do języka ma składnię, która jest bardzo daleka od tego, co napisałby człowiek, ale naprawdę łatwo jest wyjaśnić jego zasady.

Andrea
źródło
3
Trudno nazwać to czytelnym dla człowieka. Ja wiem quicksort. Znam sposób konfigurowania go w wielu różnych językach, wiem, co powinien zrobić, i nadal nie mogę tworzyć głów ani ogonów z dolnej części tego fragmentu kodu.
Mason Wheeler,
Myślę, że to naprawdę zależy od twojego pochodzenia. To jest dla mnie tak proste, jak się wydaje ...
Andrea
2
W każdym razie ma to przypominać ten zapis: purplemath.com/modules/setnotn.htm
Andrea
4
Jesteś pewien, że nie powinno tak być ++ [p] ++?
Peter Taylor
1
@Mason: Wydaje mi się to dość jasne i nigdy nie napisałem linii Haskell
kevin cline,
2

Myślę, że różnica ma związek ze sposobem, w jaki programowanie funkcjonalne / deklaratywne wypowiada się na temat problemu, tak jakby to był problem matematyczny, podczas gdy programowanie imperatywne opisuje proces . W świecie biznesu programy komputerowe owijają lub zastępują procesy fizyczne, więc możesz znaleźć język, który odzwierciedla to, jako bardziej intuicyjny.

Okazuje się, że styl funkcjonalny nadaje się do bezpiecznego korzystania z wielu procesorów (ponieważ minimalizuje zmienność i skutki uboczne) - coś, czego zobaczymy więcej w przyszłości. Ponadto większość współczesnych języków funkcjonalnych ma kolekcje z iteratorami, do których można przekazywać funkcje. Te metody mogą bardzo skutecznie podzielić obciążenie pracą na wiele procesorów, nawet o tym nie wiedząc.

GlenPeterson
źródło
Myślę, że to najlepsza odpowiedź - jedyna, która wyraźnie rozróżnia proces od stwierdzeń.
Milind R