Projektowanie składni - po co używać nawiasów, gdy nie są przekazywane żadne argumenty?

66

W wielu językach function_name(arg1, arg2, ...)do wywołania funkcji służy składnia . Kiedy chcemy wywołać funkcję bez żadnych argumentów, musimy to zrobić function_name().

Dziwne wydaje mi się, że kompilator lub interpreter skryptów wymagałby ()skutecznego wykrycia go jako wywołania funkcji. Jeśli wiadomo, że zmienna jest wywoływalna, dlaczego nie function_name;wystarczy?

Z drugiej strony w niektórych językach możemy zrobić: function_name 'test';a nawet function_name 'first' 'second';wywołać funkcję lub polecenie.

Myślę, że nawiasy byłyby lepsze, gdyby były potrzebne tylko do określenia kolejności priorytetów, a w innych miejscach byłyby opcjonalne. Na przykład wykonywanie if expression == true function_name;powinno być tak samo ważne jak if (expression == true) function_name();.

Moim zdaniem najbardziej irytujące jest to, że 'SOME_STRING'.toLowerCase()funkcja prototypowa wyraźnie nie potrzebuje żadnych argumentów. Dlaczego projektanci zdecydowali się na prostsze 'SOME_STRING'.lower?

Oświadczenie: Nie zrozum mnie źle, uwielbiam składnię podobną do C! ;) Po prostu pytam, czy mogłoby być lepiej. Czy wymaganie ()ma jakieś zalety wydajnościowe, czy też ułatwia zrozumienie kodu? Jestem naprawdę ciekawy, jaki jest dokładnie powód.

David Refoua
źródło
103
Co byś zrobił, gdybyś chciał przekazać funkcję jako argument do innej funkcji?
Vincent Savard
30
Tak długo, jak ludzie muszą czytać kod, czytelność jest najważniejsza.
Tulains Córdova
75
O to chodzi z czytelnością, pytasz (), ale tym, co wyróżnia się w twoim poście, jest if (expression == true)stwierdzenie. Martwisz się o to, co zbędne (), a potem użyj zbędnego == true:)
David Arno,
15
Visual Basic na to pozwolił
edc65
14
Było to obowiązkowe w Pascalu, używałeś parenów tylko dla funkcji i procedur, które przyjmowały argumenty.
RemcoGerlich,

Odpowiedzi:

251

W przypadku języków korzystających z funkcji pierwszej klasy dość często składnia odwoływania się do funkcji jest następująca:

a = object.functionName

podczas gdy akt wywołania tej funkcji to:

b = object.functionName()

aw powyższym przykładzie byłoby odwołanie do powyższej funkcji (i można ją wywołać wykonując a()), podczas gdy bzawierałoby wartość zwracaną funkcji.

Podczas gdy niektóre języki mogą wykonywać wywołania funkcji bez nawiasów, może być mylące, czy wywołują funkcję, czy po prostu odnoszą się do funkcji.

