V sobotu 2. listopadu proběhla mohutná oslava naší plnoletosti !!
Multimediaexpo.cz je již 18 let na českém internetu !!

F Sharp

Z Multimediaexpo.cz

Název tohoto článku není z technických důvodů zcela správný. Správný název by měl být F#.

F# (vyslovované anglicky jako F Sharp, doslova to označuje notu fis) je multiparadigmatický programovací jazyk pro .NET spojující funkcionální přístup s imperativním objektově orientovaným přístupem. Syntaxe jazyka vychází z ML a OCaml a dále je ovlivněna jazyky Haskell, C# a LINQ. F# je plně podporovaným jazykem pro platformu .NET a bude součástí připravovaného Visual Studia 2010[1]. V současné době se o vývoj jazyka stará Microsoft Research.

Obsah

Cíle jazyka a souhrn vlastností

F# byl vyvinutý jako varianta ML s mnoha konstrukcemi převzatými z OCaml. Narozdíl od mnoha skriptovacích jazyků se rychlostí blíží k C# (především díky silné typovosti). Podporuje také řadu dynamických programovacích technik jako je například reflexe. F# umožňuje propojení s dalšími jazyky včetně snadné implementace DSL, bezproblémově spolupracuje se všemi jazyky .NET.

Microsoft Research[2] zmiňuje mezi hlavní výhody jazyka tyto:

  • funkcionální jazyk se stručnou syntaxí a implicitním typováním
  • možnost interaktivního skriptování (jako v Pythonu)
  • kombinace typové bezpečnosti a implicitního typování (jako v ML)
  • výkon na úrovni C#, nativní běh na .NET frameworku
  • přístup ke všem knihovnám .NET
  • integrovanost a plná podpora ve Visual Studiu

F# je silně typový jazyk který ovšem používá implicitní typování (datový typ proměnné se nemusí specifikovat explicitně, překladač ho rozpozná podle přiřazované hodnoty). Jako jazyk pro .NET podporuje F# všechny typy z .NET frameworku ale navíc přidává několik neměnných typů (změna jejich hodnoty je možná pouze vytvořením nové kopie) svázaných se specifickými vlastnostmi jazyka a používaných především pro úlohy funkcionálního programování. Těmito typy jsou: tuple, record, discriminated union, list a function. V této souvislosti stojí za zmínku i že typy .NET jsou standardně v F# neměnné, opaku lze docílit použitím klíčového slova mutable.

Důležitou vlastností jazyka je interaktivní skriptování, které umožňuje komponenta F# Interactive[3]. Syntaxe jazyka se pro skriptování v některých detailech liší, navíc je možné používat tzv. light syntaxi. Jde ale jen o drobné rozdíly v ukončování příkazů.

Syntaxe jazyka

Syntaxe jazyka jak už bylo řečeno vychází hlavně z ML a OCaml. Nejvýrazněji se na vzhledu kódu podepisují jazykové konstrukce implicitního typování a pattern matchingu.

V jazyce existují komentáře dvojího typu, řádkové uvozené // a víceřádkové začínající znaky (* a končící *).

