I try to call JsonConverter<'t>.Read using reflection
static member ConvertRead (readInfo: MethodInfo, converter: JsonConverter, reader: byref<Utf8JsonReader>, typeToConvert: Type, options: JsonSerializerOptions) =
readInfo.Invoke(converter, [| box reader; box typeToConvert; box options |])
and get FS0412: A type instantiation involves a byref type. This is not permitted by the rules of Common IL
Related
I'm working on a tiny F# ADO.NET "wrapper" (yes, yet another one, besides Zaid Ajaj's Npgsql.FSharp, Pim Brouwers's Donald and many others on GitHub), and I am thinking about extending the support for different ADO.NET providers...
Basically I have a core project (ie. Michelle.Sql.Core) that contains the core types + functions, a bit similar to Dapper:
type IDbValue<'DbConnection, 'DbParameter
when 'DbConnection :> DbConnection
and 'DbParameter :> DbParameter> =
abstract ToParameter: string -> 'DbParameter
type CommandDefinition<'DbConnection, 'DbParameter, 'DbType
when 'DbConnection :> DbConnection
and 'DbParameter :> DbParameter
and 'DbType :> IDbValue<'DbConnection, 'DbParameter>> =
{ Statement: Statement
Parameters: (string * 'DbType) list
CancellationToken: CancellationToken
Timeout: TimeSpan
StoredProcedure: bool
Prepare: bool
Transaction: DbTransaction option }
First thing, you might think "Wosh there is a lot generics ornamenting your type definitions!".
Alright so first things first I'm trying to work around some limitations, most notable this one: https://github.com/fsharp/fslang-suggestions/issues/255 (along with its good friend), thought I could circumvent that issue by creating a C# project and forcing constraints in that project, it doesn't work out.
The reason I need that many generic constraints is that I want a strongly-typed connection that kinda "flow" through the calls setting the values of the different fields of that record, for example:
let playWithSQLite() =
use connection = new SQLiteConnection()
Sql.statement "INSERT INTO aTable (aColumn) VALUES(#aNumber);"
|> Sql.prepare true
|> Sql.timeout (TimeSpan.FromMinutes(1.))
|> Sql.parameters [("aNumber", SqliteDbValue.Integer 42L)]
|> Sql.executeNonQuery connection
Fyi, SqliteDbValue is defined in different assembly Michelle.Sql.Sqlite:
// https://www.sqlite.org/datatype3.html
type SqliteDbValue =
| Null
| Integer of int64
| Real of double
| Text of string
| Blob of byte array
interface IDbValue<SQLiteConnection, SQLiteParameter> with
member this.ToParameter(name) =
let parameter = SQLiteParameter()
// Not so secret impl. goes here...
parameter
The code above works, basically, the CommandDefinition record is populated via different calls defined in the core library through a Sql module (decorated with RequiredAccessAttribute).
The problem arise when the use needs to explicitly indicates the generic return type...
[<RequireQualifiedAccess>]
module Sql =
// [...]
let executeNonQuery
(connection: 'DbConnection when 'DbConnection :> DbConnection)
(commandDefinition: CommandDefinition<'DbConnection, 'DbParameter, 'DbType>
when 'DbConnection :> DbConnection
and 'DbParameter :> DbParameter
and 'DbType :> IDbValue<'DbConnection, 'DbParameter>) =
async {
// Not so secret impl. goes here
}
let executeScalar<'Scalar, .. >
(connection: 'DbConnection when 'DbConnection :> DbConnection)
(commandDefinition: CommandDefinition<'DbConnection, 'DbParameter, 'DbType>
when 'DbConnection :> DbConnection
and 'DbParameter :> DbParameter
and 'DbType :> IDbValue<'DbConnection, 'DbParameter>) =
async {
// Not so secret impl. goes here
}
So you see, in the case of the executeScalar function above, since one type has to be made explicit, it means every other generic parameter has now to be made explicit when calling that function, otherwise they are defaulted to obj, which among other things means that the end-user now needs to input 4 generic parameters:
// [...] setting up the CommandDefinition...
|> Sql.executeScalar<int64, SQLiteConnection, SQLiteParameter, SqliteDbValue> connection
and this is exactly the kind of things I would like to avoid while retaining the connection consistency.
What I tried and which is rather a clunky solution is to implement a reduced version of the executeScalar, and what I mean by:
module Michelle.Sql.Sqlite
[<RequireQualifiedAccess>]
module Sql =
let executeScalar<'Scalar> connection commandDefinition =
Sql.executeScalar<'Scalar, SQLiteConnection, SQLiteParameter, SqliteDbValue>
connection
commandDefinition
But the thing with strategy is that it essentially boils down to shadowing:
Hence this code below doesn't work:
open Michelle.Sql.Sqlite
open Michelle.Sql.Core
// [...] setting up the CommandDefinition... connection being an instance of SQLiteConnection
|> Sql.executeScalar<int64> connection
While that one does:
open Michelle.Sql.Core
open Michelle.Sql.Sqlite
// [...] setting up the CommandDefinition... connection being an instance of SQLiteConnection
|> Sql.executeScalar<int64> connection
I wish there could be a solution, I even though about static classes, but partial classes can't be defined across several assemblies.
I know that overloading is not possible with F# module functions and shadowing doesn't look like a viable solution in terms of developer experience.
So, is there any solution out there? (Putting aside creating another function with a different name or a different module with also a different name)
Koenig Lear suggested:
Not really an answer but why don't you rename your original module to CoreSQL then you can create modules for each driver type e.g. Sql.execluteScalar<'T> = CoreSql.executeScalar<'T,SQLLiteConnection, etc. and provide aliasing to every single function and never expose CoreSQL.
and this is pretty much what I ended up doing:
// open stuff goes here...
type SqliteCommandDefinition = CommandDefinition<SQLiteConnection, SQLiteParameter, SqliteDbValue>
[<RequireQualifiedAccess>]
module Sqlite =
// Other functions irrelevant to this post
let executeScalar<'Scalar> connection (commandDefinition: SqliteCommandDefinition) =
Sql.executeScalar<'Scalar, _, _, _>
connection
commandDefinition
let executeNonQuery connection (commandDefinition: SqliteCommandDefinition) =
Sql.executeNonQuery connection commandDefinition
Is there a way to infer literal type with Flow in a generic way without explicit type casting?
Here's an example that should explain what I want to do. This code will not work with Flow because there's not $Literal utility type.
// explicit type casting to literal type
const a: 'flow' = 'flow
// inferring literal type
type Primitive = null | void | boolean | number | string
function literal <T: Primitive>(value: T): $Literal<T> {
return value
}
// cast type with generic function - type is inferred to 'flow' string literal
const a = literal('flow')
The following code gives compile-time error:
type IFile interface {
Read() (n int, err error)
Write() (n int, err error)
}
type TestFile struct {
*IFile
}
Error:
./test.go:18: embedded type cannot be a pointer to interface
Why can't I embed *IFile?
The language spec does not allow it. Relevant section from the spec: Struct types:
A field declared with a type but no explicit field name is an anonymous field, also called an embedded field or an embedding of the type in the struct. An embedded type must be specified as a type name T or as a pointer to a non-interface type name *T, and T itself may not be a pointer type. The unqualified type name acts as the field name.
A pointer to an interface type is very rarely useful, as an interface type may hold a pointer or a non-pointer value.
Having said that, if a concrete type that implements your IFile type is a pointer type, then a pointer value will be wrapped in an interface value of type IFile, so you still have to embed IFile, just the value that implements IFile will be a pointer value, e.g.
// Let's say *FileImpl implements IFile:
f := TestFile{IFile: &FileImpl{}}
Edit: Answer to your comment:
First, this is Go, not C++. In Go, interfaces are not pointers, but they are represented as a pair of (type;value), where "value" may be pointer or non-pointer. More about this in blog post: The Laws of Reflection: The representation of an interface.
Second, if FileImpl is a type, f := TestFile{IFile : &FileIml} is obviously a compile-time error, you need a value of *FileImpl and &FileImpl clearly isn't. You need for example a composite literal which is in the form of &FileImpl{}, so it should be as I posted above.
I have an F# assembly called Assembly1
I have the following module:
namespace Assembly1
module MyModule =
type MyClass(x: int, y: int) =
new() = MyClass(0, 0)
In a C# assembly that references this F# assembly, the following code gives me a value of 'null' back:
var myType = Type.GetType("Assembly1.MyModule.MyClass, Assembly1");
Is what I am trying to do not possible?
To add to Mark's answer, it's also worth noting that a lot of modules in F# are represented by different names in IL (and therefore non-F# languages) than they appear with in F# itself.
For example, this piece of code:
open System
open System.Reflection
open Microsoft.FSharp.Reflection
let private core =
AppDomain.CurrentDomain.GetAssemblies()
|> Seq.find (fun a -> a.GetName().Name = "FSharp.Core")
let private seqMod =
core.GetTypes()
|> Seq.filter FSharpType.IsModule
|> Seq.find (fun t ->
t.FullName = "Microsoft.FSharp.Collections.SeqModule")
Will find the Seq module in FSharp.Core.
The FSharp.Reflection namespace has a bunch of helper methods to make working with F# specific types with System.Reflection a little bit less painful, so it's worth loading up a couple of assemblies in FSI and having a play with those if you're going to be doing a lot of reflection work with F# code.
You can create modules with "mismatching" names like this yourself using the CompiledName attribute - this is especially useful for those times where you want a type and a module with the same name. The normal convention (as shown with the Seq type/module) is to annotate the module with Compiled name.
[<CompiledName("MyTypeModule")>]
module MyType =
let getString m =
match m with
| MyType s -> s
Since you've put MyClass into a module, it's compiled as a nested class. Its name is Assembly1.MyModule+MyClass.
From C#, you should be able to load it like this:
var myType = Type.GetType("Assembly1.MyModule+MyClass, Assembly1");
If you don't want to have nested classes (which are generally frowned upon in C#), you can define it directly in a namespace:
namespace Assembly1.LibraryXyz
type MyClass(x: int, y: int) = class end
This class will have the name Assembly1.LibraryXyz.MyClass.
I want to cast MethodBase to RuntimeMethodInfo in order to retrieve the name and type of the arguments of reflected methods, and the returned type of those methods.
I can make a direct cast in Inmediate Window but have not found a way to make the cast with F#.
Why cast? You can call GetParameters() and all the other members you'd need on the MethodBase reference.
let methodInfo : MethodBase = //whatever
let firstParamName = methodInfo.GetParameters().[0].Name
EDIT (return types):
First, note that GetMethod returns MethodInfo, not MethodBase. You can't cast to RuntimeMethodInfo since, as others have noted, that is an internal type. But the ReturnType property is declared on MethodInfo, so all is well.
This therefore works, with the static type of methodInfo being MethodInfo:
let methodInfo = typeof<object>.GetMethod("ToString")
let returnTypeName = methodInfo.ReturnType.Name // "String"
Second, if you have a static reference to a MethodBase that you know is a MethodInfo, use the :?> operator. Example:
let toMethodInfo (mb : MethodBase) = mb :?> MethodInfo
On the other hand, if you're not sure about the object's actual type, you can use match:
let tryToMethodInfo (mb : MethodBase) =
match mb with
| :? MethodInfo as result -> Some result
| _ -> None
Finally, since you ask about "vice versa" in your comment: When you're going from a derived class to one of its base classes, which always succeeds, you don't need the question mark:
let toMethodBase (mi : MethodInfo) = mi :> MethodBase