Fonctions anonymes

On appelle fonction anonyme une fonction qui n'a pas de nom. De même que l'on utilise souvent des entiers ou des chaînes de caractères sans vouloir les nommer, on peut souhaiter avoir une fonction (souvent courte) sans lui donner de nom. La syntaxe des fonctions anonymes est : fun <arg1> <argn> -> <expression>

> fun x -> x + 1;;
val it : int -> int = <fun:clo@0_3>

est une fonction anonyme qui ajoute 1 à son argument. Elle peut s'utiliser partout où une fonction classique peut être appelée.

> (fun x -> x + 1) 5;;
val it : int = 6

Une fonction anonyme est une expression comme une autre.

> if 4 < 5 then
    fun x -> x + 1
else
    fun x -> x - 1;;
val it : (int -> int) = <fun:it@18>
 
> (if 4 < 5 then
    fun x -> x + 1
else
    fun x -> x - 1) 6;;
val it : int = 7

Les deux définitions suivantes sont donc équivalentes :

> let next = fun x -> x + 1;;
val next : int -> int
 
> let next' x = x + 1;;
val next' : int -> int

On peut voir la deuxième définition comme étant du sucre syntaxique (c'est-à-dire, un raccourci) pour la première.

Application partielle

Regardons la définition suivante :

> let add x = fun y -> x + y;;
val add : int -> int -> int

La fonction a pour type int -> int -> int. La flèche est associative à droite, le type peut aussi s'écrire : int -> (int -> int). Une fonction qui prend un argument un entier et renvoie une fonction de type int -> int peut aussi être vue comme une fonction qui prend deux entiers en argument et renvoie un entier.

> let add x y = x + y;; // ce code est équivalent au précédent
val add : int -> int -> int

Quelques exemples :

> let add' = add 4;;
val add' : (int -> int)
 
> add' 5;;
val it : int = 9
 
> add 4 5;;
val it : int = 9

D'une manière générale, toute fonction qui prend n arguments peut être appelée avec k arguments, k < n. Le résultat est une fonction qui prend k - n arguments. Cela s'appelle l'application partielle.

Voici un autre exemple avec les fonctions min et max, toutes deux définies dans la bibliothèque standard.

> let m = min 4;;
val m : (int -> int)
 
> m 6;;
val it : int = 4
 
> m 2;;
val it : int = 2

Les définitions suivantes sont équivalentes :

> let add x y = x + y;;
val add : int -> int -> int
 
> let add x = fun y -> x + y;;
val add : int -> int -> int
 
> let add = fun x -> fun y -> x + y;;
val add : int -> int -> int
 
> let add = fun x y -> x + y;;
val add : int -> int -> int

Opérateurs

Si l'on a besoin d'utiliser un opérateur comme une expression classique, il suffit de le mettre en parenthèses :

> (+);;
val it : (int -> int -> int) = <fun:it@46>

Un opérateur étant une fonction, on peut l'utiliser en notation préfixe (pour les amateurs de Lisp) :

> (+) 4 6;;
val it : int = 10

Ou utiliser l'application partielle :

> (+) 4;;
val it : (int -> int) = <fun:it@47>

On constate aussi que (+) est le nom de l'opérateur, c'est en effet un identifiant comme un autre. Il est donc possible, pour s'amuser, de redéfinir l'opérateur (+).

On redéfinit (+) localement :

> let (+) x y = x - y in 3 + 4;;
val it : int = -1
 
> let (-) = (+) in 5 - 3;;
val it : int = 8

Plus utile, et moins dangereux, on peut définir ses propres opérateurs :

> let (--) x y = abs (x - y);;
val ( -- ) : int -> int -> int
 
> 3 -- 5;;
val it : int = 2

Les règles pour définir ses opérateurs sont un peu compliquées (concernant les noms valides, les priorités, la notion de préfixe/infixe), mais voici quelques exemples plus ou moins utiles :

> let (@-@) x y = x + " " + y;;
val ( @-@ ) : string -> string -> string
 
> "hello" @-@ "world";;
val it : string = "hello world"
 
> let (+) = 42 in 4 - (+);;
val it : int = -38