Nahoru
 

Deklarativní programování

Představte si programovací jazyk, kterému řeknete, co má udělat a on to udělá. Neřeknete mu, jak to má udělat, ale přesto se k výsledku dokáže dostat sám. Na první pohled se může zdát, že to snad ani není možné. Deklarativní programovací paradigma však přesně takto funguje.

Co je to deklarativní programování?

Deklarativní programování je programovací paradigma, kde kód vyjadřuje logiku výpočtu, avšak ne jeho průběh. Jinými slovy, programu řekneme „co má udělat“ nikoliv „jak to má udělat“. Tím docílíme minimalizace (v nejlepším případě eliminace) vedlejších účinků snahy o kontrolování výpočtu (špatné přičtení jedničky, záměna proměnných, atp.)

Jazyky deklarativního programování často uvažují nad programy jako nad teoriemi formální logiky. Výpočty provádí jako dedukci v dané teorii (například v jazyce Prolog se využívá Hornova klauzule).

Mezi používané jazyky v oblasti deklarativního programování řadíme:

  • Databázové dotazovací jazyky (např. SQL)
  • Regulární výrazy
  • Funkcionální programovací jazyky (např. Lisp)
  • Logické programovací jazyky (např. Prolog)

Deklarativní vs. Imperativní programování

Na druhé straně břehu od deklarativního programování stojí programování imperativní. V tom naopak chápeme kód jako vyjádření průběhu výpočtu. Zajímá nás hlavně, jak program provádí výpočet.

Zde je však nutné říci, že teoreticky sice jdou imperativní a deklarativní programování proti sobě, ale v praxi často jazyky umožňují využití obou těchto myšlenek. Jde tedy spíše o způsob, jakým nad programem přemýšlíme, než o stoprocentní zaškatulkování programovacího jazyka buď do imperativního nebo deklarativního paradigmatu.

Jistým způsobem používáme deklarativní styl programování i v imperativních jazycích. Když vytvoříme například v jazyce C funkci, která vypočte n-té číslo fibonacciho posloupnosti, tak ji musíme naimplementovat a říct ji přesně co má udělat, a jak má proběhnout výpočet. To je přesně myšlenka imperativního programování. Při každém dalším využití však už neimplementujeme funkci znovu, ale pouze ji využijeme znovu a už nás nezajímá, jak je funkce implementována. Chceme pouze výsledek. Pokud jsme ji navíc implementovali před dlouhou dobou, už možná ani nevíme, jak je implementována. V tomto případě už se dá hovořit o deklarativním programování. Chceme n-té fibonacciho číslo a nezajímá nás, jak to funkce vyřeší.

Ukázka přechodu od imperativního programování k deklarativnímu

Jako demonstraci deklarativního programování si uvedeme příklad přechodu od plně imperativního kódu k deklarativnímu. Budeme pracovat v jazyce C#. V naší aplikaci máme následující objekt zaměstnance, kterého budeme chtít filtrovat podle příjmení.

class Employee
{
  public string Firstname;
  public string Lastname;

  ...
}

Pro účely filtrování vytvoříme třídu EmployeeFilter, které v konstruktoru předáme naše zaměstnance, které následně budeme filtrovat.

class EmployeeFilter
{

  List<Employee> employees; // nase pole zamestnancu

  public EmployeeFiler(List<Employee> employees)
  {
    this.employees = employees;
  }
  ...
}

Nelekejte se zápisu List<Employee> employees, je to v podstatě jen natahovací pole, které umožňuje ukládat objekty typu Employee. Filtrování podle příjmení má fungovat tak, že si zvolíme libovolný prefix příjmení a program nám vybere pouze ty zaměstnance, kteří začínají na tento prefix.

public List<Employee> FilterByLastname(string lastname)
{
  // nove pole pro zamestnance
  List<Employee> filteredList = new List<Employee>(); 

  for (int i = 0; i < employees.Count; i++)
  {
    if (employees[i].Lastname.StartsWith(lastname)) // shoduji se prijmeni?
    {
      filteredList.Add(employees[i]); // pridej do pole	
    }
  }

  return filteredList;
} 

Pomocí cyklu projedeme všechny zaměstnance a pokud se shoduje začátek příjmení s daným prefixem, tak jej přidáme do kolekce filteredList, který na konci celé metody vrátíme. Naše řešení funguje, ale není ani trochu deklarativní. Zadání znělo „Vyber pouze ty zaměstnance, kteří začínají na zadané příjmení“. Abychom se drželi deklarativního paradigmatu, tak bychom programu musel říct pouze co chceme, nikoliv jak má probíhat výpočet. Zde máme cyklus a řídící proměnnou i, které nám přímo říkají, jak má program probíhat. Proměnné i by se šlo snadno zbavit následujícím způsobem.

public List<Employee> FilterByLastname(string lastname)
{
  List<Employee> filteredList = new List<Employee>();

  foreach (Employee e in employees)
  {
    if (e.Lastname.StartsWith(lastname))
    {
      filteredList.Add(e); 
    }
  }

  return filteredList;
}

Záměnou cyklu for za foreach se zdánlivě nic nezměnilo, ale zbavili jsme se proměnné i, která nám stejně pouze specifikovala rozsah v procházení zaměstnanců v kolekci. Jelikož chceme postupně ozkoušet všechny zaměstnance, je zde daleko lepší použít foreach. Řešení se o malý krůček přiblížilo k deklarativnímu stylu, ale pořád to není ono. Použití cyklu přímo indikuje, že chceme řídit výpočet. Jak tedy zajistit, abychom řekli programu pouze co chceme, a ne jak to udělat? Řešení v tomto případě nalezneme v použití technologie LINQ, která nám umožní pěkný deklarativní zápis.

public List<Employee> FilterByLastname(string lastname)
{
  return employees.Where(e => e.Lastname.StartsWith(lastname)).ToList();
}

Není zde žádný cyklus, který by říkal, jak řídit program. Jediné, co se od nás program dozvěděl je „Vyber z listu employees všechny zaměstnance, které začínají na uvedené příjmení“. Poslední uvedenou implementaci lze zařadit do deklarativního paradigmatu. Tím, že nekontrolujeme průběh výpočtu, se typicky zkrátí a zpřehlední kód.

Podobnost s matematikou

Další zajímavost deklarativního programování představuje podobnost s matematickým zápisem. Mějme například množinu přirozených čísel N = {1, 2, 3, …} a chceme z ní vybrat všechny čísla větší než 10. Matematicky množinu zapíšeme takto: {x | x ∈ ℕ ∧ x > 10 } . Říkáme tak, že chceme všechna x, která patří do přirozených čísel a které jsou větší než 10.

Matematický zápis množiny
Obrázek č. 1: Ukázka podobnosti matematického zápisu množiny a deklarativního zápisu programu.

Každý, kdo se někdy přichomýtl k jazyku SQL, musí tu podobnost vidět. Náhradou matematických symbolů za slova totiž dostaneme stejný zápis v jazyce SQL.

SELECT number
FROM N
WHERE number > 10

Pokud chcete vědět více o deklarativním programování a nebojíte se angličtiny, podívejte se na přednášku Declarative Thinking, Declarative Practice od Kevlina Henneye.

Zdroje: