Lately I have been playing around with F#, and I must admit I like the language a lot ! I think it is very expressive and very intuitive to use.
I personally think that the best way to learn a new language is to find a project that you are familiar with (you have implemented in the language you are familiar with) and then try to convert it to the new language.
So I decided to try and convert the RX MVVM framework that I showed in the first blog on this series and convert it to F#.
The first interface IPropertySubject
is declared as :
type IPropertySubject<'t> =
inherit IObservable<'t>
abstract member Value : 't with get, set
Then the implementation looks like:
type PropertySubject<'t>() =
let _subject : Subject<'t> = new Subject<'t>()
let mutable _value : 't = Unchecked.defaultof<'t>
let setValue v =
_value <- v
_subject.OnNext(v)
member x.SetValues(obs : IObservable<'t>) =
obs.Subscribe(fun v -> setValue v)
member x.SetValue(value : 't) = setValue value
interface IPropertySubject<'t> with
member x.Value with get() = _value and set v = setValue v
member x.Subscribe observer =
_subject.Subscribe observer
The next interface is ICommandObserver
. the interface is declared as follows:
type ICommandObserver<'t> =
inherit ICommand
abstract member Execute : IObservable<'t> with get
abstract member CanExecute : IObserver<bool> with get
And the implementation :
type CommandObserver<'t>(value) as self =
let event = Event<_, _>()
let _executeSubject = new Subject<'t>()
let _canExecuteSubject = new Subject<bool>()
let mutable _canExecute = value
let disp = _canExecuteSubject.DistinctUntilChanged().Subscribe(fun v -> _canExecute <- v
event.Trigger(self, EventArgs.Empty))
new() = new CommandObserver<'t>(true)
member x.SubscribeOnValues (values : IObservable<bool>) =
values.Subscribe(_canExecuteSubject)
interface ICommandObserver<'t> with
member x.Execute with get() = _executeSubject.AsObservable()
member x.CanExecute with get() = _canExecuteSubject.AsObserver()
interface ICommand with
member x.add_CanExecuteChanged(e) = event.Publish.AddHandler(e)
member x.remove_CanExecuteChanged(e) = event.Publish.RemoveHandler(e)
member x.Execute parameter =
match parameter with | :? 't -> _executeSubject.OnNext(parameter :?> 't)
| _ -> _executeSubject.OnNext(Unchecked.defaultof<'t>)
member x.CanExecute parameter =
_canExecute
The next step is to implement ViewModelBase
. The implementation in F# looks like:
type ViewModelBase() =
let event = Event<_, _>()
member x.OnPropertyChanged(propName : string) =
event.Trigger(x, new PropertyChangedEventArgs(propName))
interface INotifyPropertyChanged with
member x.add_PropertyChanged(e) = event.Publish.AddHandler(e)
member x.remove_PropertyChanged(e) = event.Publish.RemoveHandler(e)
One we have ViewModelBase
, then it is time to declare IPropertyProvider
interface. The declaration for the interface looks like:
type IPropertyProvider<'viewmodel> =
inherit IDisposable
abstract member CreateProperty<'ttype> : Expression<System.Func<'viewmodel,'ttype>> -> IPropertySubject<'ttype>
abstract member CreateProperty<'ttype> : Expression<System.Func<'viewmodel,'ttype>> * 'ttype -> IPropertySubject<'ttype>
abstract member CreateProperty<'ttype> : Expression<System.Func<'viewmodel,'ttype>> * IObservable<'ttype> -> IPropertySubject<'ttype>
abstract member CreateCommand<'ttype> : Expression<System.Func<'viewmodel, ICommand>> -> ICommandObserver<'ttype>
abstract member CreateCommand<'ttype> : Expression<System.Func<'viewmodel, ICommand>> * bool -> ICommandObserver<'ttype>
abstract member CreateCommand<'ttype> : Expression<System.Func<'viewmodel, ICommand>> * IObservable<bool> -> ICommandObserver<'ttype>
And the implementation is :
type PropertyProvider<'viewmodel>(viewModelBase : ViewModelBase, schedulers : ISchedulers) =
let _viewModelBase = viewModelBase
let _disposables = new CompositeDisposable()
let getProperty (expr : Expression<System.Func<'viewmodel,'ttype>>) =
let propName = (expr.Body :?> MemberExpression).Member.Name
let propSubject = new PropertySubject<'t>()
propSubject.SubscribeOn(schedulers.Dispatcher).Subscribe(fun _ -> _viewModelBase.OnPropertyChanged(propName))
|> _disposables.Add
propSubject
interface IPropertyProvider<'viewmodel> with
member x.CreateProperty<'ttype> expr =
let propSubject = getProperty expr
propSubject :> IPropertySubject<'ttype>
member x.CreateProperty<'ttype> (expr : Expression<System.Func<'viewmodel,'ttype>>, value : IObservable<'ttype>) =
let propSubject = getProperty expr
propSubject.SetValues(value) |> _disposables.Add
propSubject :> IPropertySubject<'ttype>
member x.CreateProperty<'ttype> (expr : Expression<System.Func<'viewmodel,'ttype>>, value : 'ttype) =
let propSubject = getProperty expr
propSubject.SetValue(value)
propSubject :> IPropertySubject<'ttype>
member x.CreateCommand<'ttype> expr =
new CommandObserver<'ttype>(true) :> ICommandObserver<'ttype>
member x.CreateCommand<'ttype>(expr : Expression<System.Func<'viewmodel, ICommand>>, value : bool) =
new CommandObserver<'ttype>(value) :> ICommandObserver<'ttype>
member x.CreateCommand<'ttype>(expr : Expression<System.Func<'viewmodel, ICommand>>, values : IObservable<bool>) =
let cmdObserver = new CommandObserver<'ttype>()
cmdObserver.SubscribeOnValues(values) |> _disposables.Add
cmdObserver :> ICommandObserver<'ttype>
interface IDisposable with
member x.Dispose() =
_disposables.Dispose()
The next step is to declare a factory that will create property provider. The interface for the factory looks like :
type IPropertyProviderFactory =
abstract member Create<'t> : ViewModelBase -> IPropertyProvider<'t>
The implementation of the interface looks like :
type PropertyProviderFactory(schedulers : ISchedulers) =
interface IPropertyProviderFactory with
member x.Create<'t> (vm : ViewModelBase) =
new PropertyProvider<'t>(vm, schedulers) :> IPropertyProvider<'t>
Next we need to implement message bus. The interface for the message bus is as follows :
type IMessageBus =
abstract member Subscribe<'t> : System.Action<'t> -> IDisposable
abstract member Publish<'t> : 't -> unit
And the implementation is :
type MessageBus() =
let _messageBus = new Subject<Object>()
interface IMessageBus with
member x.Subscribe<'t> onNext =
_messageBus.OfType<'t>().Subscribe(onNext)
member x.Publish<'t>(value : 't) =
_messageBus.OnNext(value :> Object)
And lastly we will declare and implement schedulers. The interface for the schedulers looks like :
type ISchedulers =
abstract member Dispatcher : IScheduler with get
abstract member NewThread : IScheduler with get
abstract member NewTask : IScheduler with get
abstract member ThreadPool : IScheduler with get
abstract member Timer : IScheduler with get
And the implementation is as follows:
type Schedulers(guiSynchronizationContext : IGuiSynchronizationContext) =
let _dispatcher = new SynchronizationContextScheduler(guiSynchronizationContext.SynchronizationContext)
interface ISchedulers with
member x.Dispatcher with get() = _dispatcher :> IScheduler
member x.NewThread with get() = Scheduler.NewThread :> IScheduler
member x.NewTask with get() = Scheduler.TaskPool :> IScheduler
member x.ThreadPool with get() = Scheduler.ThreadPool :> IScheduler
member x.Timer with get() = Scheduler.Immediate :> IScheduler
As you can see from the code above, F# code is a lot more compact and has a lot less boilerplate and ceremony.
I hope you find it interesting and helpful.