Proměnná (name v terminologii F#, protože se nejedná o proměnné v klasickém slova smyslu, mohou totiž uchovávat i funkce) se deklaruje tak že jejímu názvu předchází klíčové slovo </code>let</code>, které znamená automatické přiřazení typu proměnné (podobně jako var v C# 3.0).

Hodnota se proměnné přiřadí při deklaraci pomocí operátoru =, pokud je potřeba ji později změnit (pouze v kontextu měnitelných proměnných označených klíčovým slovem mutable), slouží k tomu operátor <-.

Pro spolupráci s .NET je důležité konstrukce pro tvorbu objektů převzatá z C# - objekty se tvoří pomocí klíčové slova new. Pro „oznámení“ používání nějakého namespace se používá klíčové slovo open (ekvivalent using v C#). K vlastnostem a metodám se přistupuje klasicky přes tečkovou notaci a metody se volají s parametry v závorkách.

Mezi literály, mimo klasické čísla a řetězce, patří také některé konstrukce F#, jako tuple (viz dále).

Datový typ (třída) se deklaruje pomocí klíčového slova type a to jak typy .NET tak i speciální typy F#, konkrétněji viz níže nebo v [3].

Funkce se definují několika způsoby, buď pomocí klíčového slova fun, nebo jako výraz kde na levé straně jsou vedle let a názvu také parametry a za rovnítkem následuje definice. Rekurzivní funkce musejí být jako rekurzivní explicitně definovány pomocí klíčového slova rec<code>.

Důležitou konstrukcí F# je pattern matching, umožňuje porovnávat výraz s jinými resp. hledět na jeden výraz z několika pohledů. Nejčastější aplikací je rozložení typu tuple (viz níže) nebo řízení toku programu konstrukcí <code>match proměnná with | když-výraz -> tak-výraz kde část po | se může libovolněkrát opakovat. Tato konstrukce může nahradit řadu ifů a switchů a zlepšit čitelnost kódu.

F# podporuje i základní cykly. Ty jsou vlastně funkce bez návratové hodnoty (v terminologii jazyka F# „výrazy typu unit“).

Klasický cyklus for je uvozen klíčovým slovem for, následuje řídící proměnná, rovnítko, počáteční hodnota, klíčové slovo to nebo downto (určující přičítání/odčítání jedničky), koncová hodnota, do a tělo cyklu. Jinak řečeno: for řídící_prom = počáteční_hodnota [down]to konečná_hodnota do tělo_cyklu.

Cyklus známý jako foreach (projde celé pole a do každé iterace nabídne jednu položku) se zapíše [for jedna_položka in procházené_pole do tělo_cyklu ].

Cyklus while má jednoduchou syntaxi while podminka do tělo_cyklu.

Datové typy F#

Všechny typy F# jsou aliasy pro speciální generické datové typy .NET. Mimoto jsou v F# další aliasy na často používané třídy .NET, například obj pro System.Object nebo ResizeArray<T> pro System.Collections.Generic.List<T> (kvůli odlišení od F# typu List)[3].

Tuple

Tuple je nejjednodušší speciální datový typ v F#. Umožňuje „zabalit“ dvě nebo více nepojmenovaných hodnot každou libovolného typu. Množství i typ hodnot musejí být známy už při překladu. Je to v podstatě primitivní přepravka vhodná pro interní použití jež ani nemusí být explicitně definována jako typ (vytvoří se automaticky při použití). Každý tuple je odvozen od generické třídy Tuple<_,_>.

Příklad uložení jména se soundexovým kódem a jeho rozložení na hodnoty pomocí jednoduché konstrukce pattern matching:

let mrCarroll = ("Carroll", "C64")
let (name, soundex) = mrCarroll

Tentýž příklad v interaktivním režimu (s odpověďmi konzole):

> let mrCarroll = ("Carroll", "C64");;
val mrCarroll : string * string = ("Carroll", "C64")
> let (name, soundex) = mrCarroll;;
val soundex : string = "C64"
val name : string = "Carroll"

Record

Record (záznam) je rozšířeným typem tuple. Umožňuje pojmenovat jednotlivé datové složky a přistupovat k nim známou „tečkovou notací“, velmi se tak podobá typu struct v C#. Record už musí být definován jako typ. S .NET je record provázán tak že při jeho definici se vytvoří třída a jednotlivé datové složky má jako vlastnosti.

Příklad vytvoření Recordu a odvození z něj druhého:

type ShopItem = { Name:string; Price:int }
let commonCar = { Name="car"; Price=1000 }
let luxuryCar = { commonCar with Price=5000 }

Discriminated union

Discriminated union je typ který umožňuje uložení hodnoty jednoho z různých nabídnutých typů. O konkrétním typu se rozhoduje podle volaného konstruktoru.

Příklad elegantní reprezentace binárního stromu pomocí Discriminated union. Zápis int * BinaryTree * BinaryTree vyjádřuje typ tuple s třemi položkami, první typu int a zbylé dvě typu BinaryTree.

type BinaryTree =
| Fork of int * BinaryTree * BinaryTree
| Leaf of int
let heap = Fork (2,Fork (17,Fork (22,Leaf (49),Leaf (31)),Leaf (51)),Fork (29,Leaf(11),Leaf (25)))

List

List (seznam) je typický spojovaný seznam tvořený vždy položkou a odkazem na zbytek seznamu v zápise první_položka::zbytek_seznamu (tzv tail operátor známý např. z LISPu) nebo ve zkráceném zápise [ hodnota1;hodnota2;... ]. Prázdnýseznam se zapíše [].

F# obsahuje také speciální jazykovou konstrukci pro tvorbu typu array který ale je standardním typem .NET a není tedy neměnný. Lze ho vytvořit konstrukcí [| hodnota1;hodnota2;... |].

Příklad dvou možných zápisů tvorby seznamu List:

let list1 = 1::2::3::[]
let list2 = [1; 2; 3]

Function

V F# je každá funkce typem. Funkce jsou také neměnným typem, ale F# nabízí řadu možností jak upravovat jejich volání, například použitím curryingu nebo skládáním funkcí, funkce mohou být předávány jako parametry dalších funkcí, F# také podporuje lambda funkce.

Příklad definice jednoduché funkce pro sčítání a následně z ní odvozené funkce pro přičtení 10tky (jednoduchá ukázka techniky zvané currying) a nakonec volání které vyústí v hodnotu 20 uloženou v proměnné twenty:

let add = ( fun lhs rhs -> lhs + rhs )
let add10 = add 10
let twenty = add10 10

Funkci add lze také nadefinovat takto:

let add lhs rhs = lhs + rhs

Příklad funkce sumy všech hodnot v Listu. Řešeno pro funkcionální jazkyky typickým způsobem (odpojení první položky, její zpracování, a rekurzivní volání na zbytek pole), využívá pattern matching a ilustruje deklaraci rekurzivní funkce klíčovým slovem rec.

let list = [ 1 .. 10 ] (* seznam se všemi položkami od 1 do 10 *)
//pomocná funkce s akumulační proměnnou acc
let rec sumAcc acc list = match list with
| top::tail -> sumAcc (acc + top) tail
| _ -> acc
//výsledná funkce sumy
let sum list = sumAcc 0 list
let res = sum list (* val res : int = 55 *)

K předchozímu příkladu stojí za zmínku i vlastnost jazyka F# podobná se zásobníkovými jazyky, totiž že obě funkce by se mohly jmenovat stejně a program by fungoval správně, druhá by skryla první a sama by ji uvnitř bez problémů volala. Je to dáno způsobem, jakým v F# funguje obor hodnot proměnné[3].

Vlastnosti jazyka

F# používá pattern matching jednak aby převedl jména na hodnoty, ale také pro kontrolu, zda mají data požadovanou strukturu nebo hodnotu. Pattern matching může být použit se všemi standardními F# typy, nejčastěji s typy tuple a discriminated union. Jazyk F# také podporuje obecnější pattern matching pomocí tzv. active patterns, které umožňují kontrolu dat z různých pohledů na typ.

Protože je jazyk F# určen pro platformu .NET (musí vyhovovat požadavkům CLI) a protože jako jedno z paradigmat podporuje imperativní objektově orientované programování, obsahuje konstrukce pro tvorbu smyček for a while a .NET tříd i rozhraní (v terminologii F# společně nazývané object types – objektové typy) v podobném rozsahu jako ostatní jazyky .NET[3].

F# od verze 1.9.1 obsahuje tzv. sequence expressions (sekvenční výrazy)[4] zapisované jako seq{ ... } , které obsahují sekvenční bloky různých konstrukcí. Na rozdíl od ostatních jazykových konstrukcí F# jsou „líně vykonávané“ (lazily evaluated, tj. vyhodnocují se až v momentě využití). Mohou být použity pro filtrování i pro zkrácení zápisu (mnoharozměrné) kolekce. Jsou základem pro podporu LINQ a důležité pro asynchronní volání ( async{ ... } ).

F# je díky kombinaci vlastností funkcionálních a objektových jazyků vhodný na programování asynchronních operací a vícevláknových aplikací pro víceprocesorové systémy.

Jako jedno z paradigmat které F# do jisté míry přejímá je Language-oriented programming[3], usnadňuje tak tvorbu DSL, ovšem omezenou syntaxí jazyka (lze „pouze“ změnit význam příkazů F#). Nejjednodušší příklad je využití discriminated unions jako deklarativní jazyk na vyhodnocování aritmetických výrazů[3]. F# rozšiřuje schopnosti .NET System.Reflection a umožňuje tak meta-programming (meta programování – manipulování s kódem jako s daty).

Název jazyka

Název jazyka F# je, podobně jako v ostatních jazycích .NET (počínaje jazykem C#), odvozen z hudební notace, kde křížek označuje zvýšení noty o půl tónu a v tomto případě by označoval notu fis, tedy F zvýšené o půl tónu.

Křížek na počítačové klávesnici (#) a křížek v hudební nauce (♯) jsou dva odlišné znaky. Pro zápis názvu jazyka F Sharp se nepoužívá znak hudebního křížku z technických důvodů, protože tento se na standardní klávesnici nevyskytuje. Pro zjednodušení se tak používá klasický křížek.

Příklady

Příklad nejjednodušší aplikace:

printfn "Ahoj světe."

Příklad demonstrující syntaxi jazyka na funkci rekurzivně počítající faktoriál:

let rec factorial n = match n with
    | 0 -> 1
    | _ -> n * factorial (n – 1)

Příklad cyklu for, vypíše čísla od 1 do 10:

for n = 1 to 10 do printfn "%d" n

Příklad cyklu while:

let mutable n = 3
while n < 10 do
    n <- (n+1)
    printfn "%d" n

Příklad cyklu for (foreach) v F#, vrátí pole sudých čísel od 2 do 20:

let nums = [1..10]
[ for num in nums do yield num*2 ]

Příklad jednoduché okenní aplikace .NET:

(* otevření formulářové knihovny .NET *)
open System.Windows.Forms
(* vytvoření okna(Form) a nastavení vlastností *)
let form = new Form(Visible=true, TopMost=true, Text="Welcome to F#")
(* vytvoření nápisu(Label) s textem *)
let label =
  let temp = new Label()
  let x = 3 + (4 * 5)
  (* nastavení hodnoty vlastnosti Text *)
  temp.Text <- sprintf "x = %d" x
  (* vrácení hodnoty (do proměnné label) *)
  temp
(* přidání nápisu do okna *)
do form.Controls.Add(label)
(* spuštění aplikace *)
[<STAThread>]
do Application.Run(form)

Externí odkazy

  1. . Dostupné online.  
  2. . Dostupné online.  
  3. 3,0 3,1 3,2 3,3 3,4 3,5 3,6 . Dostupné online.  
  4. . Dostupné online.