Nathan Merrill
źródło
9
Warto wspomnieć, że ta różnica jest interesująca tylko w przypadku skutków ubocznych - dla czystej funkcji nie ma to znaczenia
Bergi
73
@Bergi: To ma duże znaczenie dla czystych funkcji! Jeśli mam funkcję, make_listktóra pakuje swoje argumenty do listy, istnieje duża różnica między f(make_list)przekazywaniem funkcji do fpustej listy.
user2357112,
12
@Bergi: Nawet wtedy nadal chciałbym móc przekazać SATInstance.solveinną, niewiarygodnie kosztowną, czystą funkcję do innej funkcji bez natychmiastowej próby jej uruchomienia. Sprawia również, że robi się naprawdę niezręcznie, jeśli someFunctionrobi coś innego w zależności od tego, czy someFunctionjest zadeklarowane jako czyste. Na przykład, jeśli mam (Pseudokod) func f() {return g}, func g() {doSomethingImpure}oraz func apply(x) {x()}, po czym apply(f)zwraca f. Jeśli następnie stwierdzę, że fjest czysty, nagle apply(f)przechodzi gdo apply, applywzywa gi robi coś nieczystego.
user2357112,
6
@ user2357112 Strategia oceny jest niezależna od czystości. Wykorzystanie składni wywołań jako adnotacji, kiedy ocenia się, co jest możliwe (ale prawdopodobnie niezręczne), jednak nie zmienia to wyniku ani jego poprawności. Jeśli chodzi o twój przykład apply(f), jeśli gjest nieczysty (a applytakże?), To wszystko jest nieczyste i oczywiście się psuje.
Bergi,
14
Przykładami są Python i ECMAScript, które nie mają tak naprawdę podstawowej koncepcji „metod”, zamiast tego masz nazwaną właściwość, która zwraca anonimowy obiekt funkcji pierwszej klasy, a następnie wywołujesz tę anonimową funkcję pierwszej klasy. W szczególności, zarówno w Pythonie, jak i ECMAScript, powiedzenie foo.bar()to nie jedna operacja „wywołaj metodę barobiektu foo”, ale raczej dwie operacje, „dereferencyjne pole barobiektu, foo a następnie wywołanie obiektu zwróconego z tego pola”. Tak, .to operator selektor pola i ()jest operator call i są niezależne.
Jörg W Mittag
63

Rzeczywiście, Scala pozwala na to, chociaż obowiązuje konwencja: jeśli metoda ma skutki uboczne, i tak należy użyć nawiasów.

Jako autor kompilatora uważam, że gwarantowana obecność nawiasów jest całkiem wygodna; Zawsze wiedziałbym, że jest to wywołanie metody, i nie musiałbym budować rozwidlenia dla nieparzystego przypadku.

Jako programista i czytnik kodów obecność nawiasów nie pozostawia wątpliwości, że jest to wywołanie metody, mimo że nie są przekazywane żadne parametry.

Przekazywanie parametrów nie jest jedyną cechą charakterystyczną wywołania metody. Dlaczego miałbym traktować metodę bez parametrów inaczej niż metodę, która ma parametry?

Robert Harvey
źródło
Dzięki i podałeś uzasadnione powody, dla których jest to ważne. Czy możesz również podać przykłady, dlaczego projektanci języków powinni używać funkcji w prototypach?
David Refoua,
obecność nawiasów nie pozostawia wątpliwości, że jest to metoda, z którą się całkowicie zgadzam. Wolę języki programowania bardziej niż jawne.
Corey Ogburn,
20
Scala jest w rzeczywistości nieco bardziej subtelna: podczas gdy inne języki dopuszczają tylko jedną listę parametrów o zerowych lub więcej parametrach, Scala pozwala na zero lub więcej list parametrów o zerowych lub większej liczbie parametrów. Tak więc def foo()jest to metoda z jedną pustą listą parametrów i def foojest to metoda bez listy parametrów, a te dwie rzeczy są różne ! Ogólnie metoda bez listy parametrów musi być wywoływana bez listy argumentów, a metoda z pustą listą parametrów musi być wywoływana z pustą listą argumentów. Wywołanie metody bez listy parametrów z pustym argumentem jest w rzeczywistości…
Jörg W Mittag
19
… Interpretowane jako wywołanie applymetody obiektu zwróconego przez wywołanie metody z pustą listą argumentów, podczas gdy wywoływanie metody z pustą listą parametrów bez listy argumentów może w zależności od kontekstu być różnie interpretowane jako wywoływanie metody z pustą listą argumentów, η-rozwinięcie w częściowo zastosowaną metodę (tj. „odniesienie do metody”), w przeciwnym razie może się nie skompilować. Ponadto Scala przestrzega zasady jednolitego dostępu, nie rozróżniając (syntaktycznie) między wywołaniem metody bez listy argumentów a odwołaniem do pola.
Jörg W Mittag,
3
Nawet gdy doceniam Ruby z powodu braku wymaganej „ceremonii” - parens, nawiasów klamrowych, wyraźnego typu - mogę powiedzieć, że nawias metodyczny czyta się szybciej ; ale kiedyś znajomy idiomatyczny Ruby jest dość czytelny i zdecydowanie szybszy do napisania.
radarbob
19

