29 października 2012

Różnica między wyrażeniem a wyrażeniem (instrukcją)

Istnieje różnica między wyrażeniem a wyrażeniem. Jaka?! - zapytasz? Natychmiast idzie to wyłapać przy przejściu na język angielski, w którym mamy dwa różne słowa odpowiadające pojedynczemu w polskim - "expression" oraz "statement" (aczkolwiek dla tego drugiego Google Translator wskazuje na "oświadczenie" czy "deklaracja" przed "wyrażenie").

Jeśli pracujesz z językami imperatywnymi, np. Java, w których króluje "statement" i/lub językami funkcyjnymi, np. Scala lub Clojure, w których prym wiedzie "expression", warto znać różnicę. Przyznaję, że mi to trochę zajęło (zajęcia na studiach jakoś tak wtedy nużyły).

Niby oczywista jest ta różnica, ale nie wszyscy ją wyłapują. U mnie długo trwało zanim zaskoczyło. "expression" zawsze zwraca wartość i jest jej reprezentantem, a "statement" może, ale nie musi i zwykle stosowany jest dla skutków ubocznych, np. przypisanie częściowego wyliczenia do zmiennej poza nim, np. pętla for w Javie.

Od tej chwili usilnie próbuję zapamiętać, że w językach funkcyjnych korzystamy wyłącznie z "wyrażeń", podczas gdy w imperatywnych również i "instrukcji" (patrz Wikipedia w Instrukcja (informatyka) w sekcji Wyrażenia).

I już nie mam problemów ze wskazaniem wyrażenia od instrukcji. A nawet w tłumaczeniu!

Dla zwrócenia uwagi, posłużę się kawałkami kodu w Clojure.

If jest specjalną formą (= wyrażeniem) w Clojure (w przeciwieństwie do Javy) i działa na podobieństwo funkcji, które można przypisywać (aliasować), przekazywać do czy zwracać z funkcji.
user=> ; przypisanie
user=> (def if-inside
  #_=>   (if nil "nil jest prawda" "nil nie jest prawda"))
#'user/if-inside
user=> if-inside
"nil nie jest prawda"

user=> ; przekazanie do funkcji
user=> (defn my-fn [if-stmt]
  #_=>   if-stmt)
#'user/my-fn
user=> (my-fn if-inside)
"nil nie jest prawda"

user=> ; zwrocenie z funkcji
user=> (defn my-fn-returns-if-stmt [if-stmt]
  #_=>   (fn [] if-stmt))
#'user/my-fn-returns-if-stmt
user=> (my-fn-returns-if-stmt if-inside)
#<user$my_fn_returns_if_stmt$fn__3525 user$my_fn_returns_if_stmt$fn__3525@2304a962>
user=> ((my-fn-returns-if-stmt if-inside))
"nil nie jest prawda"
Proste, co?

A może jednak nie? Pytaj, aby było! Mówią, że to podstawy podstaw programowania funkcyjnego i brak zrozumienie fundamentów może negatywnie "promieniować" na dalszą znajomość jego.

3 komentarze:

  1. Uwaga na stwierdzenie „If jest specjalną formą (= wyrażeniem)” — forma specjalna i wyrażenie to nie to samo więc stawianie znaku równości jest tu niewłaściwe. Poza tym warto może zwrócić uwagę na to, że w Clojure nie każde wyrażenie zwraca wartość. Na przykład następujące nie zwraca: (while true).
    Grzegorz Balcerek

    OdpowiedzUsuń
    Odpowiedzi
    1. Dobrze, że o tym wspominasz, bo sam ostatnio się nad tym głowiłem.

      (while) jest makrem i formą, a forma nie musi być wyrażeniem, bo staje się nim, kiedy jest wykonywalna. Tyle teorii. Innymi słowy, wszystko w Clojure jest formą, która po wczytaniu staje się wyrażeniem.

      Pewnie niewiele to wyjaśnia, bo więcej powtarzam niż piszę ze zrozumieniem. Pracuję nad tym, aby to zmienić.

      Usuń
    2. Rozszerzając odpowiedź - http://clojure.org/evaluation opisuje proces wykonania sekwencji form i rzuca światło na cały proces wprowadzania poleceń, które są kompilowane i uruchamiane w Clojure.

      Właśnie brnę przez ten materiał, aby opanować koncept.

      Usuń