Přeskočit na obsah

F Sharp

Z Wikipedie, otevřené encyklopedie
Název tohoto článku nemůže z technických důvodů obsahovat znak #. Správný název by měl být F#.
F#
Paradigmamultiparadigmatický
Vznik2002
AutorMicrosoft Research, Don Syme
VývojářMicrosoft, The F# Software Foundation
Poslední verze3.1.1[1] (24. 1. 2014)
Typová kontrolastatické, silné, implicitní
Ovlivněn jazykyOCaml, C#, Python, Haskell, ML, LINQ, Scala, Erlang
Ovlivnil jazykyF*, LiveScript, C#
OSMultiplatformní (.NET, Mono)
LicenceApache license
WebMicrosoft Research - F#

F# (vyslovované anglicky jako F Sharp, /ef ʃɑɹp/, 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 je součástí Visual Studia[2] od verze 2010. V současné době se o vývoj jazyka stará Microsoft Research.

Cíle jazyka a souhrn vlastností

F# byl vyvinutý jako varianta ML s mnoha konstrukcemi převzatými z OCaml. Na rozdí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[3] 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, že i 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[4]. 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 let, které zároveň znamená automatické určení 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ého 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[4].

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.

Důležitou konstrukcí F# je pattern matching, umožňuje porovnávat výraz s jinými a podle toho rozhodnout o dalším postupu výpočtu, přitom pojmenuje a tím získá přístup na složky dat. Nejčastější aplikací je rozložení typu tuple (viz níže) nebo řízení toku programu konstrukcí 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)[4].

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 atributy.

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 vyjadř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ý spojový 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í jazyky 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é[4].

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[4].

F# od verze 1.9.1 obsahuje tzv. sequence expressions (sekvenční výrazy)[5] 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[4], 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ů[4]. 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

Výpis na standardní výstup:

printfn "Ahoj světe."

Jednoduchá rekurzivně definovaná funkce, která slouží k výpočtu faktoriálu:

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

Cyklus s předem známým počtem opakování (typu for), který vypíše celá čísla od 1 do 10:

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

Cyklus s předem neznámým počtem opakování (typu while), jenž vypíše celá čísla od 4 do 10:

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

Cyklus s předem známým počtem opakování (typu foreach), který pole deseti celých čísel od 1 do 10 přetaví na pole deseti sudých čísel od 2 do 20:

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

Jednoduchá aplikace .NET s grafickým uživatelským rozhraním:

(* 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)

Reference

  1. Archivovaná kopie. visualstudiogallery.msdn.microsoft.com [online]. [cit. 2014-10-05]. Dostupné v archivu pořízeném dne 2014-02-22. 
  2. SOMASEGAR, Sam. F# - A Functional Programming Language [online]. [cit. 2009-12-02]. Dostupné online. 
  3. Microsoft Research [online]. [cit. 2009-12-02]. Dostupné online. 
  4. a b c d e f g PETŘÍČEK, Tomáš. F# Language Overview [online]. [cit. 2009-12-01]. Dostupné online. 
  5. SYME, Don. Some Details on F# Computation Expressions [online]. [cit. 2009-12-03]. Dostupné online.