Is it possible to have an instance method of a class which only works when the generic type is restricted to a particular type?
For example, I want to define an Add method which only works when the contained type is float. Here's the closest I've gotten but I'm unsure how to constrain the 'Value type to float in the member _.Add signature. Right now I get an error on the next to the last line saying the type of 'Value cannot be resolved at compile time. Can I restrict this method to only apply when the type of 'Value is float?
type ImArr<[<Measure>] 'Index, 'Value>(values: array<'Value>) =
// We want to make sure we have our own copy to protect against mutation
let values = values |> Array.copy
member internal _.Values : array<'Value> = values
member this.Item
with get(index: int<'Index>) =
values.[int index]
type Arr<[<Measure>] 'Index, 'Value>(values: array<'Value>) =
// We want to make sure we have our own copy to protect against mutation
let values = values |> Array.copy
member this.Item
with get(index: int<'Index>) =
values.[int index]
and set(index: int<'Index>) (value: 'Value) =
values.[int index] <- value
member internal _.Values : array<'Value> = values
// This is the method I would like to constrain to only be used when the
// type of 'Value is float
member _.Add (b: ImArr<'Index, float>) =
if values.Length <> b.Values.Length then
invalidArg (nameof b) "Cannot add arrays of different lengths"
// TODO: Turn this into something using SIMD intrinsics
let mutable i = 0
while i < values.Length && i < b.Values.Length do
values.[i] <- values.[i] + b.Values.[i] // <-- Error here
i <- i + 1
As mentioned by #brianberns in the comments, not directly. However, C#-style extension methods can be defined for a specialized version of a class. The caveat is that as an extension method, it won't have access to private members.
[<Extension>]
type ArrExtensions =
[<Extension>]
static member Add (this: ImArr<'Index, float>, b: ImArr<'Index, float>) =
if this.Values.Length <> b.Values.Length then
invalidArg (nameof b) "Cannot add arrays of different lengths"
// TODO: Turn this into something using SIMD intrinsics
let mutable i = 0
while i < this.Values.Length && i < b.Values.Length do
this.Values.[i] <- this.Values.[i] + b.Values.[i] // <-- Error here
i <- i + 1
// When the namespace containing the above type is opened,
// the following can be called:
floatArr1.Add(floatArr2)
Related
I am having issues creating a generic math class (here as very tiny example code a class of type Vector) that, however, is not limited to one single numerical data type for its values, but instead uses ^F as static resolved type parameter, and I expect it to be whatever the user uses to instanciate the class, such as int but also BigRational or MyCustomNumber, as long as it adheres to the constraints.
type Vector< ^F> (_values : ^F []) =
let values = _values
member inline __.Dimension = Array.length values
member inline __.Item with get i = values.[i + 1]
static member inline ( * ) (a: Vector< ^F>) (scalar: ^F) =
Vector< ^F>(Array.init (a.Dimension) (fun i -> values.[i] * scalar)
override inline __.ToString() = "{" + (values.ToString()) + "}" // <--- problem-line
My problem now with this code is, that I still don't know how to properly override the Object.ToString() method (same for how to implement IComparable, which I believe I could fix the same way).
Is this actually even possible?
Many thanks in advance!
Do not annotate the arithmetic operator's arguments with a type parameter, they will be inferred alright
Pass the arguments of the arithmetic operator as a tuple
Close the parenthesis in the implementation of the arithmetic operator
Replace the let-bound value values by a property
type Vector<'F> (_values : 'F[]) =
member val Values = _values
member inline me.Dimension = Array.length me.Values
member inline me.Item with get i = me.Values.[i + 1]
static member inline ( * ) (a: Vector<_>, scalar) =
Vector<_>(Array.init (a.Dimension) (fun i -> a.Values.[i] * scalar))
override me.ToString() = "{" + (me.Values.ToString()) + "}"
When try the following code in F# interactive
> let a = None
- let b = (a, Some 1);;
> b;;
val it : 'a option * int option = (null, Some 1)
It show that b has type 'a option * int option, the type of b is correct. However, the value of first element of the tuple is null, not None, Why?
When try to verify the first element of the tuple whether really is null
printfn "%s" (match b with (null, _) -> "null" | _ -> "not null");;
it gives the following error
error FS0043: The type ''a option' does not have 'null' as a prope r
value
When try to get the fisrt value in tuple,
let c = fst b;;
it gives
error FS0030: Value restriction. The value 'c' has been inferred to
have generic type
val c : '_a option Either define 'c' as a simple data term, make it a function with explicit argume nts or, if you do not intend for it
to be generic, add a type annotation.
The internal representation for the value None is indeed a null value. But that is the internal representation, the compiler identifies null and None as two completely different values, so you cannot compare them as they are different types. That is why you get: error FS0043.
This is actually something to be careful of:
let a = None
let b = (a, Some 1)
let print v = printfn "%A" v
sprintf "%A" a |> print // "<null>"
sprintf "%A" b |> print // "(None, Some 1)"
sprintf "%O" a |> print // "<null>"
sprintf "%O" b |> print // "(, Some(1))"
string a |> print // ""
string b |> print // "(, Some(1))"
a .IsNone |> print // true
a .IsSome |> print // false
a .GetType() |> print // System.NullReferenceException: Object reference not set to an instance of an object.
a .ToString() |> print // System.NullReferenceException: Object reference not set to an instance of an object.
(fst b).ToString() |> print // System.NullReferenceException: Object reference not set to an instance of an object.
(snd b).ToString() |> print // "Some(1)"
... because calling some methods on a None value throws the dreaded NullReference Exception and converting to string is also erratic.
Regarding error FS0030 basically values cannot be generic. This has been discussed many times in SO.
Values from Discriminated Unions seem to be treated in a special way, they seem to be granted an exception, for instance these are generic and still Ok:
type MyDU<'A, 'B> =
| ValNo
| ValA of 'A
| ValB of 'B
let v1 = ValNo // MyDU<'a,'b> double generic but Ok
let v2 = ValA 1 // MyDU<int,'a> generic but Ok
let v3 = ValB 1 // MyDU<'a,int> generic but Ok
but these are not Ok
let valNo() = ValNo
let valA a = ValA a
let valB b = ValB b
let w1 = valNo() // MyDU<'_a,'_b> double generic not Ok
let w2 = valA 1 // MyDU<int,'_a> generic not Ok
let w3 = valB 1 // MyDU<'_a,int> generic not Ok
I am trying to create an immutable collection type that behaves as a hybrid
of multiset/bag and Map that records the number of occurrences of each item.
I can write a mutable one with code a little like below and I tried to write an immutable one by inheriting from Map but Map is sealed and will not let me define any overrides.
type TallySet<'k_t when 'k_t : comparison>() = class
// inherit Map<'k_t, int>
let m_map:((Map<'k_t, int>) ref) = ref (Map.empty)
member x.add item =
m_map :=
match (!m_map).TryFind item with
| None -> (!m_map).Add(item, 1)
| Some n -> (!m_map).Add(item, 1 + n)
!m_map
member x.Count with get() = Map.fold (fun cc k v -> cc + v) 0 !m_map
end
What should I write ?
Have a look at ExtCore.Collections.Multiset. As in your code, it's just a map with the value type set to the count. Multiset.add and Multiset.count correspond to the members in your example.
There is some difficulty in getting Option type dynamically. Suppose I have a function:
let printType x =
if (box x) = null then printfn "the type is 'null'"
else printfn "the type is %A" (x.GetType())
And we have the output here:
printType 3 // the type is System.Int32
printType (Some(3)) // the type is Microsoft.FSharp.Core.FSharpOption`1[System.Int32]
printType None // the type is null
printType null // the type is null
How to differentiate between None and null when getting an expression's type?
At runtime, option None values are represented as null and so you cannot determine their runtime type. However, you could write a generic function that prints the static type of the option:
let printType (x:'T option) =
printfn "Static type is: %s" (typeof<'T>.Name)
Depending on what you need, this might or might not do what you need.
printType (None : int option) // Int32
printType (None : string option) // String
printType None // Object (the default used by the inference)
EDIT If you want to be able to call the function on any arguments, you can use typeof<'T> as in my example together with the rest of the logic as in the solution by Matthew. The following does the same thing as Matthew's snippet (but it does not create quotations for no good reason):
let printType (x:'T) =
let typeOfX = typeof<'T>
if not <| FSharpType.IsUnion(typeOfX) && (box x) = null then
printfn "Static type is %s, but the value is 'null'" typeOfX.Name
else
printfn "Static type is %s and value is not 'null'" typeOfX.Name
Edit: The code quotation is not needed and typeof can be used instead, see #Tomas Petricek's answer
You could use a code quotation to get the type from None
open Microsoft.FSharp.Reflection
let printType x =
let typeOfX = <# x #>.Type
if not <| FSharpType.IsUnion(typeOfX) && (box x) = null then
printfn "the type is 'null'"
else
printfn "the type is %A" typeOfX
Your input:
printType 3 // the type is System.Int32
printType (Some(3)) // the type is Microsoft.FSharp.Core.FSharpOption`1[System.Int32]
printType None // the type is Microsoft.FSharp.Core.FSharpOption`1[System.Object]
printType null // the type is 'null'
This can be a common problem when implementing interpreters. To add to the above solutions, you could also do the following:
open System
type TagObj (obj : obj, t : Type) =
member __.Obj = obj
member __.Type = t
and TagObj<'T>(value : 'T) =
inherit TagObj(value, typeof<'T>)
member __.Value = value
override __.ToString() = sprintf "%O" value
let mkTag x = TagObj<_>(x) :> TagObj
// examples
let t = mkTag (None : int option)
let t' = mkTag ()
t.Obj = t'.Obj // true
t.Type = t'.Type // false
This is roughly how constant values are represented in quotations as well.
From the MSDN documentation I understand that if Run is implemented it will be called automatically at the end of the computational expression. It says that:
builder.Run(builder.Delay(fun () -> {| cexpr |}))
will be generated for the computational expression. Run and/or Delay will be omitted if they are not defined in the workflow builder. I was expecting my ReaderBuilder to return a list of MyItem objects when Run is called automatically. So I do not understand why I'm getting a type mismatch error. The errors are generated by the return statement inside the ProcedureBuilder foo at the end of my code listing here. Could someone please explain what I'm misunderstanding about workflow builders and what I have implemented incorrectly?
I'm getting the following errors:
The type ''a list' is not compatible with the type 'ReaderBuilder'
Type constraint mismatch. The type 'a list is not compatible with type ReaderBuilder The type ''a list' is not compatible with the type 'ReaderBuilder'
open System
open System.Data
open System.Data.Common
open System.Configuration
let config = ConfigurationManager.ConnectionStrings.Item("db")
let factory = DbProviderFactories.GetFactory(config.ProviderName)
type Direction =
| In
| Out
| Ref
| Return
type dbType =
| Int32
| String of int
type ReaderBuilder(cmd) =
let mutable items = []
member x.Foo = 2
member x.YieldFrom item =
items <- item::items
item
member x.Run item =
items
type ProcBuilder(procedureName:string) =
let name = procedureName
let mutable parameters = []
let mutable cmd:DbCommand = null
let mutable data = []
member x.Command with get() = cmd
member x.CreateCommand() =
factory.CreateCommand()
member x.AddParameter(p:string*dbType*Direction) =
parameters <- p::parameters
member x.Bind(v,f) =
f v
member x.Reader = ReaderBuilder(cmd)
member x.Return(rBuilder:ReaderBuilder) =
data
let (?<-) (builder:ProcBuilder) (prop:string) (value:'t) =
builder.Command.Parameters.[prop].Value <- value
type MyItem() =
let mutable _a = 0
let mutable _b = String.Empty
let mutable _c = DateTime.Now
member x.a
with get() = _a
and set n = _a <- n
member x.b
with get() = _b
and set n = _b <- n
member x.c
with get() = _c
and set n = _c <- n
let proc name = ProcBuilder(name)
let (%) (builder:ProcBuilder) (p:string*dbType*Direction) =
builder.AddParameter(p)
builder
let (?) (r:DbDataReader) (s:string) = r.GetOrdinal(s)
let foo x y =
let foo = proc "foo" % ("x", Int32, In) % ("y", String(15), In)
foo?x <- x
foo?y <- y
foo {
do! foo?x <- x
do! foo?y <- y
return foo.Reader {
let item = MyItem()
item.a <- r.GetInt32("a")
item.b <- r.GetString("b")
item.c <- r.GetDateTime("c")
yield! item
}
}
The problem in your example is that the foo.Reader { ... } block has a return type MyItem list (because this is what the Run member of the ReaderBuilder type returns). However, the Return member of ProcBuilder expects an argument of type ReaderBuilder.
The data field of ReaderBuilder will be always an empty list, so this is also suspicious. I think you probably want to change the Return of ProcBuilder to take an argument MyItem list instead.
However, I think that using custom computation builder for database access doesn't really give you much advantage. You're not creating a "non-standard computation" in some sense. Instead, you probably just want a nice syntax for calling commands & reading data. Using the dynamic operator can make this quite elegant even without computation builders - I wrote an article about this some time ago.