Is there a way to inject support for the F# Option type into ServiceStack? - f#

Updated below...
I recently started experimenting with ServiceStack in F#, so naturally I started with porting the Hello World sample:
open ServiceStack.ServiceHost
open ServiceStack.ServiceInterface
open ServiceStack.WebHost.Endpoints
[<CLIMutable; Route("/hello"); Route("/hello/{Name}")>]
type Hello = { Name : string }
[<CLIMutable>]
type HelloResponse = { Result : string }
type HelloService() =
inherit Service()
member x.Any(req:Hello) =
box { Result = sprintf "Hello, %s!" req.Name }
type HelloAppHost() =
inherit AppHostBase("Hello Web Services", typeof<HelloService>.Assembly)
override x.Configure container = ()
type Global() =
inherit System.Web.HttpApplication()
member x.Application_Start() =
let appHost = new HelloAppHost()
appHost.Init()
That works great. It's very concise, easy to work with, I love it. However, I noticed that the routes defined in the sample allow for the Name parameter to not be included. Of course, Hello, ! looks kind of lame as output. I could use String.IsNullOrEmpty, but it is idiomatic in F# to be explicit about things that are optional by using the Option type. So I modified my Hello type accordingly to see what would happen:
[<CLIMutable; Route("/hello"); Route("/hello/{Name}")>]
type Hello = { Name : string option }
As soon as I did this, the F# type system forced me to deal with the fact that Name might not have a value, so I changed HelloService to this to get everything to compile:
type HelloService() =
inherit Service()
member x.Any(req:Hello) =
box { Result =
match req.Name with
| Some name -> sprintf "Hello, %s!" name
| None -> "Hello!" }
This compiles, and runs perfectly when I don't supply a Name parameter. However, when I do supply a name...
KeyValueDataContractDeserializer: Error converting to type: Type
definitions should start with a '{', expecting serialized type
'FSharpOption`1', got string starting with: World
This wasn't a complete surprise of course, but it brings me to my question:
It would be trivial for me to write a function that can wrap an instance of type T into an instance of type FSharpOption<T>. Are there any hooks in ServiceStack that would let me provide such a function for use during deserialization? I looked, but I couldn't find any, and I'm hoping I was just looking in the wrong place.
This is more important for F# use than it might seem at first, because classes defined in F# are by default not allowed to be null. So the only (satisfying, non-hacky) way of having one class as an optional property of another class is with, you guessed it, the Option type.
Update:
I was able to sort-of get this working by making the following changes:
In the ServiceStack source, I made this type public:
ServiceStack.Text.Common.ParseFactoryDelegate
...and I also made this field public:
ServiceStack.Text.Jsv.JsvReader.ParseFnCache
With those two things public, I was able to write this code in F# to modify the ParseFnCache dictionary. I had to run this code prior to creating an instance of my AppHost - it didn't work if I ran it inside the AppHost's Configure method.
JsvReader.ParseFnCache.[typeof<Option<string>>] <-
ParseFactoryDelegate(fun () ->
ParseStringDelegate(fun s -> (if String.IsNullOrEmpty s then None else Some s) |> box))
This works for my original test case, but aside from the fact that I had to make brittle changes to the internals of ServiceStack, it sucks because I have to do it once for each type I want to be able to wrap in an Option<T>.
What would be better is if I could do this in a generic way. In C# terms, it would be awesome if I could provide to ServiceStack a Func<T, Option<T>> and ServiceStack would, when deserializing a property whose generic type definition matches that of the return type of my function, deserialize T and then pass the result into my function.
Something like that would be amazingly convenient, but I could live with the once-per-wrapped-type approach if it were actually part of ServiceStack and not my ugly hack that probably breaks something somewhere else.

So there are a couple of extensibility points in ServiceStack, on the framework level you can add your own Custom Request Binder this allows you to provide your own model binder that's used, e.g:
base.RequestBinders.Add(typeof(Hello), httpReq => {
var requestDto = ...;
return requestDto;
});
But then you would need to handle the model binding for the different Content-Types yourself, see CreateContentTypeRequest for how ServiceStack does it.
Then there are hooks at the JSON Serializer level, e.g:
JsConfig<Hello>.OnDeserializedFn = dto => newDto;
This lets you modify the instance of the type returned, but it still needs to be the same type but it looks like the F# option modifier changes the structural definition of the type?
But I'm open to adding any hooks that would make ServiceStack more palatable for F#.
What does the code look like to generically convert a normal Hello type to an F# Hello type with option?

The only thing I can think of is to replace the option type with your own type, one that has an implicit conversion from string to myOption, and anything else you need.
Not all that nice, but workable. Your type would probably also need to be serializable.
type myOption =
| None
| Some of string
static member public op_Implicit (s:string) = if s <> null then Some s else None
member public this.Value = match this with
| Some s -> s
| _ -> null
member this.Opt = match this with
| Some s -> Option.Some s
| None -> Option.None
Your record type would then be
[<CLIMutable>]
type Hello =
{ Name : myOption }
On the other hand, ServiceStack is open source, so maybe something could be done there.