To właściwie subtelny przypadek wyboru składni. Będę mówić w językach funkcjonalnych, które są oparte na typowym rachunku lambda.

We wspomnianych językach każda funkcja ma dokładnie jeden argument . To, co często nazywamy „wieloma argumentami”, jest w rzeczywistości pojedynczym parametrem typu produktu. Na przykład funkcja, która porównuje dwie liczby całkowite:

leq : int * int -> bool
leq (a, b) = a <= b

bierze jedną parę liczb całkowitych. Nawiasy nie oznaczają parametrów funkcji; służą do dopasowania argumentu do wzorca . Aby cię przekonać, że to naprawdę jeden argument, zamiast tego możemy użyć funkcji projekcyjnych zamiast dopasowywania wzorca do zdekonstruowania pary:

leq some_pair = some_pair.1 <= some_pair.2

Tak więc nawiasy są wygodą, która pozwala nam dopasować wzór i zaoszczędzić trochę pisania. Nie są wymagane.

Co z funkcją, która rzekomo nie ma argumentów? Taka funkcja faktycznie ma domenę Unit. Pojedynczy element Unitjest zwykle zapisywany jako (), dlatego pojawiają się nawiasy.

say_hi : Unit -> string
say_hi a = "Hi buddy!"

Aby wywołać tę funkcję, musielibyśmy zastosować ją do wartości typu Unit, która musi być (), więc w końcu piszemy say_hi ().

Tak więc naprawdę nie ma czegoś takiego jak lista argumentów!

