// This program illustrates the power of pattern matching versus dynamic // dispatch. open System;; // a number can be an integer, float or fraction type number = I of int | F of float | R of int*int;; let half = R(1,2);; let quarter = R(1,4);; let one = I(1);; let pi = F(3.1416);; let rec gcd = function | (0,b) -> b | (a,b) -> gcd(b%a,a);; // function to pretty print numbers: let tostring = function | I(x) -> string(x) | F(x) -> string(x) | R(x,1) -> string(x) | R(x,y) ->string(x)+"/"+string(y);; Console.WriteLine(tostring(R(3,1)));; // function to add two numbers, without losing precision let rec add = function | (I(x),I(y)) -> I(x+y) | (F(x),F(y)) -> F(x+y) | (R(a,b),R(c,d)) -> let n,d = a*d + c*b, b*d let cd = gcd(n,d) R(n/cd,d/cd) | (I(x),R(a,b)) -> add(R(x,1),R(a,b)) | (I(x),F(y)) -> F(float(x)+y) | (R(a,b),F(x)) -> let c = float(a)/float(b) in F(c+x) | (x,y) -> add(y,x);; Console.WriteLine( tostring(add(quarter,one)) );; (* What are the advantages compared to dynamic dispatch, visitor pattern? 1. Matches pattern of arguments (no more n.) 2. No need to type cast: we do not specify type of variables as 'number' because it will always do pattern matching on each number type. That is, you will need to specify STATICALLY what to do for each type of number. 3. Therefore, F# is statically type safe. *)