I cannot make an upcast of a generic with a valid sub type
type IRequest =
abstract build: string -> unit
type ReqA =
interface IRequest with
member this.build _ = ()
type Request<'a when 'a :> IRequest> = { Req: 'a }
type Request = Request<IRequest>
let fn (x: Request<ReqA>): Request =
// The type 'Request' does not have any proper subtypes and need not be used as the target of a static coercion
// Type constraint mismatch. The type 'Request<ReqA>' is not compatible with type 'Request'
x :> Request
let fn2 (x: Request<ReqA>): Request =
// No problem
{
Req= x.Req
}
Event though Request<ReqA> is a valid specialization of Request<IRequest> I can't cast it using :>, what's the right way to cast this example?
This is not actually an allowed cast - while you can construct Request<IRequest> from Request<ReqA>, this may not in general be true for all generic types.
This issue with generics is what's known as "covariance" and "contravariance". Both .NET and C# support this (see the documentation for C#), but there is no way to define a covariant generic type in F# and even for types defined in C#, the F# compiler does not support this rule.
To see that this actually does not work, you can try defining fn using a box and unsafe cast:
let fn (x: Request<ReqA>): Request =
box x :?> Request
fn { Req = ReqA() }
This type checks, but when you try to run the code, you get an exception at run-time:
System.InvalidCastException: Unable to cast object of type 'Request`1[FSI_0036+ReqA]' to type 'Request`1[FSI_0035+IRequest]'.
The summary is - if F# supported covariance and contravariance, you could define Request as a generic type with covariant type parameter and then the above cast would be valid. But without that, the cast is not valid.
I found the proper way to do this:
My error was using a record as an interface, but if I declare a full interface for the Request then it works fine.
In this Example, now the Record just has a field that is the type of the interface:
module Example1 =
type IProps =
abstract build: string -> unit
type IReq =
abstract Req: IProps
type PropsA =
{ a: string }
interface IProps with
member this.build _ = ()
type Request =
{ Req: IProps }
interface IReq with
member this.Req = this.Req :> IProps
// type Request = Request<IProps> <-- not possible, not necesary
// let fn (x: Request<PropsA>): IReq = <-- not possible
let fn (x: Request): IReq =
// It works
x :> IReq
{ Req = { a = "" } }
|> fun x -> x.Req.a // <-- but here Req is IProps, so I cannot accesss the concrete type PropsA
|> ignore
but it makes it too generic, another try:
module Example2 =
type IProps =
abstract build: string -> unit
type IReq =
abstract Req: IProps
type PropsA =
{ a: string }
interface IProps with
member this.build _ = ()
type Request<'a when 'a :> IProps> =
{ Req: 'a }
interface IReq with
member this.Req = this.Req :> IProps
type Request = Request<IProps> // <-- Possible, but not necessary
let fn (x: Request<PropsA>): Request =
// Type constraint mismatch. The type 'Request<PropsA>' is not compatible with type 'Request'
x :> Request
let fn2 (x: Request<PropsA>): IReq = // <-- notice the type alias is not needed, as now IReq is a proper interface
// It works, proper way to do it.
x :> IReq
{ Req = { a = "" } }
|> fun x -> x.Req.a // Works, Req:: PropsA
|> ignore
Now Record<PropsA> is a full concrete type that is compatible with IReq and casting just works ™️, this allows the use of flexible types and the rest of generic capabilities to work as expected, I was under the misconception that a generic record would behave as an interface.
Another more specialized version that is also possible is:
module Example3 =
type IProps =
abstract build: string -> unit
type IReq<'a when 'a :> IProps> =
abstract Req: 'a
type PropsA =
{ a: string }
interface IProps with
member this.build _ = ()
type Request<'a when 'a :> IProps> =
{ Req: 'a }
interface IReq<'a> with
member this.Req = this.Req // <-- notice the cast is no longer needed
type Request = Request<IProps> // <-- Possible, but not necessary
let fn2 (x: Request<PropsA>): IReq<_> =
// It works, proper way to do it.
x :> IReq<_>
{ Req = { a = "" } }
|> fun x -> x.Req.a // Works, Req:: PropsA
|> ignore
The benefit of this version is that the cast is not needed and gives more flexibility to IReq.
Related
With reference to Is there an equivalent of C#'s nameof(..) in F#?
how can the nameof function used or extended for the following case?
let nameof (q:Expr<_>) =
match q with
| Patterns.Let(_, _, DerivedPatterns.Lambdas(_, Patterns.Call(_, mi, _))) -> mi.Name
| Patterns.PropertyGet(_, mi, _) -> mi.Name
| DerivedPatterns.Lambdas(_, Patterns.Call(_, mi, _)) -> mi.Name
| _ -> failwith "Unexpected format"
let any<'R> : 'R = failwith "!"
let s = _nameof <# System.Char.IsControl #> //OK
type A<'a>() =
static member MethodWith2Pars(guid:Guid, str:string) = ""
static member MethodWith2Pars(guid:Guid, ba:byte[]) = ""
let s1 = nameof <# A<_>.MethodWith2Pars #> //Error FS0503 A member or object constructor 'MethodWith2Pars' taking 1 arguments is not accessible from this code location. All accessible versions of method 'MethodWith2Pars' take 2 arguments
let s2 = nameof <# A<_>.MethodWith2Pars : Guid * string -> string #> //Same error
The compiler gives the following error:
Error FS0503 A member or object constructor 'MethodWith2Pars' taking 1 arguments is not accessible from this code location. All accessible versions of method 'MethodWith2Pars' take 2 arguments
The answer you linked is a bit outdated. F# 5.0 (released just recently) offers the true nameof feature. See the announcement: https://devblogs.microsoft.com/dotnet/announcing-f-4-7/#nameof
This feature also existed in preview since F# 4.7: https://devblogs.microsoft.com/dotnet/announcing-f-4-7/#nameof
You can write your code like this:
open System
type A() =
static member MethodWith2Pars(guid:Guid, str:string) = ""
static member MethodWith2Pars(guid:Guid, ba:byte[]) = ""
let s1 = nameof (A.MethodWith2Pars : Guid * byte[] -> string)
let s2 = nameof (A.MethodWith2Pars : Guid * string -> string)
The type annotation is needed due to overloading. Not sure why there is a generic type parameter on the class declaration, but it's not used anywhere so I just removed it.
I am trying to write an API on top of a C# library. The C# code has a dictionary from types to values that I would like to represent using immutable data structures.
However, I am having trouble writing the type constraints correctly:
open System
// System.Type cannot be used a map key
// TODO: Is this enough data to form a key?
type ComparableType =
{
AssemblyQualifiedName : string
}
type TypeMap<'t> =
private
| TypeMap of Map<ComparableType, 't>
module TypeMap =
let private innerMap =
function
| TypeMap m -> m
let empty () =
TypeMap Map.empty
// Here is the problem line!
let add<'t, 'v when 'v :> 't> (v : 'v) (m : TypeMap<'t>) =
let t = typeof<'v>
let k =
{
AssemblyQualifiedName = t.AssemblyQualifiedName
}
m
|> innerMap
|> Map.add k (v :> 't)
|> TypeMap
Error:
Invalid constraint: the type used for the constraint is sealed, which means the constraint could only be satisfied by at most one solution
The problem is a limitation of the F# compiler. In this code, it will assume that 'A = 'B:
type T<'A, 'B when 'A :> 'B>() = class end
The work-around is to make the type parameter of TypeMap<'t> concrete. Unfortunately this requires lots of boiler-plate, but it is a solution I can live with:
type FooTypeMap =
private
| FooTypeMap of Map<ComparableType, Foo>
module FooTypeMap =
let private innerMap =
function
| FooTypeMap m -> m
// etc...
There is a request for change here: https://github.com/fsharp/fslang-suggestions/issues/255
See also: https://stackoverflow.com/a/64113104/1256041
I do not think F# can handle a generic constraint like that. Also, it wont let you do a coertion to an undetermined type. Your best bet is to use box v instead of (v :> 't)
Like this:
type ComparableType =
{
AssemblyQualifiedName : string
}
type TypeMap =
private
| TypeMap of Map<ComparableType, obj>
module TypeMap =
let private innerMap =
function
| TypeMap m -> m
let empty () =
TypeMap Map.empty
let inline add (v : 'v) (m : TypeMap) =
let t = typeof< 'v>
let k =
{
AssemblyQualifiedName = t.AssemblyQualifiedName
}
m
|> innerMap
|> Map.add k (box v)
|> TypeMap
let inline get< ^v> (m : TypeMap) : ^v =
let t = typeof<'v>
let k =
{
AssemblyQualifiedName = t.AssemblyQualifiedName
}
(innerMap m).[k]
|> unbox
I am trying to achieve the following:
I have an interface, called IBotCommand and a few classes that implement it. I want to find all these classes, through reflection, instantiate an instance of each and put the result in a dictionary.
the interface is the following:
type IBotCommands =
abstract member Name: unit -> string
abstract member Description: unit -> string
abstract member Help: unit -> string
abstract member Execute: MessageEventArgs -> string[] -> string
and the code:
let t = typeof<IBotCommands>
t.Assembly.GetTypes()
|> Seq.filter (fun x -> x.IsSubclassOf(t))
|> Seq.iter (fun x ->
(
let i = Activator.CreateInstance(x) :> IBotCommands
botCommands.[i.Name] <- i
)
)
the issue I have is with the CreateInstance line. CreateInstance returns an obj type that can't be cast to IBotCommands.
I have the same in C# and it works properly but the C# version is using dynamics:
public static IEnumerable<Type> FindClassSubclassOfType<T>()
{
var a = typeof(T)
.Assembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(T)))
.Select(t => t);
return a.ToList();
}
var types = ReflectionHelper.FindClassSubclassOfType<BotCommand>();
foreach (var t in types)
{
dynamic c = Activator.CreateInstance(t);
BotCommands[c.Name] = c;
}
how can I get this behavior to work in F#?
can you cast an object to an interface in F#? it's my first time using interfaces in F#
In F#, there is a difference between upcast a :> T and downacst a :?> T.
Upcast is used when the compiler statically knows that a implements an interface T. This is useful if you have a value of a concrete class and want to get a value that has a type of an interface.
Downcast is used when the compiler does not statically know whether a implements an interface. In other words, this means that the cast can fail.
In your case, you need a downcast, because the compiler does not know whether obj implements IBotInterface. All you need to do is to add ?:
let i = Activator.CreateInstance(x) :?> IBotCommands
botCommands.[i.Name] <- i
I'd like to transform my F# OOP version of Tagless Final into a typical FP approach and I'm thinking to use Statically Resolved Type Parameters of Type Classes from OO.
What I've done is
open System
open FSharpPlus
type UserName = string
type DataResult<'t> = DataResult of 't with
static member Map ( x:DataResult<'t> , f) =
match x with
| DataResult t -> DataResult (f t)
creating the SRTP I need
type Cache =
static member inline getOfCache cacheImpl data =
( ^T : (member getFromCache : 't -> DataResult<'t> option) (cacheImpl, data))
static member inline storeOfCache cacheImpl data =
( ^T : (member storeToCache : 't -> unit) (cacheImpl, data))
type DataSource() =
static member inline getOfSource dataSourceImpl data =
( ^T : (member getFromSource : 't -> DataResult<'t>) (dataSourceImpl, data))
static member inline storeOfSource dataSourceImpl data =
( ^T : (member storeToSource : 't -> unit) (dataSourceImpl, data))
and their concrete implementations
type CacheNotInCache() =
member this.getFromCache _ = None
member this.storeCache _ = ()
type CacheInCache() =
member this.getFromCache user = monad {
return! DataResult user |> Some}
member this.storeCache _ = ()
type DataSourceNotInCache() =
member this.getFromSource user = monad {
return! DataResult user }
type DataSourceInCache() =
member this.getFromSource _ =
raise (NotImplementedException())
by which I can define a tagless final DSL
let requestData (cacheImpl: ^Cache) (dataSourceImpl: ^DataSource) (userName:UserName) = monad {
match Cache.getOfCache cacheImpl userName with
| Some dataResult ->
return! map ((+) "cache: ") dataResult
| None ->
return! map ((+) "source: ") (DataSource.getOfSource dataSourceImpl userName) }
and that kind of works as follows
[<EntryPoint>]
let main argv =
let cacheImpl1 = CacheInCache()
let dataSourceImpl1 = DataSourceInCache()
let cacheImpl2 = CacheNotInCache()
let dataSourceImpl2 = DataSourceNotInCache()
requestData cacheImpl1 dataSourceImpl1 "john" |> printfn "%A"
//requestData (cacheImpl2 ) dataSourceImpl2 "john" |> printfn "%A"
0
The problem is that I'm getting the warning
construct causes code to be less generic than indicated by the type
annotations
for both cacheImpl1 and dataSourceImpl1 and so I can't reuse requestData for the other case.
Is there a way to detour this issue?
I'm not familiar with the abstraction you're trying to implement, but looking at your code it seems you're missing an inline modifier here:
let inline requestData (cacheImpl: ^Cache) (dataSourceImpl: ^DataSource) (userName:UserName) = monad {
match Cache.getOfCache cacheImpl userName with
| Some dataResult ->
return! map ((+) "cache: ") dataResult
| None ->
return! map ((+) "source: ") (DataSource.getOfSource dataSourceImpl userName) }
As a side note, you can simplify your map function like this:
type DataResult<'t> = DataResult of 't with
static member Map (DataResult t, f) = DataResult (f t)
I am familiar with final tagless, but I'm not sure why you would use SRTPs.
Final tagless uses type classes, and these can be emulated with interfaces (see the way scala emulates typeclasses).
The approach is similar to (basically the same) as "object algebra", which can be implemented using standard OO constructs.
Why do Bind1 and Bind2 have different signatures?
type T() =
let bind(v, f) = v
member self.Bind1 = bind
member self.Bind2(a, b) = bind(a, b)
fsi reports them as
type T =
class
new : unit -> T
member Bind2 : a:'a * b:'b -> 'a
member Bind1 : (obj * obj -> obj)
end
This came up when I was playing with some computation expressions and couldn't figure out why I was getting an error message about Bind not being defined. Bind1-style didn't work, Bind2 did, and I couldn't figure out why.
Given the same objects, they do return the same result:
> q.Bind1(1:>obj,3:>obj);;
val it : obj = 1
> q.Bind2(1:>obj,3:>obj);;
val it : obj = 1
>
Using Microsoft F# Interactive, (c) Microsoft Corporation, All Rights Reserved
F# Version 1.9.7.4, compiling for .NET Framework Version v4.0.21006
Bind1 is a get property that returns a function while bind2 is a function. You can see the get accessor if you evaluate bind1 and bind2 from an instance.
> let t = new T();;
val t : T
> t.Bind1;;
val it : (obj * obj -> obj) = <fun:get_Bind1#3>
> t.Bind2;;
val it : ('a * 'b -> 'a) = <fun:it#10>
You wrote the shorthand of
member self.Bind1
with get() = bind
Using reflector you can see in Bind1 where obj comes from and the function object.
internal class get_Bind1#7 : FSharpFunc<Tuple<object, object>, object>
{
// Fields
public T self;
// Methods
internal get_Bind1#7(T self)
{
this.self = self;
}
public override object Invoke(Tuple<object, object> tupledArg)
{
object v = tupledArg.get_Item1();
object f = tupledArg.get_Item2();
return this.self.bind<object, object>(v, f);
}
}
Along with what kvb said you can add type annotation to the class to avoid the generic objects.
type T<'a, 'b>() =
let bind(v:'a, f:'b) = (v:'a)
member self.Bind1 = bind
member self.Bind2(a, b) = bind(a, b)
type T<'a,'b> =
class
new : unit -> T<'a,'b>
member Bind2 : a:'a * b:'b -> 'a
member Bind1 : ('a * 'b -> 'a)
end
To elaborate on Erik's answer, because it is impossible to have generic properties on .NET objects, F# has to pick non-generic types for v and f, which default to obj. You could choose other specific types and use a type annotation to give Bind1 a different (but still non-generic) signature.