F# extensions used in a context where a function is not expected - f#

Hi I have an extension method in f# that checks is TcpClinet is connected and it is rather simple code:
[<Extension>]
type TcpExtension() =
[<Extension>]
static member inline IsConnectionEstablished(client: TcpClient) : bool =
let ipProperties : IPGlobalProperties = IPGlobalProperties.GetIPGlobalProperties()
let tcpConnection : Option<TcpConnectionInformation> = ipProperties.GetActiveTcpConnections() |> Seq.tryFind(fun connection -> connection.LocalEndPoint.Equals(client.Client.LocalEndPoint) && connection.RemoteEndPoint.Equals(client.Client.RemoteEndPoint))
match tcpConnection with
| Some connection -> true
| None -> false
Now I am trying to use it by simple:
let private WaitForData (client : TcpClient) =
let isConnectionAlive : bool = client.IsConnectionEstablished
isConnectionAlive
But I am getting message that looks like:
This function takes too many arguments, or is used in a context where a function is not expected
When I checked Microsoft documentation this is how they show it should be handled, what am I missing here?

The error message is a bit misleading, you're actually missing an argument
To invoke the function you need to pass in unit like if you were invoking it from C#
let isConnectionAlive : bool = client.IsConnectionEstablished()

Related

How do I assign a multi-parameter F# function to a C# variable?

I have the following F# function:
let myFSharpFunction : IO.TryTransform<IDatabaseService,EditForm,SyncType,ErrorDescription> =
fun _ _ -> Ok someValue
I would like to use this F# function as a value/delegate in my C# code:
FSharpFunc<IDatabaseService, FSharpFunc<EditForm, FSharpResult<SyncType, ErrorDescription>>> referenceToFSharpFunction = myFSharpFunction;
However, I get an error:
Error CS0428 Cannot convert method group 'myFSharpFunction' to
non-delegate type 'FSharpFunc>>'. Did you intend to invoke the
method?
I then tried:
public delegate FSharpResult<SyncType, ErrorDescription> myDelegate(IDatabaseService database, EditForm editForm);
...
myDelegate someDelegate = myFSharpFunction;
FSharpFunc<IDatabaseService, FSharpFunc<EditForm, FSharpResult<SyncType, ErrorDescription>>> FSharpFuncToInvoke = someDelegate;
However, this attempt also gave me an error:
type 'Sevan.Android.FormsSyncFragment.myDelegate' to
'Microsoft.FSharp.Core.FSharpFunc>>'
Update:
Attempt 1:
I then tried adding the following function to an F# module:
let toFunc2 f = Func<_, _, _> f // f : 'a -> 'b -> 'c
I next updated the C# code to the following:
var referenceToFSharpFunction = toFunc2<IDatabaseService, EditForm, FSharpResult<SyncType, ErrorDescription>>(myFSharpFunction);
But I then received this error:
Cannot convert method group 'FSharpFuncToInvoke' to non-delegate type
'FSharpFunc>>'. Did you intend to invoke
the method?
Attempt 2:
I also tried setting the F# code to the following:
let toFunc2 f = Func<IDatabaseService,EditForm, Result<SyncType,ErrorDescription>> f // f : 'a -> 'b -> 'c
Then within my C#, I attempted this:
var referenceToFSharpFunction = toFunc2<IDatabaseService, EditForm, FSharpResult<SyncType, ErrorDescription>>(myFSharpFunctione);
But that attempt gave me this error:
Error CS0308 The non-generic method
'Mock.toFunc2(FSharpFunc>>)' cannot be used with type arguments
In conclusion, how do I assign a multi-parameter F# function to a C# variable?
If you are defining some F# functionality that should be used by a C# client, then I would strongly recommend exposing all the functionality in a C#-friendly way - that means, doing all the wrapping on the F# side, so that the C# consumer does not have to worry about things like FSharpFunc at all.
It's a bit hard to say how this should work in your case - because your example does not actually show any realistic piece of code - only some kind of adapter with a fairly complex interface - but you could do something along the following lines:
// Your F# function that is used elsewhere in F# code
let myFsharpFunction =
fun _ _ -> Ok someValue
// Your F# logic, exposed as a C#-friendly `System.Func` delegate
let myCsharpFunction =
System.Func<_, _, _>(myFsharpFunction)
The F# function type FSharpFunc is something completely separate from the Func/Action used in C#, so you need to "convert" it by creating the respective type that C# understands, which is easy, but needs to be done for each number of arguments individually.
let toFunc1 f = Func<_, _> f // f : 'a -> 'b
let toFunc2 f = Func<_, _, _> f // f : 'a -> 'b -> 'c
// etc.
let toAction0 f = Action f // f : unit -> unit
let toAction1 f = Action<_> f // f : 'a -> unit
// etc.

