Funkcyjne programowanie (dalej FP) to paradygmat, który pomimo coraz większej popularności i coraz lepszego wsparcia w językach programowania, wciąż wydaje się nieoczywisty w codziennym programowaniu.

Na tę kwestię warto spojrzeć szerzej, ponieważ stosowanie dowolnego paradygmatu wiąże się z przyjęciem pewnych odgórnych zasad, które z jednej strony torują myślenie o problemach, a z drugiej strony ograniczają pole manewru. Innymi słowy, oddajemy część kontroli/władzy w zamian za większą przewidywalność.

W przypadku FP dąży się do uzyskania kodu, który nie zarządza stanem ani również nie powoduje efektów ubocznych w postaci dodatkowych działań takich jak np. zapisanie pliku czy wysłanie wiadomości. To co zaskakuje w tym podejściu, to fakt, że z punktu widzenia użytkownika, taki kod tak naprawdę nie robi nic użytecznego. W takim razie, jaki jest powód dla którego programiści wybierają to podejście?

Motywacją do stosowania FP są korzyści wynikające z oddzielenia obliczeń od pozostałych akcji jakie mają miejsce w aplikacji.

Jeśli funkcja tylko oblicza wynik, bez wywoływania dodatkowych efektów, wtedy jest łatwiejsza w czytaniu oraz testowaniu. Dzieje się tak, ponieważ funkcja zawsze obliczy ten sam wynik dla tych samych danych wejściowych .

Spójrzmy na poniższy kod, który rysuje siatkę na ekranie.

Możemy wtedy:

  • bezpośrednio wywołać funkcje odpowiedzialne za rysowanie,
  • albo zgodnie z FP utworzyć funkcję, która będzie wolna od efektów.

Przedstawiony kod oblicza w pierwszej kolejności linie:

def grid_lines(pos, width, height, rows, cols):
    x, y = pos
    cell_width = width // cols
    cell_height = height // rows

    for row_y in range(rows + 1):
        line_start_pos = (x, y + cell_height * row_y)
        line_end_pos = (x + width, y + cell_height * row_y)
        yield line_start_pos, line_end_pos

    for col_x in range(cols + 1):
        line_start_pos = (x + cell_width * col_x, y)
        line_end_pos = (x + cell_width * col_x, y + height)
        yield line_start_pos, line_end_pos

by następnie użyć tę funkcję w następujący sposób:

for start_pos, end_pos in grid_lines(
    pos=(x, y),
    width=self.CELL_SIZE * self.CELL_COLS,
    height=self.CELL_SIZE * self.CELL_ROWS,
    rows=self.CELL_ROWS,
    cols=self.CELL_COLS
):
    pygame.draw.line(self._screen, color, start_pos, end_pos)

Docelowe użycie funkcji wygląda teraz trywialnie, ponieważ  grid_lines odpowiada za przeprowadzenie obliczeń.


Niestety stosowanie FP nie zawsze ma sens.

Gdyby funkcja grid_lines w języku Python, przy każdym wywołaniu zamiast generatora, zwracała listę krotek, wówczas takie podejście byłoby niepraktyczne z punktu widzenia wydajności.