C # Dev - próbowałem Lisps, ale nie rozumiem [zamknięty]

37

Po kilku miesiącach nauki i gry z Lispem, zarówno CL, jak i odrobiną Clojure, nadal nie widzę przekonującego powodu, aby napisać coś w nim zamiast C #.

Naprawdę chciałbym mieć jakieś ważne powody lub ktoś, kto zauważyłby, że brakuje mi czegoś naprawdę dużego .

Mocne strony Lisp (według moich badań):

  • Zwarta, ekspresyjna notacja - Bardziej niż C #, tak ... ale wydaje mi się, że potrafię wyrazić te pomysły również w C #.
  • Domniemana obsługa programowania funkcjonalnego - C # z metodami rozszerzenia LINQ:
    • mapcar = .Wybierz (lambda)
    • mapcan = .Wybierz (lambda) .Aggregate ((a, b) => a.Union (b))
    • samochód / pierwszy = .pierwszy ()
    • cdr / rest = .Skip (1) .... itd.
  • Obsługa funkcji lambda i wyższego rzędu - C # ma to, a składnia jest prawdopodobnie prostsza:
    • „(lambda (x) (body))” versus „x => (body)”
    • „# (” z „%”, „% 1”, „% 2” jest miłe w Clojure
  • Wysłanie metody oddzielone od obiektów - C # ma to poprzez metody rozszerzenia
  • Wysyłanie multimetodowe - C # nie ma tego natywnie, ale mógłbym zaimplementować go jako wywołanie funkcji w ciągu kilku godzin
  • Kod to dane (i makra) - być może nie „dostałem” makra, ale nie widziałem żadnego przykładu, w którym pomysł makra nie mógłby zostać zaimplementowany jako funkcja; nie zmienia „języka”, ale nie jestem pewien, czy to jest siła
  • DSL - Można to zrobić tylko poprzez kompozycję funkcji ... ale działa
  • Nietypowe programowanie „eksploracyjne” - w przypadku struktur / klas, autoproperties i „obiekt” C # działają całkiem dobrze i możesz łatwo przerodzić się w mocniejsze pisanie w miarę postępów
  • Działa na sprzęcie innym niż Windows - Tak, więc? Poza uczelnią znam tylko jedną osobę, która nie uruchamia systemu Windows w domu, lub przynajmniej maszynę wirtualną systemu Windows na * nix / Mac. (Z drugiej strony, może to jest ważniejsze niż myślałem i właśnie zostałem poddany praniu mózgu ...)
  • REPL za projektowanie od dołu do góry - Ok, przyznaję, że to naprawdę bardzo miłe i tęsknię za tym w C #.