Related

Entity Framework Core : Apply Conversion to all properties of type Option<T> in F#

I'm using Entity Framework Core 2.1 in F#.
I want to setup a generic type conversion for Option types so that EF knows how to handle them.
I found this very helpful post that shows how to setup a generic convertor for Option types.
So here's what I have so far (putting snippits together)
[<Table("Users", Schema = "pm")>]
type [<CLIMutable>] User = {
[<DatabaseGenerated(DatabaseGeneratedOption.Identity)>]
UserID : int64
FirstName : string
LastName : string
LastLoggedInTime : DateTimeOffset option
}
with
static member Configure (mb : ModelBuilder) =
mb.Entity<User>()
.Property(fun p -> p.LastLoggedInTime)
.HasConversion(OptionConverter<DateTimeOffset>()) |> ignore
This is great, except if I add a new property to my type. I will need to add the last three lines of the above code for that property as well. I'm looking for a way to do it for all properties all at once :D
So then I found this helpful SO answer that lead me to the below code
let tt = mb.Model.GetEntityTypes()
|> Seq.map(fun et -> et.GetProperties()) |> Seq.concat
|> Seq.filter(fun p -> p.ClrType = typeof<Option<DateTimeOffset>>)
|> Seq.map(fun p -> mb.Entity(p.DeclaringEntityType.ClrType)
.Property(p.Name)
.HasConversion(OptionConverter<DateTimeOffset>()))
Now the problem that I'm trying to solve is to figure out how to make this code work with Generic types so I don't have to specify the type. I want it to be something like p.ClrType = typeof<Option<T'>> and at the end, something like .HasConversion(OptionConverter<T'>())
Any thoughts?
so going off your initial question "I want to setup a generic type conversion for Option types so that EF knows how to handle them." I am assuming you mean you want EF to properly map an option to a "NULL" column of the corresponding base type, and that you are having problems because EF doesn't know how to handle FSharp.Option?
You could instead just use Nullable in place of Option for your EF types, and it will resolve as it does in C#.
So for instance, your example type becomes:
type [<CLIMutable>] User = {
[<DatabaseGenerated(DatabaseGeneratedOption.Identity)>]
UserID : int64
FirstName : string
LastName : string
LastLoggedInTime : Nullable<DateTimeOffset>
}
For types that can not be Nullable but are Options in your domain (strings for example), you just set them as their base type and it will work just fine.
If you are mapping to F# domain types somewhere else, you can just use Option.ToNullable and Option.OfNullable there, or map None to null if the type cannot be nullable as stated above.
Hope this helps!

NancyFx F# App with parameter

I am trying to get this example translated from C# to F#
public class MyModule : NancyModule
{
private IMyDependency _dependency;
public MyModule(IMyDependency dependency)
{
_dependency = dependency;
Get["/"] = x =>
{
};
// Register other routes
}
}
(source 1)
However adding a parameter to constructor
type HelloModule(dependency) as self =
inherit NancyModule()
do
self.Get.["/"] <- fun _ -> "Hello" :> obj
(source 2)
results in a run-time exception: System.InvalidOperationException: 'Something went wrong when trying to satisfy one of the dependencies during composition ...
How can I correctly add a dependency like a data-source to the code? Or, generally, how do I pass something from outside of HelloModule to the inside?
I'm guessing this might be caused by not specifying the type of the dependency parameter of the constructor in your F# code. This would result in the F# compiler assigning that parameter a generic type, and then Nancy's dependency injection framework doesn't know what to inject.
Try the following and see if it fixes your problem:
type HelloModule(dependency : IMyDependency) as self =
inherit NancyModule()
do
self.Get.["/"] <- fun _ -> "Hello" :> obj
P.S. Naturally, for this to work, you'll also need to have some type that implements the IMyDependency interface, and have told the Nancy framework about that type. From this part of the Nancy documentation that you linked to, it looks like merely declaring the type is enough, but if that's not actually enough then you'll have to register the type manually. I'm not familiar enough with Nancy to give you specific advice there beyond what the documentation says.

Topshelf, Owin selfhost, F# and explicit fields

