I'm currently rewriting a graduate student project that was written in c# to f#.
I'm stumped on how to handle IRepository interfaces in f#. It seems trivial in c#, but f# doesn't like what I've done.
The IRepository in question is defined in the myNameSpace.SolarSystem name space. Which I make sure to include in my f# project.
Here are my notes:
f# - invalid use of an interface type <--(let repo = IRepository<SolarSystem>())
open myNameSpace.SolarSystem
let searchCatalog = [| 8; 11; 31 |]
let repo = IRepository<SolarSystem>()
let ClassOfSolarSystems classOfStar =
repo.Query().Where(fun s -> s.SolarGroups.Any(fun c -> searchCatalog.Contains(classOfStar) ))
c# - no error:
using myNameSpace.SolarSystem
private readonly int[] searchCatalog = new int[] { 8, 11, 31 };
public IRepository<SolarSystem> Repo { get; set; }
public IEnumerable<SolarSystem> ClassOfSolarSystems(Int32 classOfStar)
{
return Repo.Query()
.Where(s => s.SolarGroups.Any(c => searchCatalog.Contains(classOfStar)));
}
I exhausted my googlefu and could not find any meaningful(to me at least) solutions.
Is there a way to use IRepository interfaces in f#?
Thanks!
Your F# line is equivalent to this C#, as the keyword new is implicit in F#.
var repo = new IRepository<SolarSystem>()
The C# compiler would not let you do that either. Also, your C# example is a property, while in F# it is a value binding. To define properties in F# you need to use the member keyword.
Edit
I played around with the editor a bit until I found something the compiler was mildly happy about, came up with this.
let ClassOfSolarSystems (repo : IRepository<SolarSystem>) classOfStar =
repo.Query()
|> Seq.filter(fun s -> s.SolarGroups.Any(fun c -> searchCatalog.Contains(classOfStar) ))
I didn't mess too much with your LINQ expressions but you should consider using the F# Seq module instead.
When you write
let repo = IRepository<SolarSystem>()
you try to create an instance of the interface, as you well know, it is impossible.
Just create a function that takes a repository and search parameters.
open SolarSystems
open System.Linq
type RepositoryFunctions =
member this.ClassOfSolarSystems (repo:IRepository<SolarSystem>) (classOfStar:int32) =
repo.Query().Where(fun s -> s.SolarGroups.Any(fun c -> searchCatalog.Contains(classOfStar) ))
So, if you not prefer to use LINQ in F#, you can write:
open SolarSystems
type RepositoryFunctions =
member this.ClassOfSolarSystems (repo:IRepository<SolarSystem>) (classOfStar:int32) =
let catalogContainsStar searchCatalog = searchCatalog.Contains(classOfStar)
let systemContainsStar solarSys = solarSys.SolarGroups.Any( catalogContainsStar )
let getAllWithStar = Seq.filter systemContainsStar
in
repo.Query() |> getAllWithStar
Related
A couple days ago, I posted a question about deserialization with enums in F#.
The question is here: Deserialization in F# vs. C#
The answer pointed to some code written by Isaac Abraham, at: https://gist.github.com/isaacabraham/ba679f285bfd15d2f53e
However I am facing another problem:
If the object to deserialize to has an object of type 'enum option', the deserialization will fail, whereas it'll work if the type is just 'enum'.
A minimal example:
type TestType =
| A = 0
| B = 1
type TestObjectA =
{
test : TestType
}
type TestObjectB =
{
test : TestType option
}
let x = "{\"test\":\"A\"}"
let TestA = Deserialize<TestObjectA> x // will work
let TestB = Deserialize<TestObjectB> x // will fail
and the large deserialization code is at: https://pastebin.com/95JZLa6j
I put the whole code in a fiddle: https://dotnetfiddle.net/0Vc0Rh
but it can't be run from there since the F# version they support will not accept the 'object' keyword.
So, my question is: why can't I use the option type on an enum, but it works on other types? As a side note, since I'm quite new to F#, I'm not fully understanding Isaac's code, although I spent some time going through it and trying to troubleshoot it.
My understanding is that this line:
|> Seq.map (fun (value, propertyInfo) -> Convert.ChangeType(value, propertyInfo.PropertyType))
will try to convert the type to the right enum, but not to the enum option.
As a bonus question, is there a working solution that does full idiomatic deserialization with enums? (without going through null types)
open System.IO
type TestType =
| A = 0
| B = 1
type TestObjectB =
{
test : TestType option
}
let jsonSerializeToString obj =
use writer = new StringWriter()
let ser = new Newtonsoft.Json.JsonSerializer()
ser.Formatting <- Newtonsoft.Json.Formatting.Indented
ser.Serialize(writer, obj)
writer.ToString()
let jsonDeserializeFromString str =
Newtonsoft.Json.JsonConvert.DeserializeObject<TestObjectB>(str)
let Test obj =
let str = jsonSerializeToString obj
let obj' = jsonDeserializeFromString str
obj'
[<EntryPoint>]
let main argv =
{ test = Some TestType.B } |> Test |> ignore
{ test = None } |> Test |> ignore
0
Note: if you need to serialize a large collection of objects, then stream them to a file instead of an in-memory string to avoid an OutOfMemoryException. Like use writer = File.CreateText(filePath).
As a bonus question, is there a working solution that does full
idiomatic deserialization with enums?
I use the Microsoft.FsharpLu.Json package in production and find it works quite well for serializing and deserializing between "plain" javascript and idiomatic F#. Note Microsoft.FsharpLu.Json relies on Newtonsoft.Json under the hood.
Below is an example with your types and your test string, using Expecto for tests.
namespace FsharpLuJsonTest
open Newtonsoft.Json
open Microsoft.FSharpLu.Json
open Expecto
open Expecto.Flip
// Setup for FSharpLu.Json
type JsonSettings =
static member settings =
let s = JsonSerializerSettings(
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore)
s.Converters.Add(CompactUnionJsonConverter())
s
static member formatting = Formatting.None
type JsonSerializer = With<JsonSettings>
// Your example
type TestType =
| A = 0
| B = 1
type TestObjectA = { test : TestType }
type TestObjectB = { test : TestType option }
module Tests =
let x = """{"test":"A"}"""
[<Tests>]
let tests =
testList "Deserialization Tests" [
testCase "To TestObjectA" <| fun _ ->
JsonSerializer.deserialize x
|> Expect.equal "" { TestObjectA.test = TestType.A }
testCase "To TestObjectB" <| fun _ ->
JsonSerializer.deserialize x
|> Expect.equal "" { TestObjectB.test = Some TestType.A }
]
module Main =
[<EntryPoint>]
let main args =
runTestsInAssembly defaultConfig args
As you can see FsharpLu.Json supports Discriminated Unions and option types out of the box in the way you prefer. FsharpLu.Json is a less flexible solution than some others like Chiron (which allow for much more customisation) but I tend to prefer the opinionated approach of FsharpLu.Json.
I haven't used it personally, but the new FSharp.SystemText.Json library with the JsonUnionEncoding.ExternalTag setting should work roughly the same way FsharpLu.Json does. That library uses Microsoft's new System.Text.Json library under the hood rather than Newtonsoft.Json.
I'm trying to createa a base test class where I can setup common methods.
type BaseTest() =
member self.Assert (functionToEvaluate:bool, ?errorMessage:string) =
let a = fun () -> defaultArg errorMessage ""
match errorMessage with
| None -> NUnit.Framework.Assert.That(functionToEvaluate)
| _ -> NUnit.Framework.Assert.That(functionToEvaluate, a )
[<TestFixture>]
type MyTest () =
inherit BaseTest()
[<Test>]
member self.``test something``() =
let x = 1
self.Assert_( (x = 2))
// or
self.Assert_( (x = 2), "x value is not 2")
How to make the code "clean" (let a ... is horrible for me)
How to avoid using this/self in the derived class?
How can I write it like self.Assert(x=1) or even better just Assert(x=1) instead of self.Assert((x=1)) ?
What I want tot do (and I can do with C#) is this:
// in base test class
protected void Assert(bool test) => NUnit.Framework.Assert.That(test);
protected void Assert(bool test, string errorMessage) => NUnit.Framework.Assert.That(test, errorMessage);
// in test class
public void TestSomething() {
var x = 1
Assert(x==2)
// or
Assert(x==2, "x is not 2")
}
Your problem is that you're trying to translate the C# program into F# verbatim and expect it to look "nice" without realizing that the initial C# program is already full of C#-specific tricks that exist in order to make it look "nice". One example is the base class. Why is there a base class? Does it represent something? No, it doesn't: it's there for the sole purpose of avoiding a class name when calling these functions - i.e. you write Assert instead of SomeHelper.Assert. This stems out of the fact that in C# you can't have free-standing functions.
But in F# - you can!
let assrt x = NUnit.Framework.Assert.That(x)
let assrtWith x msg = NUnit.Framework.Assert.That(x, msg)
[<TestFixture>]
type SaleRepositoryTest () =
[<Test>]
member self.``test something``() =
let x = 1
assrt (x=2)
assrtWith (x=2) "x is not 2"
(Note that you can't use the name assert, because it's a keyword)
Also note that you generally don't need classes. The fact that in C# you can't do anything without them is a giant mistake that grows out of Java design, which was a misunderstanding of OO.
You can have free-standing functions. If I remember correctly, NUnit should be able to discover such tests just fine (though I can't verify right now):
let [<Test>] ``test something``() =
let x = 1
assrt (x=2)
assrtWith (x=2) "x is not 2"
Finally, I strongly recommend that you consider FsUnit. It can bind to NUnit (if you're locked into that) and provides a nice library of F#-idiomatic assertions.
I have three WsdlService type providers each pointing to a different version of the same WCF service, and the underlying MyService.ServiceTypes.Ticket are mostly the same. I am using the WsdlService provider so that the only code we have in source is the objects we use from the service. This project has started to replace a slice of an old project where everything generated by SvcUtil.exe is checked in to source, even if it is unused (yuck).
I have a hunch that code quotations or higher kinded types might work because the functions I want to condense are so similar. My use case does not appear similar to examples I have seen using F# quotations, and I do not yet understand HKTs.
For just 3 service versions, the copy-paste approach in module VersionMappers is not so bad. However, we have about 10 different versions of this service. I can do copy-paste again, but this feels like something a functional language can handle.
My overarching questions: Is there some F# way to reduce the repetitious ticketxxToDomain code in module VersionMappers to something like I have in module CanThisPseudoCodeBeReal, or have I cornered myself in copy-paste land? Are code quotations or HKTs a viable option?
``` f#
module DataAccess =
type Service40 = WsdlService<My40ServiceUrl>
type Service41 = WsdlService<My41ServiceUrl>
type Service42 = WsdlService<My42ServiceUrl>
// All the ticketXXToDomain functions look like they can be less copy-paste, but how?
// Still, I think this F# approach is MUCH better than the monolith project we have where WCF SvcUtil.exe
// generate everything for every version and we check that in to source.
module VersionMappers =
open DataAccess
module Ticket =
let ticket40ToDomain (t : Service40.ServiceTypes.Ticket) =
MyCSharpProject.Pocos.Ticket(
TicketId = t.TicketId
// with 10+ other fields
TicketType = t.TicketType)
let ticket41ToDomain (t : Service41.ServiceTypes.Ticket) =
MyCSharpProject.Pocos.Ticket(
TicketId = t.TicketId
// with 10+ other fields
TicketType = t.TicketType)
let ticket42ToDomain (t : Service42.ServiceTypes.Ticket) =
MyCSharpProject.Pocos.Ticket(
TicketId = t.TicketId
// with 10+ other fields
TicketType = t.TicketType)
module CanThisPseudoCodeBeReal =
type ServiceTicket =
| V40Ticket of Service40.ServiceTypes.Ticket
| V41Ticket of Service41.ServiceTypes.Ticket
| V42Ticket of Service42.ServiceTypes.Ticket
let ticketToDomain (t : 'a when 'a is one case of ServiceTicket) =
// Now the compiler will hopefully complain if t.SomeField is not on
// all cases, and I am happy to handle that.
MyCSharpProject.Pocos.Ticket(
// I am using fake field names for clarity.
// every VxxTicket has this field, no warning
TicketId = t.TicketId,
// compiler warns that only V40Ticket has this field
SomeV40Field = t.SomeV40Field,
// compiler warns that V42Ticket does not have this field
SomeV40And41Field = t.SomeV40And41Field
)
```
Side note: everything surrounding the ServiceTypes.Ticket object, including the service calls to retrieve them, are identical across all versions of the service so we could just use one WsdlService pointing to the latest version. We confirmed this from the source code. There are other slices of the service where is not the case, so I am trying to see if there is some way to reduce the repetition before people complain about it. This project is a pilot to try out F# and the WsdlService provider to "strangle" part of the existing WCF service facade.
F# knows Statically Resolved Type Parameters which allow for 'compile-time generics' (typesafe duck-typing):
open System
type T1 = { f1: string; f2: int; f3: DateTime }
type T2 = { f2: int; f3: DateTime; f4: double }
type TCommon = { f2: int; f3: DateTime }
let inline toTCommon n = {
f2 = (^N : (member f2 : int) n)
f3 = (^N : (member f3 : DateTime) n) }
the signature of toTCommon is:
val inline toTCommon :
n: ^N -> TCommon
when ^N : (member get_f2 : ^N -> int) and
^N : (member get_f3 : ^N -> DateTime)
Any type having f2 : int and f3 : DateTime satisfies this.
I hit something new to me with the following piece of code when following the equivalent in C# here. The compiler gives multiple errors basically telling the IConnectableObservable created in source.Publish() does not match IObservable even though it derives from it (according to the MSDN article linked).
Is there something in F# that is different with regard to C# concerning inheritance in this case or can someone provider pointers as to what is going on? Have I just made a typo I can't see? What comes to the heading regarding covariance, it's just a wild guess as I'm at least temporarily out of ideas. And so, maybe writing somewhere may help me and others...
One example of the many error messages:
No overloads match for method 'Create'. The available overloads are shown below (or in the Error List window).
No overloads match for method 'Switch'. The available overloads are shown below (or in the Error List window).
Error Possible overload: '(extension) IObservable.Switch<'TSource>() :
IObservable<'TSource>'. Type constraint mismatch. The type
IObservable<IConnectableObservable<'b>> is not compatible with type
IObservable<IObservable<'a>> The type 'IObservable<'a>' does not match the type 'IConnectableObservable<'b>'.
open System.Reactive.Concurrency
open System.Reactive.Disposables
open System.Reactive.Subjects
open System.Reactive.Linq
type Observable with
static member inline Suspendable(source: IObservable<_>, suspend: IObservable<bool>, isSuspendedInitially: bool): IObservable<_> =
Observable.Create<_>(fun observer ->
let shared = source.Publish()
let pausable =
suspend.StartWith(isSuspendedInitially)
.TakeUntil(shared.LastOrDefaultAsync())
.DistinctUntilChanged()
.Select(fun p -> if p then shared else Observable.Empty<_>())
.Switch()
new CompositeDisposable(pausable.Subscribe(observer), shared.Connect()))
The corresponding C# code
public static class RxExtensions
{
public static IObservable<T> Suspendable<T>(this IObservable<T> stream, IObservable<bool> suspend, bool isSuspendedInitially)
{
return Observable.Create<T>(o =>
{
var shared = stream.Publish();
var pausable = suspend
.StartWith(isSuspendedInitially)
.TakeUntil(shared.LastOrDefaultAsync())
.DistinctUntilChanged()
.Select(p => p ? shared : Observable.Empty<T>())
.Switch();
return new CompositeDisposable(pausable.Subscribe(o), shared.Connect());
});
}
}
This was a bit tricky, but you need to add two upcasts: shared to IObservable<_>, and the result of the lambda function to IDisposable. These would be implicit in C#, but need to be explicit in F#:
type Observable with
static member inline Suspendable (source: IObservable<_>,
suspend: IObservable<bool>,
isSuspendedInitially: bool): IObservable<'a> =
Observable.Create<_>(fun observer ->
let shared = source.Publish()
let pausable =
suspend.StartWith(isSuspendedInitially)
.TakeUntil(shared.LastOrDefaultAsync())
.DistinctUntilChanged()
.Select(fun p -> if p then shared :> IObservable<_>
else Observable.Empty<_>())
.Switch()
new CompositeDisposable(pausable.Subscribe(observer),
shared.Connect()) :> IDisposable)
Has anyone used Ninject in F# code? any hint is appreciated.
I don't think there's anything particular about using Ninject in F#. The Ninject example of Samurai / IWeapon / Sword looks like this:
open Ninject
type IWeapon =
abstract Hit: string -> unit
type Sword() =
interface IWeapon with
member x.Hit s = printfn "Slash %s" s
type Samurai(weapon: IWeapon) =
member x.Attack target =
weapon.Hit target
[<EntryPoint>]
let main args =
use kernel = new StandardKernel()
kernel.Bind<IWeapon>().To<Sword>() |> ignore
kernel.Bind<Samurai>().ToSelf() |> ignore
let samurai = kernel.Get<Samurai>()
samurai.Attack "enemy"
0
F# features like implicit constructors and type inference make the code quite concise.