Hi, pattern matching is a good start, but it can be used only to a part of the problem - you can use dynamic type test if you know exactly the type of the value you are testing (e.g. string or function from int to unit). First we will have to use F# 'box' primitive to turn the value to a value of type 'obj' which can contain any F# value (so you can use it to store functions as well as strings):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 

#light
open Microsoft.FSharp.Reflection

// function: int -> unit
let x1 = box (fun n -> printf "%d" (n + 1))
// string
let x2 = box ("hello world")

let test (x:obj) =
  match x with
  | :? (int -> unit) as f -> f(41)
  | :? string as s -> printf "%s" s
  | _ -> failwith "unknown"
  
test x1  
test x2

However when you want to test for a function that accepts int and returns anything, it becomes a bit difficult - you can't write something like ":? (int -> 'a)" because all type arguments have to be instantiated at compile time, but here you would need to assign the concrete type to 'a at runtime.

To check for the type at runtime you will have to use F# reflection library which allows you to determine dynamically what is the type of value (if it is a function, F# record, etc..) and then you can test if it is a function and if it's argument is an integer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 

#light
open Microsoft.FSharp.Reflection

// function: int -> int
let x1 = box (fun n -> n+2)
// function: int -> string
let x2 = box (fun (n:int) -> n.ToString())

// Test dynamically type of the value 
let (|AnyIntFunc|_|) (x:obj) =
  match Type.GetInfo(x.GetType()) with
  | FunctionType(t1,t2) when t1 = (typeof<int>) -> // It is a function and it's argument is 'int'
      Some(t2) // return the second type so we can print it
  | _ -> None  // it is not a function or doesn't take int as an argument
  
let test (x:obj) =
  match x with
  | AnyIntFunc(res) -> printf "function from int to '%s'" res.Name
  | _ -> failwith "unknown"
  
test x1  
test x2  
System.Console.ReadLine()

In this example 'AnyIntFunc' is an 'active pattern' which allows you to write your own rules for pattern matching, and it is used in the match block in the 'test' function.

Anyway - can I ask why do you want to do this? Using dynamic tests like this in F# is very powerful technique, but I think it has to be used very carefully because you lose some very useful F# checks. I think good use cases are serialization of the data and similar...

By on 7/10/2007 5:53 AM ()

It's just a contrived problem designed to help me understand what F# knows about types. :)

It grew out of my trying to create a function that recursively prints the contents of a sequence, list, or any other enumerable type. Given an element in the input, I have to be able to tell whether the element is an enumerable type or an "atom" that can be printed.

(It was maddeningly frustrating to see that a sequence's default printer only prints the first 4 elements in a sequence.)

--
Jeff S.

By on 7/10/2007 6:07 AM ()

Here is a solution:

1
2
3
4
5
6
7
8
open Reflection
let g x =
  match Type.GetInfo ((box x).GetType()) with
   | TypeInfo.FunctionType(t, _) when t = (type int) -> printf "int fun"
   | TypeInfo.FunctionType _ -> printf "other fun"
   | _ -> match box x with
           | :? string as s -> printf "string %s" s
           | _ -> printf "other";;

--
Laurent.

By on 7/10/2007 5:50 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper