F#-ban a következő három fő könyvtár érhető el alapértelmezésben:
Továbbá a következő könyvtárakat szokták még használni F# alkalmazásokban:
Típusok: A típusokat a "Namespace-neve.Type" előjelzővel használhatjuk. Egyszerűen "Type" is irható, ha egy "open Namespace-neve" deklarációt is megadtunk a program elején.
Konstruktorok: Egy .NET típus példányának létrehozására a new Type(arg1,...,argN) alakú kifejezések használhatók.
Metódusok és property-k: Objektumok metódusait, property-it és attribútumait a "obj.Method(arg1,...,argN)", "obj.Property" illetve "obj.Field" szintaktikával érhetjük el.
Statikus metódusok és property-k: Statikus tagfüggvényeket "Namespace.Type.Method(arg1,...,argN)" vagy a "Type.Method(arg1,...,argN)" hivatkozással érhetjük el, hasonlóan a property-khez és attribútumokhoz.
Például a System.IO.FileStream használata
Void: A „void” megfelelője F#-ban a "unit" .Például a System.Console.WriteLine() típusa "unit"
Delegate-ok (függvénypointerek): Delegate-okat egyszerűen a delegate kulcsszó használatával és a megfelelő argumentumlista megadásával definiálhatunk. Például, ha a delegate szignatúrája a következő: delegate void EventHandler(object,EventArgs) majd adjuk át a függvényt, melynek típusa: obj -> EventArgs -> unit, ahol a típusellenőrző meg fogja tenni a szükséges típuskényszerítéseket.
Event-ek: Az event Add/AddHandler/RemoveHandler/Fire metódusaival használhatjuk őket. Az Add egy függvény értéket kap paraméterként, a többi függvény használata egyszerûbb, delegált értékeket várnak.
Például: menu.Click.Add(fun evArgs -> expr), ahol expr egy callback függvény helyes implementációja. Az események szintén használhtók a Microsoft.FSharp.Idioms.IDelegateEvent és Microsoft.FSharp.Idioms.IEvent típusok értékeként.
Az F# értékek, amelyek .NET interfészeket implementálnak, objektum kifejezések használatával generálhatók.
Egy objektum kifejezés egy osztály vagy interfész kiterjesztettjét és/vagy implementációját deklarálja. Példának vegyük a következõt, az F# beépített, többalakú < és > operátorok funkcionalitását használjuk, hogy létrehozzunk egy objektumot, amely implementálja a .NET IComparer interfészt.
Jellemzõen az F# egy új osztályt í, ami az adott típus kiterjesztettje vagy implementációja.
Ha a virtuális propertiket szeretnénk felülírni vagy implementálni, akkor a "get_PropertyName" és "set_PropertyName" metódusokat kell felüldefiniálni, amelyek implementálják a propertiket. Hasonlóan mûködnek a virtuális eventek is, itt pedig az "add_EventName", "remove_EventName" és "fire_EventName" metódusokat kell felüldefiniálni.
Az objektum kifejezésen keresztül létrehozott objektumok szintén támogatják a többszörös interfészeket. A szintaxisa:
{ new with
interface with
...
interface with }
Az interfészek nem részei az objektum kifejezéses típusnak, de a típus teszteken keresztül felderíthetõk.
Minden F# és .NET érték konvertálható System.Object típusura - amit F#-ban obj-nek hívnak. Ennél fogva az F# értékek tárolhatók olyan heterogén gyûjteményekben mint a System.Collections.ArrayList. A vissza felé történõ konverzió dinamikusan ellenõrzött és így hibákat okozhat. Az F# a következõ operátorokat támogatja a konvertáláshoz oda, és vissza erre a típusra:
box: 'a -> obj
unbox: obj -> 'a
Az upcast(típus kényszerítés felfelé) és boxing kikényszerítése akkor történik meg, amikor átadjuk az értéket a függvényeknek, amelyek elfogadnak általánosabb típusokat az F# kényszerítési relációja szerint. A kényszerítések akár mikor alkalmazhatók, amikor a feltételek teljesülnek egy paraméter elõírása és aktuális értéke között. Egy boxing kényszerítés csak akkor fog teljesülni, ha egy F# értékt egy .NET érték típus és a kényszerítés célpontja egy .NET referencia típus (tipikusan System.Object). A .NET és F# minden osztálya és interfésze konvertálható a bázis típusaik vagy interfészeik tranzitív lezártjára. Más F# típusok (rekordok, diszkriminánsos uniók és absztrakt típusok) nem rendezhetõk hierarchiába és így csak obj típusra vagy olyan interfész típusra konvertálható, amelynek explicit bõvíthetõk a típusai.
.NET típusok downcastolhatók is olyan típusra, amelyek benne vannak a kényszerítési relációban, ezek futásidõben kivételeket válthatnak ki. F# támogatja a következõ operátorokat, amelyek .NET értékrõl konvertálnak tiszteletben tartva a kényszerítési relációt:
expr==
| e :? ty -- dinamikusan teszteli, hogy 'e' 'ty' típusú-e. Fordítási idejû hibát okoz, ha a lokális változó típusa nem olyan, hogy érvényes lehet a lefelé irányuló típus tesztre.
| e :> ty -- statikus felfelé történõ típus kényszerítés 'e'-t 'ty'-ra. Fordítási idejû hibát okoz, ha a lokális változó típusa nem olyan, hogy érvényes lehet az upcast.
| e :?> ty -- dinamikusan lefelé történõ típus kényszerítés 'e'-t 'ty'-ra. Fordítási idejû hibát okoz, ha a lokális változó típusa nem olyan, hogy érvényes lehet a downcast.
| downcast e -- futás idejû downcast, 'e'-t egy tetszõleges a környezetében látható típusúra konvertálja. Fordítási idejû hibát okoz, ha a lokális változó típusa nem olyan, hogy érvényes lehet a downcast. Megjegyzés: a struktúra átdolgozás alatt
| upcast e -- statikusan ellenõrzött upcast'e'-rõl egy tetszõleges a környezetében látható típusúra. Fordítási idejû hibát okoz, ha a lokális változó típusa nem olyan, hogy érvényes lehet az upcast.
Megjegyzés: a struktúra átdolgozás alatt és lehet, hogy elavult
pat==
| :? ty -- Ez a minta megegyezik egy .NET típus teszttel. Fordítási idejû hibát okoz, ha a lokális változó típusa nem olyan, hogy érvényes lehet a lefelé irányuló típus tesztre.
| :? ty as id -- Ez a minta megegyezik egy .NET típus teszttel. Ha sikeres akkor az 'id' változót az adott típusú értékhez köti.
Az e :? ty kifejezés ekvivalens egy dinamikus típus teszttel. Egy figyelmeztetést kapunk, ha az e típusról statikusan nem meghatározható, hogy 'ty'-nak egy altípusa. Egy error fog megjelenni, ha atípus teszt mindig sikerrel jár.
A :? minta különösen jól használható .NET kivételosztályok tesztelésére. Például:
Érvényes kasztolás lehetséges a .NET típusok és azok osztály- vagy interfész kiterjesztettje között, valamint az F# referencia típusai és 'obj' típusa között.
F# kódban a .NET referencia ípusok null értékez is felvehetnek. A null érték ellenõrizhetõ és a következõ konstrukciókat kozták létre:
expr==
| null -- egy tetszõleges típusnak a null értékét generálja a közvetlen környezetbõl. Fordítási idejû hibát okoz, ha a lokális típus információ nem garantálja, hogy a típus érték biztosan egy .NET referencia típus.
pat==
| null -- a minta egy null tesztnek felel meg. Fordítási idejû hibát fog okozni, ha a lokális típus nem biztosítja, hogy az érték, amivel összehasonlítják az egy .NET referncia típus.
Itt egy példa, ahol egy null tesztet valósítunk meg mintával:
Itt egy példa, ahol a null tesztet kifejezéssel használjuk:
Forráskód szinten a null nem egy érvényes érték más F# típusok számára és ha valaki megkísérli a null ellenõrzõ konstrukciók használatát olyan típusokkal, mint az int akkor a fordító panaszkodni fog. Abban az esetben szintén, ha egy null ellenõrzést hajt végre vagy egy null érékez generál egy változónak, de néhány F# értékre használható lesz.
A teljes F# kódban gyakran felteszik, hogy a .NET referencia értékek nem null-ok. Ezért egy jó megoldás, hogy ha kiküszöböljük a null-t olyan hamr, amint lehet. Igazából csak az API-knál lenne szükséges használatuk.
A null nem része egyébként az F# rendszernek, ezen oknál fogva a fordító nem ellnörzi, hogy lett e ellenörzés téve az API hívások után. Ha nem végez ellenörzést akkor a C#-hoz hasonlóan NullPointerException-t vált ki, amikor a az objektumon meghívódik egy metódus.
Megjegyzés: lehetséges generálni null értéket minden F# típushoz számos hátsó ajtón keresztül, például veszünk egy null objektumot és egy F# típussá alakítjuk.
Példa
A következő példa azt mutatja be, hogy miként érhetjük el F#--ból a .NET Reflection könvvtárát. Kékkel jelöltük azokat a kódrészleteket, ahol a .NET könyvtárakkal kommunikálunk.
F# kód direktben hozzáférhető C#-ból és más .NET nyelvekből, úgy, hogy egyszerűen a felhasználható értékeket statikus metódusokként illetve statikus mezőkként használjuk. F# környezetben definiált típusok is hasonló egyszerűséggel érhetők el.
A következő példa az mutatja, hogy mikét tehetőek meg a fent leírt dolgok.
Konkrét típusok (rekord, absztrakt és disztkriminánsos unio típusok): Elnevezett F# típusokra könnyen hivatkozhatunk más .NET nyelvbõl egyszerûen idézve a helyes típus nevet. A beépített típusokra, mint a list és az option a Microsoft.FSharp.List és Microsoft.FSharp.Option nevek használhatók.
Generic típusok: Konkrét paraméteres F# típusoknak meg kell adni az alkalmazott generic paramétereket abban az esetben, ha az F# kód le lett fordítva ahhoz, hogy használhassuk.
Típus rövidítések. F# típus rövidítések kerülendõk, mert például nem láthatók más .NET nyelvbõl.
F# adat típusok .NET osztélyokhoz történõ fordítása támogatással megy végbe azért, hogy elérjék az adat típus által hordozott informácóz. Néha kiegészítõ alosztályokat is generálnak diszkriminánsnak, bár ezek nem használhatók közvetlenül.
F# diszkriminánsos uniót felépítõ értékek.
Egy statikus metódus minden egyes diszkrimináns számára hozzáférhetõ, például:
F# diszkriminánsos unió megkülönböztetése C#-ban: A következõ rész arra az esetre vonatkozik, ha több mint egy diszkriminánsunk van. A támogatottsága nagyon kis mértékkben függ attól, hogy a "null"-t használja-e az F# compiler az adattípus egy lehetséges értékének reprezentációjaként. Ez azért van, mert a típusok, melyek "null"-t használnak reprezentációban, azok nehány elõforduló tagját nem képesek támogatni (ezek NullPointerException-t fognak eredménezni, amikor C#-ból használnánk õket).
A "null"-t az F# fordító csak pontosan a következõ szituációkban fogja használni:
Ha a "null"-t nem használjuk a típus reprezentálásakor, akkor a típus rendelkezni fog
Viszont abban az esetben, ha a "null"-t használjuk a típus reprezentációjában, a típus rendelelkezni fog egy egy GetTag() statikus metódussal, és különbözõ tag_... fordítási idejû konstansokkal. A megkülönböztetések ezek segítségével oldhatók meg. Ebben az esetben a a predikátumos megkülönböztetés végrehajtható az értékek "null"-lal történõ összehasonlításával, mivel a "null" érték akkor garantáltan csak az üres konstruktorban használt. Ezen oknál fogva, ha egy C# érték x MyType típusú és a MyType F#-beli definíciója: type MyType = A of ... | B of ... | C of ... akkor a következõ C# kód helyes:
Egy tuple típus neveinek és tagfüggvényeinek használata a következő alakban lehetséges:
Tuple<_,_>.Item1
Tuple<_,_>.Item2
Tuple<_,_,_>.Item1
Tuple<_,_,_>.Item2
Tuple<_,_,_>.Item3
Tuple<_,_,_,_>.Item1
Tuple<_,_,_,_>.Item2
Tuple<_,_,_,_>.Item3
Tuple<_,_,_,_>.Item4
...
Tuple<_,_,_,_,_,_,_>.Item1
...
Tuple<_,_,_,_,_,_,_>.Item7
A 7-nél nagyobb elemszámú rendezett n-eseknek iylen típusú elérése még specifikálás alatt van. A fenti nevek némiképpen megváltoznak, amikor .NET 1.0 vagy 1.1 keretrendszer alá programozunk, mivel akkor nem használhatók a generic típus aritásai, hogy egyértelmûvé tegyék a "Tuple" név aktuális variációját. Így a nevek Tuple2, Tuple3, stb-k lesznek egészen Tuple7-ig.
Az F#-beli rekordok adatmezõit property-ként kell használni (nem mezõként), például:
Természetesen azok a mezők, melyek nem csak olvashatóak, rendelkeznek set propertyvel is.
Az F# függvény típusának lefordított alakja Microsoft.FSharp.FastFunc<A,B>. Ezen már nem fognak változtatni, bár a Microsoft.FSharp.FastFunc osztály implementációjának részleteit lehet, hogy átdolgozzák. A FastFunc<A,B> nem egy delegált típus, ugyanis ezt a lehetőséget végül elvetették a rossz hatékponyság és a nehézkes interoperabilitás miatt.
Függvényhívások:
A függvény értékek az f.Invoke metódus használatával hívhatók meg. Több paramétert is elfogadó függvény típusok az iterált függvény típusokon keresztül érhetõk el mint például: int -> int -> int. Létezik ennek néhány optimalizált megoldása, melyek statikus metódusai használhatók e célra: Microsoft.FSharp.FastFunc.InvokeFast2, Microsoft.FSharp.FastFunc.InvokeFast3 stb.
Pontosan egy paramétert váró függvények létrehozása
Fontos, hogy képesek legyünk létrehozni és használni a FastFunc típusú értékeket C#-ból olyan egyszerû és természetes módon amennyire csak lehetséges. Egy lehetõség függvény érték létrehozására, hogy a FastFunc<A,B> alosztályait használjuk az "Invoke" metódus túlterhelésével. Azonban nem ez az ajánlott módja ilyen értékek létrehozására, mivel ekkor nem lehetséges a C# névnélküli delegált tulajdonságának kihasználása, és így mikor létrejönne a delegált, minden változóját, amelyre a függvényben hivatkozunk, kézzel kellene "elkapni".
A FastFunc létrehozásának ajánlott módja egy névtelen delegált használata. Egyszerûen létrehozható egy megfelelõ .NET függvény szerû delegált (pl.: System.Converter) és azután meghívható a Microsoft.FSharp.FuncConvert.ToFastFunc egy túlterhelt példánya. Jellemzõen FuncConvert.ToFastFunc(...) a következõ túlterheléseket támogatja:
Ráadásul van egy implicit konverzió a System.Converter<T,U>-ról FastFunc<T,U>-ra, és ezen oknál fogva kihagyható a FuncConvert.ToFastFunc() hívás, de CSAK akkor, amikor a delegált típusa System.Converter<A,B> (valamilyen A-ra, B-re). Pédául a következõk ekvivalensek:
Több paramétert elfogadó függvények létrehozása:
A fenti technikák remekül müködnek, amikor olyan F# függvény értéket hozunk létre, amelyek egy paramétert várnak. Azonban ezek alkalmatlanok lehetnek, amikor olyan függvényekre próbáljuk használni, melyek több paramétert is elfogadnak. Ezen oknál fogva az F# könyvtár definiál kiegészítésként hasonló típusokat azért, hogy támogassa a konverziókat. Jellemzõen a következõk definiáltak:
Továbbá a Microsoft.FSharp.FuncConvert.ToFastFunc(...) metódus a következő túlterhelésekkel rendelkezik, amelyekkel a "curried" függvényeket lehet készíteni:
Például:
Olyan F# függvény értékek létrehozására, melyek rendezett n-est kapnak paraméterként, a Microsoft.FSharp.FuncConvert.ToTupledFunc használható.