I've installed the package Topshelf.FSharp, and there is an example of how to use it here:
https://gist.github.com/haf/4252121
Part of the example defines an "Svc" (service) class like this:
type Svc() =
member x.Start() =
printfn "Started"
member x.Stop() =
printfn "Stopped"
With Owin selfhost you call one of the various static overloads of IDisposable WebApp.Start(...) to start a web server, and then dispose it to stop it. In C#, if you want to combine Topshelf and Owin, you can store the IDisposable result of Start() in a private local field of the Svc class when Start() is called, and then call Dispose() on it in the Stop() method.
In F# you can declare an unitialized field of type IDisposable using "explicit fields" (http://msdn.microsoft.com/en-us/library/dd469494.aspx), but this seems somewhat awkward, is there a better way?
You do not have to use an explicit field to produce an "uninitialized" IDisposable value. For example, to define a restartable service, you can use an implicit webApp field like this:
// Example 1: Using an implicit mutable IDisposable field.
type Svc() =
let mutable webApp = null
member __.Start() =
if webApp = null then webApp <- WebApp.Start<Startup> "http://localhost:12345"
member __.Stop() =
if webApp <> null then webApp.Dispose(); webApp <- null
// Example 2: Using an implicit mutable IDisposable option field.
type Svc'() =
let mutable webApp = None
member __.Start() =
match webApp with
| Some _ -> ()
| None -> webApp <- Some(WebApp.Start<Startup> "http://localhost:12345")
member __.Stop() =
match webApp with
| Some webAppValue -> webAppValue.Dispose(); webApp <- None
| None -> ()
If the service does not have to be restartable, I would use an (immutable) implicit lazy field instead, as #Tom suggested.
Could you make the member a Lazy<T> instead and have it automatically initialise when it is first accessed? I am not quite sure of the requirements of F# in this case i.e. why exactly this is a problem, but for deferred initialisation, Lazy would seem to be a good choice.

F# mutable with no initial value for MEF

I have a bunch of modules that export an IModule interface. So in the main program I have no problems
...
let mutable modules = Seq.empty
[<ImportMany>]
member x.Modules
with get():IEnumerable<Lazy<IModule, IModuleData>> = modules
and set(a) = modules <- a
...
But now I need to expose an interface back to those modules. So each module will import a single interface
...
let mutable parent:IParent = ?
[<Import>]
member x.Parent
with get():IParent = parent
and set(a) = parent <- a
...
So my problem is how do I go about creating my mutable "parent" when I have no initial value for it? Also, is this the appropriate way to expose an API back to component parts?
Using Unchecked.defaultof<_> should do the trick, but it means that you're circumventing the F# type system, which may be a dangerous thing to do - the system tries to prevent you from accidentally dereferencing null values (and getting NullReferenceException).
Types that are declared in F# don't have null as a proper value, which is an attempt to eliminate the usual errors caused by null. The clean F# approach is to use option types to represent the fact that a value is missing:
let mutable parent:option<IParent> = None
[<Import>]
member x.Parent
with get():IParent =
match parent with
| Some p -> p
| None -> failwith "TODO: Throw some reasonable exception here!"
and set(a) = parent <- Some(a)
If you just want to say that IParent can have a null value (perhaps because you need to use it in some C# code that will ignore the F# restriction anyway), then you can mark the type definition using a special attribute that allows using null with the type.
[<AllowNullLiteral>]
type IParent =
abstract DoStuff : unit -> unit
Then you can write let mutable parent:IParent = null. The benefit of this approach is that you can also easily check whether a value is null (using just if parent <> null then ...) which is not that obvious when you use Unchecked.defaultof<_>.
let mutable parent = Unchecked.defaultof<IParent>
should do the trick.
Following up on what Tomas explained, you should probably put your imports directly into your constructor. That will allow your code to be a bit more idiomatic.

why is the implementation of my abstract member not public

I've been struggling to get this to compile for about an hour. It must be something stupid. Can you spot it?
in my lib project:
namespace TravelerStuff
open System
type Traveler =
abstract GetData : unit -> unit
type public DeltaTraveler() =
interface Traveler with
member v.GetData () =
printf "hello"
and in my console test app:
[<EntryPoint>] let main _ =
let traveler = new TravelerStuff.DeltaTraveler()
traveler.GetData // this line won't compile: (The field, constructor or member 'GetData' is not defined)
As gradbot says, F# doesn't currently implicitly convert values to interfaces when searching for members. Also, F# only uses explicit interface implementation (as known from C#) and not implicit implementation where members are not only compiled as implementation of an interface, but also as ordinary (directly visible) members of the type.
Aside from casting, you can duplicate the member in the type definition:
type DeltaTraveler() =
member v.GetData () = printf "hello"
interface Traveler with
member v.GetData () = v.GetData()
Also, if you just want to implement an interface, but don't need to add any members, you can use F# object expressions (which are more lightweight):
let deltaTraveler() =
{ new Traveler with
member v.GetData () = printf "hello" }
// The function directly returns value of type 'Traveler'
let t = deltaTraveler()
t.GetData()
You need to upcast. F# currently won't do it for you in this situation.
(traveler :> TravelerStuff.Traveler).GetData()
// open the namespace to reduce typing.
open TravelerStuff
(traveler :> Traveler).GetData()
Snip from F# docs.
In many object-oriented languages,
upcasting is implicit; in F#, the
rules are slightly different.
Upcasting is applied automatically
when you pass arguments to methods on
an object type. However, for let-bound
functions in a module, upcasting is
not automatic, unless the parameter
type is declared as a flexible type.
For more information, see Flexible Types (F#).

Resources