Type inference in F#

 
21 augustus 2009

Elke Javaan en Dotnetter is 8% (gokje hoor) van zijn tijd bezig om de compiler te vertellen dat de i variabele van het type int is, net zoals die andere tig i variabelen in de rest van de code. En als we een lap tekst tussen haakjes zetten, we een string bedoelen. Wat een [ouderwets] gedoe hé. En dat in deze moderne eeuw. Terwijl er intelligente programma’s bestaan die Kasparov van het bord afschaken is het even te veel gevraagd om te (tijdens compiletime) detecteren wat voor type we nu zouden bedoelen. Terwijl, als de compiler een beetje doordenkt het vaak wel erg voor de handliggend is wat het nu zou moeten zijn. Het gevolg is dat iedere nieuwe typed taal sceptisch wordt ontvangen. Wat de voordelen van static boven dynamic typed ook moge zijn, dit gedoe willen we gewoon niet meer. What about F#?

Uitleg

F# is een static typed language wat wil zeggen dat de compiler het type van elke constuctie bepaalt tijdens de compilatie. Normaal gesproken heeft dit tot gevolg dat er voor elk type een z.g. type annotatie geplaatst moet worden om zo de compiler te informeren over het bedoelde type. F# is uitgerust met een listig type inference systeem dat voor veel gevallen het type kan detecteren zodat type annotaties niet meer benodigd zijn. Dit wordt bereikt door het type te af te leiden van de context waarin het wordt gebruikt. Dit gebeurt via een ‘bottom-up’ methode, dus van binnen uit naar buiten toe in een functie context. In de praktijk zal er in enkele gevallen een type annotatie toegevoegd moet worden om de compiler een handje te helpen. Dit maakt de code een stuk minder verbose.

Voorbeelden

01. let add (a:int) (b:int) : int =
02.  a + b
03. val add : int -> int -> int

Hier hebben we een functie gespecificeerd die twee integers accepteert en als resultaat een integer terug geeft. De twee parameters a en b en de return type hebben we hier expliciet gedefinieerd met een type annotatie. Dit is echter niet nodig omdat de compiler dit type ook kan detecteren. De operator + werkt standaard met integers dus a) moeten a en b ook wel integers zijn en b) moet het return type ook wel een integer zijn. Vandaar dat we de functie ook zo mogen schrijven.

01. let add a b = a + b
02. val add : int -> int -> int

Wanneer we deze functie uitvoeren met twee floats als parameters krijgen we een error. We zullen de compiler een handje moeten helpen door een constraint (beperkende regel) toe te voegen. Hiervoor kunnen we a) óf het returntype specificeren b) óf een parameter als float annoteren c) óf een value van de + operator annoteren. In alle drie de gevallen zorgt de constraint ervoor dat de hele functie met floats werkt.

01. let add a b : float =
02.  a + b
03. val add : float -> float -> float

Constraint met gespecificeert returntype.

01. let add (a:float) b =
02.  a + b
03. val add : float -> float -> float

Constraint met type annotatie op de eerste parameter.

01. let add a b =
02.  a + (b:float)
03. val add : float -> float -> float

Constraint met type annotatie op een waarde van de operator.

Mooi hé?


Werken met ?
Kijk dan bij onze mogelijkheden voor zowel starters als ervaren engineers.


Categorieën: Development

Tags:


Reactie

  • Hoi Andries, heel veel van deze type inference zie je ook terugkomen in lambda expressies in C#. Niet raar, aangezien die op F# gebaseerd schijnen te zijn.

    Redelijk ongerelateerd, maar wel leuk, aangezien je dynamisch en statisch getypeerde talen aanstipt: http://www.pphsg.org/cdsmith/types.html – een redelijk helder overzicht van (on-)waarheden in het debat over deze beide stijlen.

    Geplaatst op 24 augustus 2009 om 20:41 Permalink