Czego brakuje mi w Lisp (ze względu na połączenie C #, .NET, Visual Studio, Resharper):

  • Przestrzenie nazw Nawet przy użyciu metod statycznych lubię wiązać je z „klasą”, aby kategoryzować ich kontekst (wydaje się, że Clojure to ma, a CL nie.)
  • Świetna obsługa kompilacji i czasu projektowania
    • system typów pozwala mi określić „poprawność” struktur danych, które przekazuję
    • wszystko błędnie napisane jest podkreślane w czasie rzeczywistym; Nie muszę czekać, aż środowisko uruchomieniowe się dowie
    • ulepszenia kodu (takie jak użycie podejścia FP zamiast imperatywnego) są automatycznie sugerowane
  • Narzędzia programistyczne GUI: WinForms i WPF (wiem, że Clojure ma dostęp do bibliotek GUI Java, ale są one dla mnie całkowicie obce).
  • Narzędzia do debugowania GUI: punkty przerwania, wejście, przejście, inspektory wartości (tekst, xml, niestandardowe), zegarki, debugowanie według wątku, warunkowe punkty przerwania, okno stosu wywołań z możliwością przeskakiwania do kodu na dowolnym poziomie na stosie
    • (Szczerze mówiąc, moje doświadczenie z Emacsem + Szlamem zdawało się to zapewniać, ale jestem częściowo zwolennikiem podejścia opartego na VS GUI)

Bardzo podoba mi się szum wokół Lisp i dałem mu szansę.

Ale czy jest coś, co mogę zrobić w Lisp, czego nie mogę zrobić równie dobrze w C #? Może to być nieco bardziej szczegółowe w C #, ale mam również autouzupełnianie.

czego mi brakuje? Dlaczego powinienem używać Clojure / CL?

talonx
źródło
4
Ponieważ programujesz już w funkcjonalnym stylu i brzmi to tak, jakbyś polegał dość mocno na infrastrukturze Windows, nie widzę żadnego istotnego powodu, abyś przestawił się z C #. Większość osób, które znam, używa Maca lub Linuksa, więc wiele zależy od tłumu, z którym biegniesz (korzystam z każdej wersji systemu Windows od wersji 3.1, ale nadal wolę pracować z oprogramowaniem międzyplatformowym i ogólnie systemami * nix ... ale potem nie wykonuję żadnej natywnej pracy GUI, wszystkie serwery i rzeczy związane z interfejsem sieciowym)
Sean Corfield
2
Możesz rzucić okiem na The Swine Before Perl . Słuchanie przyjemnej rozmowy to prosty przykład tego, do czego służą makra.
Matthias Benkard,
4
„Nie widziałem żadnego przykładu, w którym pomysł makra nie mógłby zostać zaimplementowany jako funkcja” - chciałbym zobaczyć, jak napiszesz ANDlub ORjako funkcję. Można to zrobić (biorąc pod uwagę LAMBDA, że jest to również makro), ale nie widzę oczywistego sposobu na zrobienie tego, co nie byłoby do niczego.
1
„Przestrzenie nazw. ... Wygląda na to, że Clojure ma, CL nie” - czy możesz bardziej szczegółowo określić, czego szukasz? Tak naprawdę nie korzystałem z Clojure, ale jego przestrzenie nazw przypominają pakiety CL.
4
Jonathan: CL używa :(lub ::nieeksportowanych symboli) do nazywania symboli w pakietach, np. Twój przykład użyłby http:sessioni chat:session.

Odpowiedzi:

23

Makra pozwalają pisać kompilatory bez opuszczania wygody twojego języka. DSL w językach takich jak C # zazwyczaj stanowią tłumacza środowiska wykonawczego. W zależności od domeny może to być problematyczne ze względu na wydajność. Szybkość ma znaczenie , w przeciwnym razie kodowałbyś w interpretowanym języku, a nie w języku C #.

Makra pozwalają również uwzględniać czynniki ludzkie - jaka jest najbardziej przyjazna dla użytkownika składnia dla określonej DSL? Dzięki C # niewiele można zrobić ze składnią.

Więc Lisp pozwala ci uczestniczyć w projektowaniu języka bez poświęcania drogi do wydajności . Chociaż te kwestie mogą nie mieć znaczenia dla Ciebie , są one niezwykle ważne, ponieważ leżą u podstaw wszystkich użytecznych języków programowania zorientowanych na produkcję. W języku C # ten podstawowy element jest w najlepszym wypadku bardzo ograniczony.

Znaczenie makr nie jest tracone w innych językach - przychodzą na myśl szablony Haskell, camlp4. Ale znowu jest to kwestia użyteczności - makra Lisp do tej pory są prawdopodobnie najbardziej przyjazną dla użytkownika, ale potężną implementacją transformacji w czasie kompilacji.

Podsumowując, to, co ludzie zazwyczaj robią w językach takich jak C #, to budowanie DSIL (języków specyficznych dla domeny). Lisp daje ci możliwość zbudowania czegoś znacznie bardziej radykalnego - DSP (Domain Specific Paradigms). Rakieta (wcześniej PLT-Scheme) jest szczególnie inspirująca pod tym względem - mają leniwy język (Lazy Racket), maszynowy (Typed Racket), Prolog i Datalog, wszystkie idiomatycznie osadzone za pośrednictwem makr. Wszystkie te paradygmaty zapewniają potężne rozwiązania dla dużych klas problemów - problemów, których nie najlepiej rozwiązać za pomocą programowania imperatywnego, a nawet paradygmatów FP.

Robert Harvey
źródło
3
Interesujące w tym argumencie jest to, że nigdy nie spotkałem się z szansą (lub po prostu całkowicie przeoczyłem ją w swojej ignorancji) na wdrożenie DSL / DSIL. Czy mógłbyś rozwinąć moc DSL? (Zarówno dla programisty, jak i użytkownika.) To brzmi, jakby to była najważniejsza rzecz, której tak naprawdę brakuje.
15
@Jonathan Mitchem, używasz już wielu zewnętrznych DSL - plików konfiguracyjnych, skryptów kompilacji, konfiguracji ORM, generatorów kodu wizualnego (np. Edytora winforms), XAML, generatorów parserów itp. Osadzone DSL są jeszcze lepsze, ponieważ nie potrzebują dla nich osobnego narzędzia do budowania i są obsługiwane w jednym języku. Musisz także znać przynajmniej dwa wbudowane DSL - wyrażenia regularne i SQL.
SK-logic
Wow ok. Po prostu nigdy nie myślałem o nich jako o DSL. Teraz celowo odstąpiłem od wielu z tego, na podstawie „wszystkiego, co mogę zrobić z c #, zamierzam trzymać się c # for”. Osobiście lubię trzymać się jednego narzędzia o spójnym zachowaniu. Ale rozumiem wartość DSL w tych przykładach.
2
@Jonathan Mitchem, problem z danym narzędziem polega na tym, że niekoniecznie musi on być odpowiedni do określonego celu. Musisz wybrać różne narzędzia do różnych zadań. A mocą każdego meta-języka, w tym Lisp, jest to, że nie musisz w pełni przełączać się na inne narzędzie, ponieważ możesz rozwijać własne narzędzia specyficzne dla domeny oprócz głównego języka. Sugeruję, aby spojrzeć na Nemerle, łatwiej byłoby go zrozumieć programistom C #, a jego makra są prawie tak potężne jak Lisp.
SK-logic
3
„Lubię trzymać się jednego narzędzia ...” Właściwie wykonane DSL w Lisp nigdy nie kryją pełnej mocy Lisp, więc wciąż masz jedno narzędzie. Po prostu dodają do słownictwa w sposób, który sprawia, że ​​kodowanie dla konkretnej dziedziny jest bardziej „naturalne”, tj. Z lepszymi abstrakcjami i ogólną organizacją.
15

Prawie wszystko, co pierwotnie było dostępne tylko w Lisps, na szczęście zostało przeniesione na wiele innych współczesnych języków. Największym wyjątkiem jest filozofia „kod to dane” i makra.

Kod to dane (i makra) - być może nie „dostałem” makra, ale nie widziałem żadnego przykładu, w którym pomysł makra nie mógłby zostać zaimplementowany jako funkcja

Tak, makra są trudne do odczytania. Ogólnie trudno jest mówić o metaprogramowaniu. Jeśli widziałeś jakieś makro, które można zaimplementować jako funkcję, programista robił to źle. Makra należy używać tylko wtedy, gdy funkcja nie działa.

Przykład makra „witaj świecie” polega na napisaniu „jeśli” pod względem warunków. Jeśli naprawdę chcesz poznać moc makr, spróbuj zdefiniować „my-if” w clojure, używając funkcji i obserwuj, jak się psuje. Nie chcę być tajemniczy, po prostu nigdy nie widziałem, żeby ktokolwiek zaczął rozumieć makra na podstawie samego wyjaśnienia, ale ten pokaz slajdów jest całkiem dobrym intrem: http://www.slideshare.net/pcalcado/lisp-macros-in -20 minut z prezentacją clojure

Miałem małe projekty, w których zapisałem dosłownie setki / tysiące linii kodu za pomocą makr (w porównaniu do tego, co zrobiłby w Javie). Znaczna część Clojure jest zaimplementowana w Clojure, co jest możliwe tylko dzięki systemowi makr.

Mblinn
źródło
3
Nie dotykając żadnego kodu, przebiegłem scenariusz implementacji „if” bez makr w mojej głowie, zarówno w Clojure, jak i C #. Nie można tego skutecznie (lub dosłownie) zrobić. Przyznaję, że to miłe. Ale brakuje mi związku, w jaki sposób jest to dla mnie przydatne. Co mogę zrobić z makrami (poza napisem „if”), których nie mogę zrobić ze sprytnym projektem funkcjonalnym lub OO? „Jak to może mi pomóc” nie jest prawidłowym kryterium oceny języka, ale służy ocenie, czy go użyję. (Naprawdę chcę, aby warto było zmienić Lisp, ale nie osiągnęło to wartości realnej alternatywy.)
1
Zajmie mi to trochę czasu, aby przejrzeć ten przykład. Dla mnie „zapisywanie kodu płyty bazowej” zwykle wywołuje termin „refaktoryzacja”, co jest częścią tego, dlaczego wyjaśnienia mnie nie przekonały. Mam rozwiązanie, które działa w każdym przypadku, w którym je zastosowałem. Myślę, że wtedy pytanie brzmi: jak rozpoznać przypadki, w których można użyć makra?
1
Zgadzam się, że makra są warte zachodu, ale nie jest prawdą, że moje-jeśli tego wymaga, może być zapisane jako funkcja wyższego rzędu (patrz przykład CL poniżej). Naprawdę potrzebujesz makr tylko wtedy, gdy chcesz przejść kod lub umieścić go w nowym kontekście leksykalnym. (defun my-if (test then &optional else) (cond (test (funcall then)) ('otherwise (funcall else))))
1
Zasadniczo za pomocą funkcji możesz zaakceptować dowolny cytowany kod, którym możesz chodzić, ale brakuje mu kontekstu leksykalnego, ponieważ idziesz i wykonujesz go w kontekście leksykalnym swojej funkcji. Lub możesz zaakceptować zamknięcia, które utrzymują kontekst leksykalny, ale możesz tylko dzwonić, nie chodzić po nich lub dodawać coś do ich kontekstu leksykalnego. Aby to zrobić, potrzebujesz makra, które może przejść i zawinąć kod, zanim jego rozwinięcie zostanie umieszczone w kontekście leksykalnym wywołania makra.
7
@Jonathan Mitchem, składnia LINQ jest dobrym przykładem tego, co można zrobić z makrami, ale zamiast tego musiała zostać zaimplementowana w rdzeniu językowym, ponieważ język nie obsługuje żadnego przyzwoitego metaprogramowania.
SK-logic
14

Nie jestem ekspertem od Common Lisp, ale znam dość dobrze zarówno C #, jak i Clojure, więc mam nadzieję, że jest to przydatna perspektywa.

  • Prosta składnia => moc - Lisps dotyczy najprostszej składni, która mogłaby działać. Wyrażenia S są wszystkim, czego potrzebujesz. Po przeczytaniu „(wywołanie funkcji arg1 arg2 arg3)”, to w zasadzie go masz. Reszta to po prostu wybieranie odpowiednich wywołań funkcji i argumentów. Wydaje się to prawie banalnie proste, ale faktem jest, że ta prostota daje Lispsowi tyle siły i elastyczności, a zwłaszcza umożliwia metaprogramowanie, z którego słynie Lisp. na przykład, jeśli chcę makro zadzwonić kilka różnych funkcji, na tych samych argumentów, po prostu zrobić coś jak poniżej (nie jest to nie tylko środowisko wykonawcze aplikacji funkcja - to makro, które generuje skompilowany kod jakbyś rozpisane wszystkie indywidualne wywołania funkcji ręcznie):
(defmacro print-many [functions args]  
  `(do   
     ~@(map   
        (fn [function] `(println (~function ~@args)))   
        functions)))

(print-many [+ - * /] [10 7 2])  
19  
1  
140  
5/7
  • W wyniku niewiarygodnie prostej składni filozofia Lisp „code is data” jest znacznie potężniejsza niż cokolwiek, co można zrobić z kompozycją funkcji / szablonami / generycznymi itp. To więcej niż tylko DSL, to generowanie kodu i manipulowanie w czasie kompilacji , jako podstawowa cecha języka. Jeśli spróbujesz uzyskać tego rodzaju funkcje w niehomonicznym języku, takim jak C # lub Java, ostatecznie skończysz na nowo wymyślając Lisp. Stąd słynna dziesiąta reguła Greenspuns: „Każdy wystarczająco skomplikowany program C lub Fortran zawiera ad hoc, nieformalnie określone, wolne od błędów, powolne wdrażanie połowy Common Lisp”. Jeśli kiedykolwiek odkryłeś, że wymyślasz w swojej aplikacji kompletny język szablonów / kontroli przepływu, prawdopodobnie wiesz, co mam na myśli .....

  • Współbieżność jest unikalną siłą Clojure (nawet w stosunku do innych Lisps), ale jeśli uważasz, że przyszłość programowania będzie oznaczać konieczność pisania aplikacji skalowalnych do wysoce wielordzeniowych maszyn, to naprawdę powinieneś się tym przyjrzeć - jest ładny przełomowy. Clojure STM (Software Transactional Memory) działa, ponieważ Clojure obejmuje niezmienne i bezblokowe konstrukcje współbieżności oparte na nowatorskim oddzieleniu tożsamości i stanu (zobacz doskonałe wideo Richa Hickeya na temat tożsamości i stanu ). Przeszczepienie tego rodzaju możliwości współbieżności na środowisko języka / środowiska wykonawczego byłoby bardzo bolesne, w którym znaczna część interfejsów API działa na obiektach zmiennych.

  • Programowanie funkcjonalne - Lisps kładzie nacisk na programowanie z funkcjami wyższego rzędu. Masz rację, że można go emulować w obiektach OO z obiektami zamkniętymi / funkcyjnymi itp., Ale różnica polega na tym, że cały język i biblioteka standardowa zostały zaprojektowane tak, aby pomóc Ci pisać kod w ten sposób. Na przykład sekwencje Clojure są leniwe , więc mogą być nieskończenie długie, w przeciwieństwie do ArrayLists lub HashMaps w C # / Java. To sprawia, że ​​niektóre algorytmy są znacznie łatwiejsze do wyrażenia, np. Poniższe implementuje nieskończoną listę liczb fibonacci:

    (def fibs (lazy-cat '(0 1) (map + fibs (drop 1 fibs))))

  • Pisanie dynamiczne - Lisps zwykle znajduje się na „dynamicznym” końcu spektrum funkcjonalnego języka programowania (w przeciwieństwie do języków takich jak Haskell z bardzo mocną i piękną koncepcją typów statycznych). Ma to zarówno zalety, jak i wady, ale twierdzę, że języki dynamiczne sprzyjają wydajności programowania ze względu na większą elastyczność. To dynamiczne pisanie wiąże się z niewielkim kosztem wydajności, ale jeśli Ci to przeszkadza, zawsze możesz dodać wskazówki do kodu, aby uzyskać pisanie statyczne. Zasadniczo - domyślnie zyskujesz wygodę i wydajność, jeśli chcesz wykonać dodatkową pracę, aby ją uzyskać.

  • Interaktywny rozwój - tak naprawdę myślę, że można uzyskać REPL dla C # 4.0, ale w Lisp jest to standardowa praktyka, a nie nowość. W ogóle nie musisz wykonywać cyklu kompilacji / kompilacji / testowania - piszesz kod bezpośrednio w działającym środowisku, a dopiero później kopiujesz go do plików źródłowych. Uczy cię zupełnie innego sposobu kodowania, w którym natychmiast widzisz wyniki i iteracyjnie udoskonalasz swoje rozwiązanie.

Kilka krótkich komentarzy na temat rzeczy, które widzisz jako „brakujące”:

  • Jak mówisz, Clojure ma przestrzenie nazw. W rzeczywistości są to dynamiczne przestrzenie nazw: możesz je aktualizować w czasie wykonywania - zapewnia to zaskakujące korzyści dla interaktywnego programowania. Miałem dużo zabawy przedefiniowaniem funkcji pod nosem działającego wątku lub włamaniem się do struktury danych na żywo ....
  • Obsługa czasu kompilacji / projektowania - niezbyt istotne, jeśli kodujesz na REPL - po prostu zrób to i zobacz wynik bezpośrednio. Powiedziawszy to, Clojure zaczyna otrzymywać ulepszoną obsługę IDE, np. Wtyczkę CounterClockwise do Eclipse .
  • Opracowywanie GUI - Tak, musisz nauczyć się nowego interfejsu API, ale zawsze tak będzie przy zmianie języków ... dobra wiadomość jest taka, że ​​interfejsy API Java nie są tak trudne, a teraz jest kilka ciekawych Clojure- konkretne opakowania / przykłady, które wyglądają na dość łatwe w użyciu. Zobacz to pytanie na temat rozwoju GUI Clojure, aby znaleźć kilka dobrych przykładów
  • Debugowanie GUI: działa z debuggerem GUI w Eclipse przy założeniu, że używasz CounterClockwise lub Enclojure, jeśli używasz Netbeans. Powiedziawszy to, nie używam tej możliwości - interaktywne debugowanie na REPL wydaje mi się bardziej wydajne.

Osobiście uważam zalety Clojure / Lisp za dość przekonujące i nie planuję powrotu do C # / Java (poza tym, co muszę pożyczyć wszystkie przydatne biblioteki!).

Jednak zajęło mi to kilka miesięcy, aby naprawdę się tym zająć. Jeśli naprawdę interesuje Cię nauka Lisp / Clojure jako pouczającej nauki, nic nie zastąpi nurkowania i zmuszania się do wdrożenia kilku rzeczy .....

mikera
źródło
1
Dzięki za świetne wyliczenie pomysłów. Właściwie często używam leniwych sekwencji za pomocą konstruktora IEnumerable <> w C # (i nie dotknąłem ArrayList od około 2005 roku), ale rozumiem ten pomysł. Prymitywy współbieżności Clojure są niezwykle imponujące. Robiłem obliczenia sieciowe w C # kilka lat temu - współbieżność na wielu maszynach wielordzeniowych - i nadal uważam, że to przyszłość. Prawdopodobnie największym pomysłem, jaki dostałem od wszystkich, jest to, że „naprawdę musisz się w to zagłębić i doświadczyć, naprawdę nie da się tego wytłumaczyć”. Więc będę dalej kopał i doświadczał.
1
Fajnie, cieszę się, że pomogłem - i to jest absolutnie właściwy duch !!
mikera
10

C # ma wiele funkcji, ale miks wydaje się inny. To, że jakiś język ma jakąś funkcję, nie oznacza, że ​​jest łatwy w użyciu, paradygmatyczny i dobrze zintegrowany z resztą języka.

Common Lisp ma tę mieszankę:

  • Wyrażenia s jako składnia danych
  • kod, ponieważ dane prowadzą do makr i innych technik obliczania symbolicznego
  • język dynamiczny umożliwia modyfikowanie środowiska wykonawczego (późne wiązanie, MOP, ...)
  • mocno napisane, dynamicznie napisane
  • interaktywne korzystanie z kompilatora przyrostowego lub interpretera
  • Ewaluator jako podstawowy silnik wykonawczy
  • zarządzana pamięć za pomocą Garbage Collection
  • Język hybrydowy z dużym wykorzystaniem imperatywnego, funkcjonalnego i obiektowego programowania. Można dodać inne paradygmaty (reguły, logika, ograniczenia, ...).

Teraz inne języki, takie jak C #, również to mają. Ale często nie z takim samym naciskiem.

C # ma

  • Składnia podobna do C.
  • głównie imperatywny obiektowy, z pewnymi funkcjami FP
  • wpisany statycznie
  • głównie użycie wsadowe z kompilatorem wsadowym
  • zarządzana pamięć za pomocą Garbage Collection

Nie pomaga to, że C # ma na przykład funkcje manipulacji kodem, jeśli nie są one powszechnie używane, jak w Lisp. W Lisp nie znajdziesz wielu programów, które nie wykorzystują makr na wiele prostych i złożonych sposobów. Używanie manipulacji kodami to naprawdę duża funkcja w Lisp. Emulacja niektórych zastosowań jest możliwa w wielu językach, ale nie czyni jej tak istotną jak w Lisp. Zobacz książki takie jak „On Lisp” lub „Practical Common Lisp”.

To samo dotyczy interaktywnego programowania. Jeśli opracujesz duży system CAD, większość prac programistycznych będzie interaktywna z edytora i REPL - bezpośrednio do działającej aplikacji CAD. Możesz emulować wiele z nich w języku C # lub innych językach - często przez implementację języka rozszerzenia. Dla Lisp głupio byłoby zrestartować rozwijaną aplikację CAD w celu dodania zmian. System CAD zawierałby wtedy również ulepszoną Lisp, która dodałaby funkcje językowe (w postaci makr i opisów symbolicznych) do opisywania obiektów CAD i ich relacji (części, zawartości, ...). Można robić podobne rzeczy w C #, ale w Lisp jest to domyślny styl programowania.

Jeśli tworzysz złożone oprogramowanie za pomocą interaktywnego procesu programistycznego lub twoje oprogramowanie ma znaczną część symboliczną (meta programowanie, inne paradygmaty, algebra komputerowa, kompozycja muzyki, planowanie, ...), Lisp może być dla Ciebie.

Jeśli pracujesz w środowisku Windows (ja nie), to C # ma tę przewagę, ponieważ jest głównym językiem programowania Microsoft. Microsoft nie obsługuje żadnej formy Lisp.

Ty piszesz:

  • Przestrzenie nazw Nawet przy użyciu metod statycznych lubię wiązać je z „klasą”, aby kategoryzować ich kontekst (wydaje się, że Clojure to ma, a CL nie.)

Common Lisp nie dołącza przestrzeni nazw do klas. Przestrzenie nazw są dostarczane przez pakiety symboli i są niezależne od klas. Jeśli używasz CLOS, musisz zmienić orientację swojego podejścia do programowania obiektowego.

  • Świetna kompilacja i wsparcie w czasie projektowania system typów pozwala mi określić „poprawność” struktur danych, które przekazuję wokół wszystkiego, co jest źle napisane, jest podkreślane w czasie rzeczywistym; Nie muszę czekać, aż środowisko uruchomieniowe, aby poznać ulepszenia kodu (takie jak użycie podejścia FP zamiast imperatywnego) są automatycznie sugerowane

Użyj kompilatora Lisp. Informuje o nieznanych zmiennych, może mieć zalecenia dotyczące stylu (w zależności od zastosowanego kompilatora), daje wskazówki dotyczące wydajności, ... Kompilator jest naciśnięty klawisz.

  • Narzędzia programistyczne GUI: WinForms i WPF (wiem, że Clojure ma dostęp do bibliotek GUI Java, ale są one dla mnie całkowicie obce).

Komercyjne Lisps, takie jak LispWorks i Allegro CL, oferują podobne rzeczy pod Windows.

  • Narzędzia do debugowania GUI: punkty przerwania, wejście, przejście, inspektorzy wartości (tekst, xml, niestandardowy), zegarki, debugowanie według wątku, warunkowe punkty przerwania, okno stosu wywołań z możliwością przeskakiwania do kodu na dowolnym poziomie na stosie (Szczerze mówiąc, moje doświadczenie z Emacsem + Szlamem wydaje się zapewniać niektóre z tego, ale jestem częściowo zwolennikiem podejścia opartego na VS GUI)

Komercyjne Lisps, takie jak LispWorks i Allegro CL, oferują to wszystko pod Windows.

Rainer Joswig
źródło
1
Moja krytyka tak naprawdę nie dotyczy Lisps. Uważam, że są całkiem zdolni. Szukam tego, co Lisp może zrobić / dać mi, czego jeszcze nie zrobiłem. W pełni rozumiem, że większość programistów C # nie korzysta z wielu „nowych” funkcji językowych. Myślę, że większą rzeczą jest to, że robię i, szczerze mówiąc, poza GUI, prawie piszę w stylu 100% FP. FP był skokiem koncepcyjnym, ale warto. Jednak przejście na Lisp, z którego jestem, wydaje się nie przynosić korzyści. Mam nadzieję, że znajdę powód, dla którego powinienem dać temu szansę.
@Jonathan Mitchem: C # tak naprawdę nie wygląda na język FP, nawet jeśli język obsługuje pewne funkcje i może z nich korzystać kilka bibliotek. Jeśli chcesz programować FP w ekosystemie Microsoft, F # może być dla Ciebie.
@ Rainer Nie, to nie wygląda na język FP, ale może to zrobić i dość zwięźle. A kiedy chcę / potrzebuję kodu rozkazującego lub OO, mogę to zrobić. (nieco nie na temat: tak naprawdę nie uważam F # za poważny język, w który warto się zagłębić, wolę omijać monady w Haskell. Ale kiedy byłem programistą C ++ i C # wyszedł, nie rozważałem tego „prawdziwy” język [i wtedy to nie było, imho])
@Jonathan Mitchem: dlaczego powinienem szukać? Napisałem, że C # obsługuje niektóre funkcje FP. Właśnie dodał do imperatywnego języka obiektowego i nie czyni go idiomatycznym, jak powiedzmy w Lisp, Scheme, a zwłaszcza nie jest porównywany z SML, F #, Haskell lub innymi głównie językami funkcjonalnymi. Chodzi mi o to: niektóre FP są możliwe w C #, ale nie jest to podstawowa funkcja.
5

Rainier Joswig powiedział to najlepiej.

Dla mnie potęgi Lisp nie można zrozumieć, patrząc tylko na listę funkcji Lisp. W przypadku Lisp, aw szczególności Common Lisp, całość jest większa niż suma części. To nie tylko REPL plus wyrażenia s plus makra plus wieloskładowa wysyłka plus zamknięcia plus pakiety plus CLOS plus restarty plus X, Y i Z. To wszystko jest genialnie zintegrowane. To codzienne doświadczenie, które bardzo różni się od codziennego korzystania z innych języków programowania.

Lisp wygląda inaczej, jest używany inaczej, podczas programowania podchodzi się do programowania w inny sposób. Jest elegancki, kompletny, duży i bezproblemowy. Nie jest pozbawione własnych wad i pecadilloes. Z pewnością istnieją porównania z innymi językami, które mogą być wykonane tam, gdzie mogą się nie pojawić. Ale w żadnym wypadku Lisp nie jest tylko językiem X plus REPL i makrami, ani językiem Y plus wysyłanie wielu metod i restartowanie.


źródło
3
Kod to dane (i makra) - być może nie „dostałem” makra, ale nie widziałem żadnego przykładu, w którym pomysł makra nie mógłby zostać zaimplementowany jako funkcja; nie zmienia „języka”, ale nie jestem pewien, czy to jest siła

Zazwyczaj makro należy stosować do czegoś, czego nie można wyrazić jako wywołanie funkcji. Nie wiem, czy istnieje warunek podobny do przełącznika w C # (podobny do tego, co istnieje w C, jak przełącznik (formularz) {przypadek ...}), ale załóżmy, że ma i ma podobne ograniczenia ( wymagające typu integralnego).

Teraz załóżmy dalej, że chcesz coś, co wygląda tak, ale wywołuje ciągi. W lisp (cóż, szczególnie w Common Lisp i prawdopodobnie w innych), to jest „tylko” makro z dala, a jeśli ograniczysz użytkownika do posiadania stałych ciągów (prawdopodobnie nie uciążliwych) do dopasowania, możesz (czas kompilacji) obliczyć minimalna sekwencja testów w celu ustalenia, który przypadek pasuje (lub użyj tabeli skrótów, przechowując zamknięcia).

Chociaż może być możliwe wyrażenie tego jako funkcji (ciąg porównania, lista przypadków i zamknięć do wykonania), prawdopodobnie nie wyglądałby jak „normalny kod” i to tam makra lisp mają wyraźną wygraną. Prawdopodobnie używany sporo makra lub specjalne formy taht są makro-jak, jak defun, defmacro, setf, cond, loopi wiele innych.

Vatine
źródło
2

To jest najlepszy artykuł wyjaśniający, jaki znalazłem, który zabiera czytelnika z powierzchni rzecznictwa Lisp, aby pokazać niektóre głębsze implikacje zastosowanych pojęć:

http://www.defmacro.org/ramblings/lisp.html

Pamiętam, jak czytałem gdzie indziej taki pomysł: inne języki przyjęły różne funkcje Lisp; wyrzucanie elementów bezużytecznych, dynamiczne pisanie, anonimowe funkcje, zamknięcia w celu uzyskania lepszego C ++ / C / Algol. Jednak, aby uzyskać pełną moc Lisp, potrzebujesz wszystkich jego podstawowych funkcji, w tym momencie masz inny dialekt Lisp, odrębnej rodziny języków, która istnieje w tej czy innej formie od ponad 50 lat.

Spacebat
źródło