From 6c6cfeaf15631d475a605e3020b45e28eb85d178 Mon Sep 17 00:00:00 2001 From: "Wyatt J. Miller" Date: Sat, 2 Jul 2022 18:02:36 -0400 Subject: [PATCH] added blog post --- .../2022-07-02-fsharp-avalonia.markdown | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/blog/_posts/2022-07-02-fsharp-avalonia.markdown diff --git a/src/blog/_posts/2022-07-02-fsharp-avalonia.markdown b/src/blog/_posts/2022-07-02-fsharp-avalonia.markdown new file mode 100644 index 0000000..0a3c92b --- /dev/null +++ b/src/blog/_posts/2022-07-02-fsharp-avalonia.markdown @@ -0,0 +1,43 @@ +# My journey with Avalonia and F# + +So, I finally did it. + +Back when I was in college (or university, whatever floats your boat), I made a Windows application called DeskHubSharp. What this application did was it grabbed user data from GitHub, including repositories from the user, and displayed the data. You were allowed to view detail about the user, view detail about a selected repository, download a branch from the repository, and view the repository's GitHub page. However, this application has been largely unmaintained. + +There were some drawbacks to this application. For starters, it only worked on Windows (yes, I know, shocker). This happened because the underlying framework was Windows Presentation Foundation (or better known as WPF). Just running on Windows wasn't enough for me. While I was developing the application, I had to use an application called Parsec to develop and run the program on Windows. Even though I still use Parsec to this day, developing and running the application was kind of tedious. Several issues popped up as well, like the repository sorting functionality was broken. I'd go and sort from least to most stars for all of the repositories, and it would sort correctly, but viewing the detail on a particular repository would pick the repository four indexes behind the repository I'm actually trying to view. This happens with every sorting option in my app. + +I had stated in the past I would proceed with some next steps. One next step was build the application in [Avalonia](http://avaloniaui.net/), a user interface toolkit that was similar to WPF, but vastly different. While WPF used a Windows-only drawing library to draw windows, buttons, labels, and lists, Avalonia used a drawing library called Skia (SkiaSharp to be exact). This means that Avalonia can not only draw on Windows platforms, Avalonia can draw on macOS and Linux platforms. As an added bonus, Avalonia can also draw on iOS, Android, and even WebAssembly. Another next step was trying interoperability with the application. Enter F#. + +Why F#? Well, it's part of the .NET framework, so I don't have to learn a whole different standard library/framework. In my Android class, I had to learn not only the Android libraries, but I also had to learn the Kotlin standard library as well (and a part of Java's standard library as well because some of Android's libraries are tied to it). For example, you call an `IEnumerable` or an `ObservableCollection` in F# just like you would in C#, which is really handy, especially when interoperability is concerned. Additionally, another next step was to mess with F#. I have never messed with F# before and I still kind of like C# still (sorry Dave) so I thought it would be nice to give F# a fighting chance (because you know a language nut). The last step was to port it over to .NET Core (which then it is now just .NET, not Core or Framework. Just .NET) to help with compatibility. + +Let's start with the easy stuff. Porting to .NET 6 was a piece of cake. A simple copy and paste from the old application to the new application did the trick. Next, came the ripping and tearing of WPF and putting Avalonia in it's place. This quite straightforward to do as well. I created new Avalonia windows and then copy-pasted the content from the WPF windows. I did the same thing when it came to the code-behind. Intellisense in VS Code was smart enough to realize I was missing some Avalonia classes so I added those in. However, that were the easy stuff ended. + +I would be wrong in saying that Avalonia is a drop-in replacement because it's not. I had to do some manual adjustments on my end. For example, to show a window, I couldn't do `this.ShowDialog()` like you would in WPF. You have to call the parent window inside the `ShowDialog` call: `this.ShowDialog(this)` or just call `this.Show()` instead. Another example is with the ListBoxes. You can't call the property `ItemsSource` like you would in WPF, you have to call the property `Items` instead. These changes, among others, are not too bad but it would be nice if everything about Avalonia was the same as WPF. + +More ripping and tearing coming your way because it's time for me to talk about F#, and what I experienced with the language and it's tooling. F# has quite the syntax and it's vastly different than C#'s syntax. So, I had to learn it for the models. In C#, the models are classes, but in F#, those classes are types, specified with the `type` keyword. Let me show you what I mean: + +```fsharp +type Email() = + let mutable _toEmail: string = "wjmiller2016@gmail.com" + let mutable _fromEmail: string = "wjmiller2016@gmail.com" + let mutable _password: string = "password" + + member this.ToEmail + with get() = _toEmail + and set(value: string) = _toEmail <- value + member this.FromEmail + with get() = _fromEmail + and set(value: string) = _fromEmail <- value + member this.Password + with get() = _password +``` + +The above example is a model for my feedback functionality inside the application. + +The `let` keywords are variables inside a type, kind of like `private` fields, which is what these variables serve as, or any kind of variable for that matter. The `mutable` keywords are for letting the variables change values. By default, F# has immutability, meaning that every variable won't change from underneath you unless explicitly defined with the `mutable` keyword. This concept is similar to Rust's immutability concept and it's `mut` keyword. The `member` keywords are for properties, and they can defined with getters and setters, like what's shown above. Don't worry, though, you don't have to add getters and setters. I had to write custom code for my backing fields. It took a while to find my ground when it came to what a property was, what a field was, and variable was. [This](https://stackoverflow.com/questions/24840948/when-should-i-use-let-member-val-and-member-this) was immensely helpful along my journey. Once I knew what was what, it wasn't too hard to figure out what I had to do, as far as storing the GitHub data is concerned. + +Let's talk tooling. F# is great and all but I could not find any serialization tool out on the Internet. I'm not talking about `Newtonsoft.Json`, that's a great tool, but I'm talking about JSON files and transforming them into objects/types for you. It's similar to this website [here](https://json2csharp.com/). I had to write every source file by hand. Don't try it. I'm so irritated by this that I might have to write a tool to do just that. There's some other tools I would like as well, like a C# to F# converter, but I'd imagine that's quite the ambition to take that on. Since C# and F# are both .NET languages, it shouldn't be that hard, right? Right? All-in-all, I think F# is an alright language. There's still so much I have to learn from it, though. Maybe writing the serialization tool will help... + +--- + +For source code on DeskHubSharp Revised, see [this](https://scm.wyattjmiller.com/wymiller/DeskHubSharpRevised/). \ No newline at end of file