As I understand Caliburn.Micro does a considerable amount of auto-wiring and plumbing for a WPF project in a convention-based manner; employing MVVM.
Question: Are there any C# specific parts in Caliburn.Micro; which used some C#-only features? What parts of Caliburn.Micro can not be used with F# code? What do I lose by using F#?
Strings in Python are usually enclosed within double quotes ( "" ) or single quotes ( '' ). To create f-strings, you only need to add an f or an F before the opening quotes of your string. For example, "This" is a string whereas f"This" is an f-String.
The f-string was introduced(PEP 498). In short, it is a way to format your string that is more readable and fast. Example: The f or F in front of strings tells Python to look at the values inside {} and substitute them with the values of the variables if exist.
The most common one is newline \n , but there are others. Many of them became less and less useful over time, such as \f . This is called form feed, is used to indicate to a printer that it should start a new page.
In Python source code, an f-string is a literal string, prefixed with 'f', which contains expressions inside braces. The expressions are replaced with their values. The key point here is that an f-string is really an expression evaluated at run time, not a constant value.
Unfortunately, Caliburn.Micro and F# don't work that well together.
For example, Caliburn.Micro treats views as regular classes during convention matching (via reflection), which is true for C# - views are partial classes.
F# has no support for partial classes and views are XAML files only. That means they can't be resolved by Caliburn.Micro. It also means you will have problem wiring up IoC container in the bootstrapper, because it won't be able to create views - at least not without manual registration with Application.LoadComponent
and such.
Setting up the boostrapper was also a pain, because you have to specify the key in App.xaml
, but for whatever reason that key was not matched with the class. Bootstrapper<T>
also passes bool useApplication = true
to BootstrapperBase
as a default - and F# had different problems depending whether that flag was set or not, unfortunately I don't remember the details. That's because in F# you wire up the entry point to the application yourself, while Caliburn.Micro is built to intercept that step automatically. That causes conflicts...
If you get it to work by overriding convention resolution mechanism completely and by bending any IoC container to your will, more power to you. In my opinion, currently there's just too much friction.
Personally, I'd set up the core foundation for the application in C# using Caliburn.Micro and do the 'real work' in F#. Alternatively, I think you can even have view models written in F# in separate project, views in C# project, then you'd just have to adjust how the convention lookup works in the bootstrapper. I'm not exactly sure about that approach though so your mileage may vary and proceed with caution.
That's a nice question. If you go through with it, be sure to let us know how it goes. I was looking at Caliburn for a side project, but I didn't make much progress on it.
So to kick off, something you might have already stumbled upon:
The basic method for raising PropertyChanged events has two overrides. One that takes property name as a string, and another that takes a lambda expression. The latter uses a clever trick/ugly hack for extracting a property name from the expression's body. This gives you a nice, succinct syntax in C# that's to some extent checked by the compiler:
public string SomeProp
{
get { return someField; }
set
{
someField = value;
NotifyOfPropertyChange(() => SomeProp);
}
}
But this doesn't translate well to F#. Your choices are either to fallback to the string one, or sugar it by using quotations. I've used something like this:
let notify<'a> (notifier: PropertyChangedBase) (expr: Expr<'a>) =
let name =
match expr with
| PropertyGet (_, pi, _) -> pi.Name
| _ -> failwith "Can't get property name to notify"
notifier.NotifyOfPropertyChange(name)
Which was called like this:
member this.SomeProp
with get () =
someField
and set(value) =
someField <- value
notify this <@ this.SomeProp @>
I believe you could take it a step further and roll it into your own PropertyChanged class built on top of PropertyChangedBase.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With