Like C#, F# is primarily a statically typed programming language. That said both languages provide support for dynamic typing.
Sometimes dynamic typing can be a pragmatic way of solving a specific problem. For example say you’re a solutions provider with a core product and have a number of clients with bespoke requirements. One client asks for a product rating feature. This can be relatively easily achieved using dynamic properties:
- a bunch of client specific properties are read from the database, including the rating value, which is then set as a dynamic property on the product object
- zero changes are required to the core code
- at the UI, on WPF, the dynamic property can simply be bound through XAML like any other object property
- on Silverlight 4,0 direct binding is not currently possible, however there is a simple workaround - specifying the dynamic property as a parameter for a value converter (example later)
C# and F# use slightly different approaches for dynamic properties:
- C# 4.0 provides a dynamic type, which tells the compiler that member lookup on the object should be deferred to run time
- F# employs a dynamic lookup operator, which when overloaded defines the behaviour at runtime. This means that in F# dynamic lookup is explicit (the ? operator), and can be mixed with static lookup on the same object (the . operator)
Is it is still possible to implement dynamic properties in F# that can be consumed by other .Net languages like C#, VB.Net, IronRuby or IronPython; plus WPF and Silverlight. The trick is to inherit from .Net 4.0’s System.Dynamic.DynamicObject type and implement the System.ComponentModel.INotifyPropertyChanged interface:
open System.Dynamic
/// Dynamic Lookup type
type DynamicLookup () =
inherit DynamicObject ()
/// Synchronization object
let sync = obj()
/// Property Changed event
let propertyChanged = Event<_,_>()
/// Properties
let mutable properties = Map.empty
/// Gets property value
member private this.GetValue name =
Map.tryFind name properties
/// Sets property value, creating a new property if none exists
member private this.SetValue (name,value) =
/// Atomically writes new properties reference
let Write () =
properties <-
properties
|> Map.remove name
|> Map.add name value
// Synchronize property writes
lock sync Write
// Trigger property changed event
(this,System.ComponentModel.PropertyChangedEventArgs(name))
|> propertyChanged.Trigger
override this.TryGetMember(binder:GetMemberBinder,result:obj byref ) =
match this.GetValue binder.Name with
| Some value -> result <- value; true
| None -> false
override this.TrySetMember(binder:SetMemberBinder, value:obj) =
this.SetValue(binder.Name,value)
true
override this.GetDynamicMemberNames() =
properties |> Seq.map (fun pair -> pair.Key)
[<CLIEvent>]
member this.PropertyChanged = propertyChanged.Publish
interface System.ComponentModel.INotifyPropertyChanged with
[<CLIEvent>]
member this.PropertyChanged = propertyChanged.Publish
static member (?) (lookup:#DynamicLookup,name:string) =
match lookup.GetValue name with
| Some(value) -> value
| None -> raise (new System.MemberAccessException())
static member (?<-) (lookup:#DynamicLookup,name:string,value:'v) =
lookup.SetValue (name,value)
static member GetValue (lookup:DynamicLookup,name) =
lookup.GetValue(name).Value
F# usage:
/// Product type inherits dynamic lookup
type Product (name,price) =
inherit DynamicLookup ()
member this.Name = name
member this.Price = price
// Initiate product object with dynamic rating value
let p = Product("F# for Scientists",49.95M)
do p?Stars <- 5
// Access product's properties
let stars = System.Convert.ToInt32(p?Stars)
do printf "%s...%M %s" p.Name p.Price (System.String('*',stars))
C# usage:
// Create Product type with dynamic stars value
Product product = new Product("Expert F#",54.99M);
((dynamic)product).Stars = 5;
// Read product properties
dynamic p = product;
string s =
string.Format("{0}...{1} {2}",
p.Name, p.Price, new String('*', (int) p.Stars));
Use from Silverlight 4.0
<t:PropertyLookup x:Key="DynamicConverter"/>
<TextBox Text="{Binding Converter={StaticResource DynamicConverter},
ConverterParameter=Stars}"/>
dynamic product = new Product("Real World FP", 35.99);
product.Stars = 5;
DataContext = product;
public class PropertyLookup : IValueConverter
{
public object Convert(
object value,
Type targetType,
object parameter,
CultureInfo culture)
{
return DynamicLookup.GetValue(
(DynamicLookup) value,
(string) parameter);
}
public object ConvertBack(object value,
Type targetType,
object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
Note: Silverlight 4.0 requires Microsoft.CSharp.dll to use dynamic types.