Hello everyone, today I bring you some F# 5.0 preview niceties FSharp Conf was last Friday (June 5th, 2020) and there were many many awesome #fsharp talks 100% recommended, if you are into web development there are a couple of talks for you:
SAFE Stack – The Road Ahead - Isaac Abraham
From Zero to F# Hero - James Randal
That being said... today's content post is little different I'll talk about the nuget package references for F# scripts (fsx files), you can read more about the new F# 5.0 features here.
In the past if you wanted to script with F# you needed to have the library code downloaded and reference the dll file directly
#r"../libs/MyLib.dll"openMyLib/// do code with MyLib
F# 5.0 is introducing the nuget references now if you want to reference a library it's pretty simple for example
that will download the Nuwtonsoft.Json package (in a transparent way for the user) and add the referenced library, after that you will be able to open any namespaces that the library provides.
I feel that gives you a better experience when you want to create some scripts either for quick usage, prototyping or even API exploration as we will see.
Recently Microsoft released a new nuget package: Microsoft.Windows.Sdk.NET which exposes in a pretty nice way the native WinRT API's from Windows. The same API's you would use from UWP Apps the most modern stuff is in there
C#/WinRT provides Windows Runtime (WinRT) projection support for the C# language. A "projection" is an adapter that enables programming the WinRT APIs in a natural and familiar way for the target language. The C#/WinRT projection hides the details of interop between C# and WinRT interfaces, and provides mappings of many WinRT types to appropriate .NET equivalents, such as strings, URIs, common value types, and generic collections.
WinRT APIs are defined in *.winmd format, and C#/WinRT includes tooling that generates C# code for consumption scenarios, or generates a *.winmd for authoring scenarios. Generated C# source code can be compiled into interop assemblies, similar to how C++/WinRT generates headers for the C++ language projection. This means that neither the C# compiler nor the .NET Runtime require built-in knowledge of WinRT any longer.
Motivation
.NET Core is the focus for the .NET platform. It is an open-source, cross-platform runtime…
I've done some WinRT stuff in the past with UWP applications and I have always felt that the WinRT API is just so nice to work with and remembering that everything you do with it is 100% native.
What can you do with the WinRT projection?
Anything that does not require some sort of UI, if you want to use an API that requires a CoreWindow or that it executes on the UI thread then you would need to implement some interfaces for your application as noted here everything else that is UI-less is good to go, and as an example: In this repository I made a small Avalonia App that leverages the WinRT API. It includes examples for the following API's
A small showcase of some of the WinRT APIs that can be used thanks t the C#/WinRT projection https://github.com/microsoft/CsWinRT
WinRT + F#
In the last build event (May 2020) one project was shown C#/WinRT which is a projection of the WinRT API over C#, this projection is compatible with .netstandard2.0 and .net5 (once it's released). This is not the first time an attempt to expose the WinRT API to Win32 apps has been made, the last one was SDK Contracts and while you could use most of the WinRT APIs it had some limitations around certain APIs like Bluetooth and if you were an F#'er like me, you were in bad luck because the SDK Contracts didn't even allow your project to compile that stuff is now past and the next iteration (which I believe is a better take) is here.
The projection is also available for C++, Rust and Python.
Samples
Check the Core project where I tried to put most of the WinRT API code
And... after half day reading... Finally some code.
The first Example is a small gist that uses the Windows.Storage API to take 5 files from the the user's music library and check the its music properties.
the code is very short and is async heavy the reason for that is there are a lot of Async APIs in WinRT and here's Larry Osterman explaining why
Fortunately the System namespace includes a nice extension method that converts an IAsyncAction to a usual Task, then we use some F# Async functions to make it work seamlessly in F#.
Here we use the MusicLibrary IStorageFolder to get its files in a very simple way the IStorageFolder contains some nice properties and methods that we can leverage to either create/delete/update new files or directories
What about a terminal music player prototype?
As noted here...
you can actually write a PoC of a media player in less than 50 LoC with F#!
If you add some lines for the System Media Transport Controls (the ones that give you info when you turn up/down the volume, change or play/pause the song) and input management, you can actually write a better PoC in less than 100 LoC
To run this sample type dotnet fsi --langversion:preview media-player.fsx
(or whatever the name of the file is), also don't forget to download .net5 preview
Here we define the function asyncGetFiles inside we do a query to get only .mp3 files also the Windows.Storage.Search API contains some nice classes to create complex queries on files as well as give you a number of ways to present that information to you.
Like in our first example we get the files from the music library in an asynchronous way. Since this is a prototype we just take 5 but we could bring the entire in a single trip if we wanted to.
The next section is quite large so I'll add the explanation as comments as we go by
letasyncGetPlaylist=async{let!files=asyncGetFiles()/// create a new media playlist/// this will be the source for our player later onletplaylist=MediaPlaybackList()foriteminfilesdolet!thumbnail=item.GetThumbnailAsync(ThumbnailMode.MusicView).AsTask()|>Async.AwaitTasklet!musicProps=item.Properties.GetMusicPropertiesAsync().AsTask()|>Async.AwaitTask/// let's create a media source for each file we foundletsource=MediaSource.CreateFromStorageFileitem/// create the items from the sourcesletmediaPlaybackItem=MediaPlaybackItemsource/// this part is important only if you want a nice/// integration with the OS, but is not really necessaryletprops=mediaPlaybackItem.GetDisplayProperties()props.Type<-MediaPlaybackType.Musicprops.MusicProperties.Title<-musicProps.Titleprops.MusicProperties.AlbumArtist<-musicProps.AlbumArtistprops.MusicProperties.AlbumTitle<-musicProps.Albumprops.MusicProperties.Artist<-musicProps.Artistprops.MusicProperties.TrackNumber<-musicProps.TrackNumberprops.Thumbnail<-RandomAccessStreamReference.CreateFromStreamthumbnail/// once you have set the properties don't forget to apply them/// otherwise these will not show in the SMTC/// (System Media Transport Controls)mediaPlaybackItem.ApplyDisplayProperties(props)/// finally add the mediaplayback item to the playlistplaylist.Items.AddmediaPlaybackItemreturnplaylist}
Now, it may seem convoluted having to create a source for the item then a source for the playback item then adding it to the playlist but here's the cool thing they are abstractions that will allow you to control the playlist and the player in an easy way without worrying what is the actual source of the media you will play, because you can play physical Video, Music, and even Streams the source can be in your hard drive or can come from the internet the API is pretty flexible.
This part is pretty simple I believe, just get the playlist, assign it to the player, then start playing.
The rest it's just to be able to change songs and pause/play by reading keystrokes
lethmsg="Press q to quit, up arrow to play or pause and arrows to move previous or next"printfn"%s"hmsgletmutablekey:ConsoleKeyInfo=Console.ReadKey()letplaylist=player.Source:?>MediaPlaybackListwhilekey.Key<>ConsoleKey.Qdomatchkey.Keywith|ConsoleKey.RightArrow->playlist.MoveNext()|>ignore|ConsoleKey.LeftArrow->playlist.MovePrevious()|>ignore|ConsoleKey.UpArrow->matchplayer.CurrentStatewith|MediaPlayerState.Paused->player.Play()|MediaPlayerState.Playing->player.Pause()|_->()|ConsoleKey.H->printfn"\n%s"hmsg|_->()key<-Console.ReadKey()player.Dispose()exit(0)
when you have a player in memory you will be only able to Play/Pause if you want to stop completely you need to dispose the player player.Dispose() that will also release the SMTC integration you may have had added.
This is a prototype that if you feel confident enough you may even be able to just copy paste the functions into a console application with a few more tweaks and you have a terminal player MVP if you want to go crossp-platform you may want to use LibVLCSharp instead 😁
Closing thoughts
So... Yeah! using F# 5.0 scripts to prototype and explore API's is a blessing. Once F#5.0 is GA (Generaly Available) you could also set a few snippets of code to showcase how simple is to use F# to do complex things (like some of the stuff you can do with the WinRT API) if you want to get some of your Colleagues to adopt F#
If you have any doubts/comments please feel free to write them below or to ping me on twitter 😁 thanks for the time you spent here.