type VBO<'T when 'T : (new : unit -> 'T) and 'T : struct and 'T :> ValueType> =
{ Handle : int
target : BufferTarget
size : int
dataSize : int
data : 'T []
pos : int
usage : BufferUsageHint }
type VBO =
static member Create(target, size, pos, usage, (data : Vector3 [])) =
VBO.CreateImpl(target, size, pos, usage, Vector2.SizeInBytes, data)
// Type mismatch. Expecting Vector3 but found Vector2
static member Create(target, size, pos, usage, (data : Vector2 [])) =
VBO.CreateImpl(target, size, pos, usage, Vector2.SizeInBytes, data)
// This construct causes code to be less generic than indicated by the type annotations.
// The type variable 'T has been constrained to be type 'Vector3'.
static member CreateImpl(target, size, pos, usage, dataSize, (data : 'T [])) =
let h = GL.GenBuffer()
{ Handle = h
target = target
size = size
dataSize = dataSize
data = data
pos = pos
usage = usage }
F# tries to constrain my code but I want it to be generic. I don't really care about what type the data is I just need it pass in the correct dataSize.
What have I done wrong?
The F# compiler seems to specialise generic types if they are used at a particular instantiation in the same block of code. Try splitting up your declaration like this:
type VBO<'T when 'T : (new : unit -> 'T) and 'T : struct and 'T :> ValueType> =
{ Handle : int
target : BufferTarget
size : int
dataSize : int
data : 'T []
pos : int
usage : BufferUsageHint }
type VBO =
static member CreateImpl(target, size, pos, usage, dataSize, (data : 'T [])) =
let h = GL.GenBuffer()
{ Handle = h
target = target
size = size
dataSize = dataSize
data = data
pos = pos
usage = usage }
type VBO with
static member Create(target, size, pos, usage, (data : Vector3 [])) =
VBO.CreateImpl(target, size, pos, usage, Vector2.SizeInBytes, data)
static member Create(target, size, pos, usage, (data : Vector2 [])) =
VBO.CreateImpl(target, size, pos, usage, Vector2.SizeInBytes, data)
F#'s type inference works top-to-bottom and left-to-right, which occasionally leads to interesting corner cases. In particular, reordering method definitions within a type can affect the inferred types. As Ganesh has pointed out, one solution to your problem is to break your type up into disjoint chunks, but this isn't actually necessary - just putting the generic method first should be sufficient (and note that you can also drop the type annotation on data, if desired).
You can see very similar behavior with simple let rec bindings. Consider:
let rec f() = h 5
and g() = h "test"
and h x = x
When attempting to infer the type of f, the compiler notes that h must take an int as input, which makes g's definition invalid. Reordering the definitions to put h first is the easiest solution (then the compiler can infer a generic type for h before looking at the bodies of f and g, so everything goes through fine).
Alternatively, you can explicitly make h generic and add type annotations to help the compiler out:
let rec f() = h 5
and g() = h "test"
and h<'t> (x:'t) :'t = x
This can be inconvenient since it can require a significant annotation overhead, but there are circumstances where simply reordering definitions is insufficient to allow the compiler to infer the correct types, so it may sometimes be necessary.
I think it is better to look at the code to understand what went wrong.
So here is a simpler example that shows the same problem.
type test =
static member test (data:int) = test.actual(data)
static member test (data:float) =test.actual(data)
static member actual (data:'t) = ()
the problem is that for static member functions, all the types need to be known - or you need a generic type.
I think the simplest solution is to change your code to look like
let actual (data:'t) = ()
type test =
static member test (data:int) = actual(data)
static member test (data:float) =actual(data)
Here, the compiler has much more freedom to alter the let binding to make it generic.
Related
I'm not sure that what I am looking for is possible, but let's try!
I am trying to define an inline function explicit that will convert a Box<'t> into a 't to any level of nesting.
The code works as expected for one level (Box<int> -> int), however Box<Box<int>> is only converted to Box<int>, despite the type-annotation on the L.H.S.
type Box<'t> =
{
Item : 't
}
with
static member inline op_Explicit (x : Box<'t>) : 't =
x.Item
let inline explicit<'t, 'u when 't : (static member op_Explicit : 't -> 'u)> (x : 't) : 'u =
't.op_Explicit x
let x : int = explicit { Item = 123 }
let y : int = explicit { Item = { Item = 123 } }
let z : int = explicit { Item = { Item = { Item = 123 } } }
I think the short answer is no. The signature of explicit clearly unwraps only a single level, and I don't think there's any type-safe way to define the function you want in F#.
It sounds like you actually want the compiler to automatically chain multiple explicit calls together? For what it's worth, C# will silently insert a single implicit (not explicit) conversion for you, but even that language won't chain more than one together implicitly.
I have always considered the types float32 and single to be interchangeable, in that they are type aliases. The same for float and double. However, they appear to be declared in different assemblies Microsoft.FSharp.Core.Operators vs Microsoft.FSharp.Core.ExtraTopLevelOperators.
Also, the popup description is slightly different, where F# says on float32 and float that it can take a string and use Parse() on it.
However, trying that with single and double succeeds just fine too:
let x = single "12.3"
let y = double "13.4"
Is there any difference I should be aware of? I have always used them interchangeably, never really gave it another thought, until I saw the differences in the popups and in signatures:
// on hovering, or in FSI, this will have the slightly confusing signature:
// val x: a: double -> float
let x (a: double) = float a
All of them are just aliases of the corresponding CLR types as you can see in prim-types-prelude.fs.
type float32 = System.Single
type float = System.Double
type single = System.Single
type double = System.Double
As for the confusing signature consider this:
type typA = A;;
type typB = typA;;
let f (x : typA) = (x : typB)
//val f : x:typA -> typB
Seems like F# prefers to use the aliases at the places you (or some other definition) used them.
Finally the namespaces you are referring to (FSharp.Core.Operators) are referring not to the float type but the float function (float : 'T -> float). See prim-types.fs.
I have a native C library and I want do some F# coding with it. The thing is I get exception:
System.TypeLoadException: Cannot marshal field 'log' of type
'LoggingModel': There is no marshaling support for this type.
at
System.StubHelpers.ValueClassMarshaler.ConvertToNative(IntPtr dst,
IntPtr src, IntPtr pMT, CleanupWorkList& pCleanupWorkList)
at
FSI_0009.Initialize(ComponentOverrideFlags flags, LoggingModel&
loggingModel, ThreadingModel& threadingModel, SchedulingModel&
schedulingModel, IntPtr memoryModel)
at
.$FSI_0011.main#()
in
D:\dev_p\f#\FunBindings\FunExample\Environment.fs:line 16 Stopped due
to error
Here the code:
module Interop
[<CLSCompliant(true); Flags>]
type LogTarget =
| None = 0
| Console = 1
| Trace = 2
| Custom = 4
[<UnmanagedFunctionPointer(CallingConvention.Cdecl)>]
type LogCallback = delegate of LogTarget * string * string * nativeint -> unit
[<UnmanagedFunctionPointer(CallingConvention.Cdecl)>]
type ReleaseCallback = delegate of nativeint -> unit
[<Struct>]
type LoggingModel =
val mutable targets : LogTarget
val mutable log : LogCallback
val mutable deleteModel : ReleaseCallback
val mutable userparam : IntPtr
[<DllImport("CLIBRARY.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "txInitialize")>]
[<MethodImpl(MethodImplOptions.ForwardRef)>]
extern int Initialize(ComponentOverrideFlags flags, LoggingModel& loggingModel, ThreadingModel& threadingModel, SchedulingModel& schedulingModel, IntPtr memoryModel)
module Environment
let initialize =
let mutable loggingModel = new LoggingModel()
let mutable threadingModel = new ThreadingModel()
let mutable schedulingModel = new SchedulingModel()
Initialize(ComponentOverrideFlags.None, &loggingModel, &threadingModel, &schedulingModel, IntPtr.Zero)
Basically, I get the aforementioned error when I try to execute "initialize" function in interactive.
I would really appreciate any help.
Update: I've checked the code a bit more and noticed that outside of the interactive console it seems to be working, without failing with exceptions. I need to provide a bit more coverage for CLibrary to be sure. Meanwhile, if there anybody who knows what could cause this exception and how it could be prevented, I would really appreciate the answer.
I think the problem is that delegate of LogTarget * string * string * nativeint -> unit declares a delegate where the arguments are curried. (This doesn't really make sense to me either since a * b normally represents a tuple.)
The subtly different delegate of (LogTarget * string * string * nativeint) -> unit declares a delegate with tupled arguments which would be compatible with a native function.
You can see this difference if you try and assign a .NET method to two different delegate types:
type Curried = delegate of int * int -> int
type Tupled = delegate of (int * int) -> int
//let a = new Curried (Math.Max) // doesn't compile
let b = new Tupled (Math.Max) // works
Have you tried adding [<MarshalAsAttribute(UnmanagedType.FunctionPtr)>] to LoggingModel?
[<Struct>]
type LoggingModel =
val mutable targets : LogTarget
[<MarshalAsAttribute(UnmanagedType.FunctionPtr)>]
val mutable log : LogCallback
[<MarshalAsAttribute(UnmanagedType.FunctionPtr)>]
val mutable deleteModel : ReleaseCallback
val mutable userparam : IntPtr
IL code without this attribute is:
// Fields
.field public class Interop.LogCallback log
but with this attribute is:
// Fields
.field public marshal(Func) class Interop.LogCallback log
Without marshal(Func)/MarshalAs attribute the delegate cannot be marshalled even with the UnmanagedFunctionPointer attribute. Cannot test it with a native library though.
I want to create a cacheline sized struct wrapping a mutable, volatile int64 in F#. I’ve tried various struct definitions including the one below, but can't get anything to compile.
[<Struct; StructLayout(LayoutKind.Explicit, Size = 64)>]
type MyStruct1 (initVal:int64) =
[<VolatileField>]
let mutable value = initVal
member x.Value
with get () = value
and set(valIn) = value <- valIn
which gives this error: "Structs cannot contain value definitions because the default constructor for structs will not execute these bindings.
consider adding additional arguments to the primary constructor for the type". I can't see what additional arguements I could add to the primary constructor above.
Any ideas?
The struct definition could be
[<Struct; StructLayout(LayoutKind.Explicit, Size = 64)>]
type MyStruct =
[<FieldOffset(0)>]
val mutable value : int64
new(initVal:int64) = { value = initVal }
member x.Value
with get() = x.value
and set(valIn) = x.value <- valIn
but then, [<VolatileField>] is not allowed on val bindings and structs can't contain let bindings.
TL;DR: AFAIK this is impossible in F#
As pointed out by #V.B. you could use Interlocked which gives a superset of volatiles guarantees (stronger guarantees ≈ more overhead). It might then be better to privateize value to prevent (accidental) writes circumventing the barrier:
[<Struct; StructLayout(LayoutKind.Explicit, Size = 64)>]
type MyStruct =
[<FieldOffset(0)>]
val mutable private value : int64
new(initVal:int64) = { value = initVal }
member public x.Value
with get() = Interlocked.Read(&x.value)
and set(valIn) = Interlocked.Exchange(&x.value, valIn) |> ignore
Interlocked gives similar guarantees as volatile, see this question.
open System.Threading
[<Struct; StructLayout(LayoutKind.Explicit, Size = 64)>]
type MyStruct =
[<FieldOffset(0)>]
val mutable value : int64
new(initVal:int64) = { value = initVal }
member x.Value
with get() = Interlocked.Read(&(x.value))
and set(valIn) = Interlocked.Exchange(&(x.value),valIn) |> ignore
Consider the following sample code where I have a generic type and 2 static member constructors that create a specialized instance of the said type.
type Cell<'T> = { slot: 'T }
with
static member CreateInt x : IntCell = { slot = x }
static member CreateString x : StringCell = { slot = x}
and IntCell = Cell<int>
and StringCell = Cell<string>
// Warnings on the next 2 lines
let x = Cell.CreateInt 123
let y = Cell.CreateString "testing"
I think I have the necessary type annotations in place and yet F# gives me warnings. E.g:
Warning 2 The instantiation of the generic type 'Cell' is missing and can't be inferred from the arguments or return type of this member. Consider providing a type instantiation when accessing this type, e.g. 'Cell<_>'.
How can I make the warning go away?
As hinted by #ildjarn, Cell is a generic type and the compiler wants to know the type 'T when calling the static member.
// Two ways to fix the compiler warning
let x = Cell<int>.CreateInt 123
let y = StringCell.CreateString "testing"
A way to avoid specifying 'T is to move the create functions into a module.
type Cell<'T> = { slot: 'T }
type IntCell = Cell<int>
type StringCell = Cell<string>
module Cell =
let createInt x : IntCell = { slot = x }
let createString x : StringCell = { slot = x }
let x = Cell.createInt 123
let y = Cell.createString "testing"
However, since you specify the desired type in the function name anyway, the following syntax may be preferred.
type Cell<'T> = { slot: 'T }
with
static member Create (x : 'T) = { slot = x }
type IntCell = Cell<int>
type StringCell = Cell<string>
let x = IntCell.Create 123
let y = StringCell.Create "testing"
// or simply
let z = Cell<float>.Create 1.0
Thanks to #Vandroiy for pointing out the missing type constraint in my Create method and for his answer that shows how the compiler can infer 'T for the generic type Cell when it can be determined by the static method being called.
The compiler cannot determine the generic parameter 'T of the methods CreateInt and CreateFloat because it is unrelated to the methods' return types. In the question, it is valid to write:
Cell<float>.Create 1.0 // useless type annotation to remove warning
However, you can just as well write
Cell<string>.Create 1.0 // Trollolol
To avoid this, you need to make sure the factory can only produce the type it is called on. When declaring a factory on a generic type, use a type annotation to equate the generic argument of its return type with the generic argument of the type it is called on.
In my opinion, the complicated formulation adds to the confusion. You can achieve the desired effect with
type Cell<'T> =
{ slot: 'T }
static member Create (x : 'T) = { slot = x }
let x = Cell.Create 123
let y = Cell.Create "testing"
Note the type annotation for x that equates the factory's input type with the generic parameter of the Cell<> type!
Edited to address the comment:
As is, the types IntCell and StringCell serve no purpose; they are just a less readable form of Cell<int> and Cell<string>. From a comment to this answer, I understand that these types should be exposed instead of Cell. As far as I know, this is not possible if they are defined as in the question, since type abbreviations have at most the accessibility of the type they abbreviate.
This is a reasonable design choice: if a type is generic, it should accept all valid generic type arguments. If IntCell and StringCell add specialized implementation, the usual way is to compose them of the appropriate instantiation of the Cell type and their specialized features. Then, the Cell type is allowed to have a more restricted accessibility than the specialized types.