Fable - Cannot get type info of generic parameter, please inline or inject a type resolver

I'm trying to write a generic json decode function in fable. It seems to compile in FSharp but I get the error message for this code:
[using the Thoth.Json library and the Fetch library from Fable.PowerPack]
let autoDecoder<'a> (json:string) (value:obj) : Result<'a, Thoth.Json.Decode.DecoderError> =
let tryDecode = Thoth.Json.Decode.Auto.fromString<'a>(json)
let getDecoderError (str:string) : Thoth.Json.Decode.DecoderError = ( "Auto decode Error", Thoth.Json.Decode.FailMessage str)
Result.mapError getDecoderError tryDecode
error FABLE: Cannot get type info of generic parameter, please inline or inject a type resolver
I'm not sure how to fix this and haven't been able to find anything on google.
I want to be able to call the function like this in my update function in Fable Elmish:
let update (msg:Msg) (model:Model) =
match msg with
..
| OrStart ->
let getData() = Fetch.fetchAs<ResultResponse> "https://randomuser.me/api/" json.autoDecoder<ResultResponse> http.getHeaders
model, Cmd.ofPromise getData () LoadedTypedData FetchError
How can I get fable to compile the autoDecoder<'a> function while keeping it generic?
Thanks
I think Fable is telling you to use inline like this:
let inline autoDecoder<'a> (json:string) (value:obj) : Result<'a, Thoth.Json.Decode.DecoderError> =
let tryDecode = Thoth.Json.Decode.Auto.fromString<'a>(json)
let getDecoderError (str:string) : Thoth.Json.Decode.DecoderError = ( "Auto decode Error", Thoth.Json.Decode.FailMessage str)
Result.mapError getDecoderError tryDecode
That is because generic functions just like inline functions need to be instantiated for each call.
BTW, the value parameter is not being used.
You can also streamline the code like this:
let inline autoDecoder<'a> (json:string) : Result<'a, Thoth.Json.Decode.DecoderError> =
Thoth.Json.Decode.Auto.fromString<'a> json
|> Result.mapError (fun (str:string) -> "Auto decode Error", Thoth.Json.Decode.FailMessage str)
I'm new to fable and I wasn't able to get it working, the fable compiler does not allow the auto decode without a specified type - fails here:
Thoth.Json.Decode.Auto.fromString<'a>(str, true)
But for anyone struggling with the fetch api in fable, this can be done with not too much boilerplate code. I couldn't get the promise to be generic, but a type specific implementation like getCustomers is quite succinct and I ended up doing something like this:
type Msg =
| Start
| LoadedCustomerData of Result<QueryDataForJson, string>
..
let getCustomers () = promise {
let! response = Fetch.fetch "http://localhost:5000/spa/api/customers" http.getHeaders
let! text = response.text()
return Thoth.Json.Decode.Auto.fromString<QueryDataForJson>(text, true)
}
..
let update (msg:Msg) (model:Model) =
match msg with
| Start ->
model, Cmd.ofPromise getCustomers () LoadedCustomerData FetchError
| LoadedCustomerData resp ->
match resp with
| Ok qdj -> { model with gridData= queryDataFromJson qdj; message= "Loaded Customer Data" }, Cmd.none
| Error str -> { model with message = str }, Cmd.none

Consuming a custom event in F#