ogrodnik
źródło
3
I dlatego puste nawiasy ()przypominają łyżki minus uchwyt.
Mindwin
3
Zdefiniowanie leqfunkcji, która używa <raczej niż <=jest dość mylące!
KRyan
5
Ta odpowiedź może być łatwiejsza do zrozumienia, jeśli użyje słowa „krotka” oprócz wyrażeń takich jak „typ produktu”.
Kevin,
Większość formalizmów będzie traktować int f (int x, int y) jako funkcję od I ^ 2 do I. Rachunek Lambda jest inny, nie używa tej metody do dopasowania tego, co w innych formalizmach jest wysokie. Zamiast tego rachunek lambda wykorzystuje curry. Porównanie byłoby x -> (y -> (x <= y)). (x -> (y -> (x <= y)) 2 ocenia na (y-> 2 <= y) i (x -> (y -> (x <= y)) 2 3 ocenia na ( y-> 2 <= y) 3, co z kolei
daje wynik
1
@PeriataBreatta Nie znam historii używania ()do reprezentowania urządzenia. Nie zdziwiłbym się, gdyby choć część tego powodu przypominała „funkcję bez parametrów”. Istnieje jednak inne możliwe wyjaśnienie składni. W algebrze typów Unitjest tak naprawdę tożsamość typów produktów. Produkt binarny jest zapisywany jako (a,b), moglibyśmy napisać produkt jednoargumentowy jako (a), więc logicznym rozszerzeniem byłoby napisanie produktu zerowego tak prosto ().
ogrodnik
14

Na przykład w języku Javascript użycie nazwy metody bez () zwraca samą funkcję bez jej wykonania. W ten sposób możesz na przykład przekazać funkcję jako argument do innej metody.

W Javie wybrano, że identyfikator, po którym następuje () lub (...), oznacza wywołanie metody, podczas gdy identyfikator bez () odnosi się do zmiennej składowej. Może to poprawić czytelność, ponieważ nie masz wątpliwości, czy masz do czynienia z metodą, czy zmienną składową. W rzeczywistości ta sama nazwa może być używana zarówno dla metody, jak i zmiennej składowej, obie będą dostępne z ich odpowiednią składnią.

Florian F.
źródło
4
+1 Tak długo, jak ludzie muszą czytać kod, czytelność jest najważniejsza.
Tulains Córdova
1
@ TulainsCórdova Nie jestem pewien, czy perl się z tobą zgadza.
Racheet
@Racheet: Perl może nie, ale Perl Best Practicesnie
slebetman
1
@Racheet Perl jest bardzo czytelny, jeśli jest napisany jako czytelny. To samo dotyczy prawie każdego nie-ezoterycznego języka. Krótki, zwięzły Perl jest bardzo czytelny dla programistów Perla, podobnie jak kod Scala lub Haskell jest czytelny dla osób znających te języki. Mogę również mówić do ciebie po niemiecku w bardzo skomplikowany sposób, co jest gramatycznie całkowicie poprawne, ale nadal nie zrozumiesz słowa, nawet jeśli jesteś biegły w języku niemieckim. To też nie wina Niemca. ;)
simbabque
w java nie „identyfikuje” metody. To się nazywa. Object::toStringidentyfikuje metodę.
njzk2,
10

Składnia jest zgodna z semantyką, więc zacznijmy od semantyki:

Jakie są sposoby korzystania z funkcji lub metody?

Istnieje wiele sposobów:

  • można wywoływać funkcję z argumentami lub bez
  • funkcję można traktować jako wartość
  • funkcja może być częściowo zastosowana (tworząc zamknięcie przyjmujące co najmniej jeden mniej argumentu i zamykające przekazane argumenty)

Posiadanie podobnej składni do różnych zastosowań jest najlepszym sposobem na stworzenie albo dwuznacznego, albo co najmniej mylącego języka (i mamy ich dość).

W językach C i C-podobnych:

  • wywoływanie funkcji odbywa się za pomocą nawiasów w celu dołączenia argumentów (może ich nie być) jak w func()
  • traktowanie funkcji jako wartości można wykonać, po prostu używając jej nazwy jak w &func(C tworzenie wskaźnika funkcji)
  • w niektórych językach masz krótkie składnie do częściowego zastosowania; Java pozwala someVariable::someMethodna przykład (ograniczony do odbiornika metody, ale nadal przydatny)

Zwróć uwagę, że każde użycie ma inną składnię, co pozwala łatwo je rozróżnić.

Matthieu M.
źródło
2
Cóż, „łatwo” jest jedną z tych „jest oczywiste tylko wtedy, gdy ma już zrozumiałe” sytuacje. Początkujący byłby całkowicie uzasadniony, zauważając, że nie jest oczywiste bez wyjaśnienia, dlaczego niektóre znaki interpunkcyjne mają takie znaczenie.
Eric Lippert,
2
@EricLippert: Rzeczywiście, łatwo nie musi oznaczać intuicyjności. Chociaż w tym przypadku wskazałbym, że w matematyce używane są również nawiasy dla funkcji „wywoływania”. To powiedziawszy, znalazłem początkujących w wielu językach, którzy zmagali się bardziej z pojęciami / semantyką niż z ogólną składnią.
Matthieu M.,
Ta odpowiedź wprowadza w błąd rozróżnienie między „curry” a „częściowym zastosowaniem”. ::Operator w Javie jest częściowym operator aplikacji, a nie operator Zmiękczanie. Częściowa aplikacja to operacja, która przyjmuje funkcję i jedną lub więcej wartości i zwraca funkcję przyjmującą mniej wartości. Curry działa tylko na funkcjach, a nie na wartościach.
Periata Breatta
@PeriataBreatta: Dobry punkt, naprawiony.
Matthieu M.,
8

W języku z efektami ubocznymi IMO naprawdę pomaga rozróżnić (teoretycznie bez skutków ubocznych) odczyt zmiennej

variable

i wywoływanie funkcji, która może powodować działania niepożądane

launch_nukes()

OTOH, jeśli nie ma efektów ubocznych (innych niż te zakodowane w systemie typów), nie ma sensu nawet rozróżniać między odczytem zmiennej a wywołaniem funkcji bez argumentów.

Daniel Jour
źródło
1
Zauważ, że OP przedstawił przypadek, w którym obiekt jest jedynym argumentem i jest prezentowany przed nazwą funkcji (która również zapewnia zakres dla nazwy funkcji). noun.verbskładnia może być tak przejrzysta, jak noun.verb()i nieco mniej zaśmiecona. Podobnie funkcja member_zwracająca lewe odwołanie może oferować przyjazną składnię niż typowa funkcja ustawiająca: obj.member_ = new_valuevs. obj.set_member(new_value)( _postfiks jest wskazówką mówiącą „odsłonięty” przypominającą _ prefiksy funkcji „ukrytych”).
Paul A. Clayton,
1
@ PaulA.Clayton Nie rozumiem, w jaki sposób moja odpowiedź nie obejmuje tego: jeśli noun.verboznacza to wywołanie funkcji, jak odróżnić to od zwykłego dostępu członka?
Daniel Jour
1
Jeśli chodzi o czytanie zmiennych, które teoretycznie są wolne od skutków ubocznych, chcę tylko powiedzieć: zawsze uważaj na fałszywych przyjaciół .
Justin Time,
8

Żadna z pozostałych odpowiedzi nie próbowała odpowiedzieć na pytanie: ile powinno być nadmiarowości przy projektowaniu języka? Ponieważ nawet jeśli potrafisz zaprojektować język, który x = sqrt yustawi x na pierwiastek kwadratowy z y, nie oznacza to, że koniecznie powinieneś.

W języku bez nadmiarowości każda sekwencja znaków oznacza coś, co oznacza, że ​​jeśli popełnisz jeden błąd, nie otrzymasz komunikatu o błędzie, twój program zrobi coś niewłaściwego, co może być czymś zupełnie innym niż to, co robisz zamierzone i bardzo trudne do debugowania. (Jak każdy, kto pracował z wyrażeniami regularnymi, będzie wiedział). Mała nadmiarowość jest dobra, ponieważ umożliwia wykrycie wielu błędów, a im więcej jest nadmiarowości, tym bardziej prawdopodobne jest, że diagnostyka będzie dokładna. Teraz oczywiście możesz posunąć się za daleko (obecnie nikt z nas nie chce pisać w języku COBOL), ale równowaga jest odpowiednia.

Nadmiarowość pomaga również w czytelności, ponieważ jest więcej wskazówek. Angielski bez redundancji byłby bardzo trudny do odczytania, to samo dotyczy języków programowania.

Michael Kay
źródło
W pewnym sensie zgadzam się: języki programowania powinny mieć mechanizm „siatki bezpieczeństwa”. Ale nie zgadzam się z tym, że „mała nadmiarowość” w składni jest dobrym sposobem na obejście tego - jest to płyta główna , a przede wszystkim wprowadza hałas, który sprawia, że ​​błędy są trudniejsze do wykrycia, i otwiera możliwości błędów składniowych, które odwracają uwagę od prawdziwe błędy. Rodzaj gadatliwości poprawiającej czytelność ma niewiele wspólnego ze składnią, a bardziej z konwencjami nazewnictwa . A sieć bezpieczeństwa jest zdecydowanie najskuteczniej wdrożona jako silny system typu , w którym najbardziej przypadkowe zmiany spowodują, że program będzie źle napisany.
leftaroundabout,
@leftaroundabout: Lubię myśleć o decyzjach dotyczących projektowania języka w kategoriach odległości Hamminga. Jeśli dwie konstrukcje mają różne znaczenia, często pomocne jest, aby różniły się one co najmniej na dwa sposoby, a ich forma różni się tylko jednym sposobem generowania skoku kompilatora. Projektant język generalnie nie może być odpowiedzialny za zapewnienie, że identyfikatory są łatwo rozpoznawalne, ale jeśli na przykład przypisanie wymaga :=i porównania ==z =tylko jest użyteczna dla inicjalizacji kompilacji, wówczas prawdopodobieństwo literówek obracających się porównań do zadań będzie znacznie zmniejszone.
supercat
@leftaroundabout: LIkewise, gdybym projektował język, prawdopodobnie wymagałbym ()wywołania funkcji z zerowym argumentem, ale także wymagałby tokena, aby „wziął adres” funkcji. Poprzednia akcja jest znacznie częstsza, więc zapisanie tokena w mniej powszechnym przypadku nie jest aż tak przydatne.
supercat
@ superupat: cóż, znowu myślę, że to rozwiązuje problem na niewłaściwym poziomie. Przypisywanie i porównywanie są koncepcyjnie różnymi operacjami, podobnie jak ocena funkcji i przyjmowanie adresu funkcji. Dlatego powinny mieć wyraźnie różne typy, więc tak naprawdę nie ma już znaczenia, czy operatorzy mają małą odległość Hamminga, ponieważ każda literówka będzie błędem typu kompilacji.
leftaroundabout
@leftaroundabout: Jeśli przypisanie jest wykonywane do zmiennej typu boolean, lub jeśli typ zwracany przez funkcję jest wskaźnikiem funkcji (lub - w niektórych językach - rzeczywistą funkcją), zastępując porównanie przypisaniem, lub wywołanie funkcji z odwołaniem do funkcji, może dać program, który jest poprawny pod względem składniowym, ale ma zupełnie inne znaczenie niż wersja oryginalna. Sprawdzanie typu ogólnie znajdzie kilka takich błędów, ale wyróżnienie składni wydaje się lepszym podejściem.
supercat 10.10.16
5

W większości przypadków są to składniowe wybory gramatyki języka. Przydatne jest, aby gramatyka różnych indywidualnych konstruktów była (względnie) jednoznaczna, gdy zostaną wzięte razem. (Jeśli występują niejasności, jak w niektórych deklaracjach w C ++, muszą istnieć określone reguły rozwiązywania problemów.) Kompilator nie ma możliwości zgadywania; wymagane jest przestrzeganie specyfikacji języka.


Visual Basic w różnych formach rozróżnia procedury, które nie zwracają wartości, i funkcje.

Procedury muszą być wywoływane jako instrukcja i nie wymagają parens, tylko argumenty oddzielone przecinkami, jeśli występują. Funkcje muszą być wywoływane jako część wyrażenia i wymagają parens.

Jest to względnie niepotrzebne rozróżnienie, które sprawia, że ​​ręczne refaktoryzacja między tymi dwiema formami jest bardziej bolesne niż musi być.

(Z drugiej strony Visual Basic używa tych samych parens ()o referencje tablicy jak dla wywołań funkcji, więc odniesienia array wyglądać funkcja nazywa. A to ułatwia ręczne refaktoryzacji tablicy do wywołania funkcji! Tak, możemy rozważać inne języki użyć []„s dla odwołań do tablicy, ale dygresję ...)


W językach C i C ++ dostęp do zawartości zmiennych jest uzyskiwany automatycznie przy użyciu ich nazw, a jeśli chcesz odwoływać się do samej zmiennej zamiast jej zawartości, zastosuj &operator jednoargumentowy .

Ten rodzaj mechanizmu można również zastosować do nazw funkcji. Surowa nazwa funkcji mogłaby oznaczać wywołanie funkcji, podczas gdy &do określenia funkcji (samej) jako danych użyto by jednego operatora. Osobiście podoba mi się pomysł dostępu do pozbawionych argumentów funkcji ubocznych bez efektów o takiej samej składni jak zmienne.

Jest to całkowicie prawdopodobne (podobnie jak inne wybory składniowe).

Erik Eidt
źródło
2
Oczywiście, podczas gdy brak nawiasów języka Visual Basic w wywołaniach procedur jest niepotrzebnym rozróżnieniem w porównaniu z wywołaniami funkcji, dodanie do nich wymaganych nawiasów byłoby niepotrzebnym rozróżnieniem między instrukcjami , z których wiele w języku wywodzącym się z języka BASIC ma efekty, które są bardzo przypomina procedury. Minęło też trochę czasu, odkąd pracowałem w wersji MS BASIC, ale czy nadal nie pozwala na składnię call <procedure name> (<arguments>)? Dość pewny, że to był prawidłowy sposób korzystania z procedur, kiedy programowałem QuickBASIC w innym życiu ...
Periata Breatta
1
@PeriataBreatta: VB nadal zezwala na składnię „wywołania”, a czasem wymaga jej w przypadkach, gdy wywoływana rzecz jest wyrażeniem [np. Można powiedzieć „Zadzwoń, jeśli (jakiśWarunek, Delegat1, Delegat2) (Argumenty)”, ale bezpośredni wywołanie wyniku operatora „If” nie byłoby poprawne jako samodzielna instrukcja].
supercat
@PeriataBreatta Podobało mi się wywołanie procedur bez nawiasów w VB6, ponieważ poprawiło wygląd kodu podobnego do DSL.
Mark Hurd
@ superuper W LinqPad zdumiewające jest to, jak często muszę używać Callmetody, w której prawie nigdy nie muszę tego robić w „normalnym” kodzie produkcyjnym.
Mark Hurd
5

Spójność i czytelność.

Gdybym dowiedział się, że pan wywołać funkcję Xtak: X(arg1, arg2, ...), a potem oczekiwać, że działają w ten sam sposób przez nie argumenty X().

Teraz uczę się, że możesz zdefiniować zmienną jako jakiś symbol i używać jej w następujący sposób:

a = 5
b = a
...

Co ja bym pomyślał, kiedy to znajdę?

c = X

Twoje domysły są równie dobre jak moje. W normalnych okolicznościach Xjest to zmienna. Ale gdybyśmy podążyli twoją ścieżką, mogłaby to również być funkcja! Czy muszę pamiętać, czy który symbol odwzorowuje na którą grupę (zmienne / funkcje)?

Możemy nałożyć sztuczne ograniczenia, np. „Funkcje zaczynają się od dużej litery. Zmienne zaczynają się od małej litery”, ale jest to niepotrzebne i komplikuje sprawy, chociaż czasami może pomóc niektórym celom projektowym.

Uwaga 1: Inne odpowiedzi całkowicie ignorują jeszcze jedną rzecz: język może pozwalać na używanie tej samej nazwy zarówno dla funkcji, jak i zmiennej, i rozróżnianie według kontekstu. Zobacz Common Lispjako przykład. Funkcja xi zmienne xwspółistnieją doskonale.

Side-Note 2: zaakceptowane odpowiedź pokazuje nam składnię: object.functionname. Po pierwsze, nie jest uniwersalny dla języków z pierwszorzędnymi funkcjami. Po drugie, jako programista traktowałbym to jako dodatkową informację: functionnamenależy do object. To, czy objectjest obiektem, klasą czy przestrzenią nazw, nie ma większego znaczenia, ale mówi mi, że gdzieś należy . Oznacza to, że musisz albo dodać sztuczną składnię object.dla każdej funkcji globalnej, albo utworzyć ją, objectby zawierała wszystkie funkcje globalne.

W obu przypadkach tracisz możliwość posiadania oddzielnych przestrzeni nazw dla funkcji i zmiennych.

MatthewRock
źródło
Tak. Spójność jest wystarczającym powodem, kropka (nie że twoje inne punkty nie są ważne - są).
Mateusz
4

Aby dodać do innych odpowiedzi, weź przykład C:

void *func_factory(void)
{
        return 0;
}

void *(*ff)(void);

void example()
{
        ff = func_factory;
        ff = func_factory();
}

Gdyby operator wywołania był opcjonalny, nie byłoby sposobu na odróżnienie przypisania funkcji od wywołania funkcji.

Jest to tym bardziej problematyczne w językach, w których nie ma systemu typów, np. JavaScript, gdzie wnioskowania o typie nie można użyć do ustalenia, co jest, a co nie jest funkcją.

Mark K Cowan
źródło
3
JavaScript nie ma systemu typów. Brakuje deklaracji typu . Ale jest całkowicie świadomy różnicy między różnymi rodzajami wartości.
Periata Breatta
1
Periata Breatta: chodzi o to, że zmienne i właściwości Java mogą zawierać typy dowolnej wartości, więc nie zawsze możesz wiedzieć, czy jest to funkcja w czasie odczytu kodu.
reinierpost
Na przykład, co robi ta funkcja? function cross(a, b) { return a + b; }
Mark K Cowan
@MarkKCowan Wynika to z tego: ecma-international.org/ecma-262/6.0/…
curiousdannii