Event handling in F# is really causing headache for me. The error I'm facing is
This expression was expected to have type
obj -> unit
but here has type
System.EventHandler<obj>
I'm publish the event as follows
type ...
let dataChanged = new Control.Event<obj>()
[<CLIEvent>]
member this.DataChanged = dataChanged.Publish
member private this.NotifyDataChanged(sender : obj) =
dataChanged.Trigger(sender)
And I'm trying to catch it like this
type ...
let dataChangedHandler = new System.EventHandler<obj>(fun sender -> this.DataItems_DataChanged(sender))
do this.DataItems.DataChanged.Add(dataChangedHandler)
member private this.DataItems_DataChanged(sender : obj) =
...
As said, the code doesn't compile. I've tried different variations but just can't get it working. Any help would be appreciated.
The compiler clearly tells you: dataChangedHandler is expected to be of type obj -> unit, which is just a function.
Just don't wrap it in EventHandler, it'll work:
let dataChangedHandler = fun (sender: obj) -> this.DataItems_DataChanged(sender)

F# type inference needs type name but I don't know how to provide it

I am an F# noob, so am struggling a bit getting some basic stuff to work. The context here is a FAKE build script.
I am trying to use the AssemblyInfoFile namespace to generate an AssemblyInfo.cs without including a generated internal class. I know I need to call the CreateCSharpAssemblyInfoWithConfig function and pass through an AssemblyInfoFileConfig that turns off the GenerateClass property. I have this:
Target "Build" (fun _ ->
CreateCSharpAssemblyInfoWithConfig (srcDir + "TestAssemblyInfo.cs")
[Attribute.Version version
Attribute.Configuration configuration]
{ GenerateClass = false }
)
But the compiler is complaining as follows:
The type 'AssemblyInfoParams' is not compatible with the type 'AssemblyInfoFileConfig'.
At first I thought I just needed to provide the specific type to the compiler so that it didn't resolve to AssemblyInfoParams. However, I couldn't figure out how to do that inline so I instead tried this:
Target "Build" (fun _ ->
let config : AssemblyInfoFileConfig = { GenerateClass = false }
CreateCSharpAssemblyInfoWithConfig (srcDir + "TestAssemblyInfo.cs")
[Attribute.Version version
Attribute.Configuration configuration)]
config
)
But now the compiler complains as follows:
The type 'AssemblyInfoFileConfig' does not contain a field 'GenerateClass'
Looking at the definition of AssemblyInfoFileConfig, it clearly contains a property called GenerateClass:
type AssemblyInfoFileConfig
(
generateClass : bool,
?useNamespace : string ) =
member x.GenerateClass = generateClass
member x.UseNamespace =
match useNamespace with
| Some n -> n
| None -> "System"
static member Default = AssemblyInfoFileConfig(true)
Can anyone tell me what I'm doing wrong here?
AssemblyInfoFileConfig is not a record - it is a more standard type with a constructor.
You could just call it like this:
let config = AssemblyInfoFileConfig(false)

F# generics for inner functions

I have the below code:
type IQuery =
abstract List<'T> : unit -> IList<'T>
let create (str)=
let getList () : IList<'T> = upcast List<'T>()
{ new IQuery with
member this.List<'T>() = getList<'T>()
And for the last line it gives me a warning stating that:
The method or function 'getList' should not be given explicit type argument(s) because it does not declare its type parameters explicitly
However if I remove <'T> from getList call then I get a compilation error as :
The member 'List<'T> : unit -> IList<'a>' does not have the correct type to override the corresponding abstract method. The required signature is 'List<'T> : unit -> IList<'T>'.
What can I do ?
You can declare getList with an explicit type parameter:
let getList<'T> () : IList<'T> = upcast List<'T>()
You then get an error:
Explicit type parameters may only be used on module or member bindings
If you then move the let binding to the top-level at the same scope as the type, it all works:
type IQuery =
abstract List<'T> : unit -> IList<'T>
let getList<'T> () : IList<'T> = upcast List<'T>()
let create (str) =
{ new IQuery with
member this.List<'T>() = getList<'T>()
}
If your real code has getList using values only in scope in create, like str, you'll need to add them as explicit parameters to getList.

Resources