Why are the signatures of these two methods different? - f#

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.

Related

F# `This expression was expected to have type 'IDictionary<Type,obj>' but here has type 'Dictionary<Type,obj>'

I am trying to convert a C# class into F#:
type Aggregator<'T when 'T : (new : unit -> 'T)>()=
static let ApplyMethod = "Apply"
member val private _aggregations : IDictionary<Type, obj> = new Dictionary<Type, obj>()
member val AggregateType = typeof<'T> with get
member val Alias = Unchecked.defaultof<string> with get
However it seems that even this simple code cannot compile:
Program.fs:1189 This expression was expected to have type
'IDictionary<Type,obj>'
but here has type
'Dictionary<Type,obj>'
Does it mean that a field declared with an interface IDictionary<Type, obj> type cannot infer the value passed knowing that this particular value implements that interface Dictionary<Type, obj>?
Actually if I am explicitely upcasting to IDictionary<Type, obj>:
member val private _aggregations : IDictionary<Type, obj> =
(new Dictionary<Type, obj>() :> IDictionary<Type, obj>)
This works, does it mean that F# is stricter than C# in that regard?
As pointed in the comment, F# does require an explicit:
type Aggregator<'T when 'T : (new : unit -> 'T)>()=
static let ApplyMethod = "Apply"
member val private _aggregations : IDictionary<Type, obj> = new Dictionary<Type, obj>() :> IDictionary<Type, obj>)
member val AggregateType = typeof<'T> with get
member val Alias = Unchecked.defaultof<string> with get
Side notes:
Btw, not everything what you can do in C# is possible in F# (no protected access modifier for example).
Result of the conversion:
type Aggregator<'T when 'T : (new : unit -> 'T) and 'T : not struct> (overrideMethodLookup : IEnumerable<MethodInfo>)=
let aggregations : IDictionary<Type, obj> = (new Dictionary<Type, obj>() :> IDictionary<Type, obj>)
let aggregateType = typeof<'T>
let mutable alias = Unchecked.defaultof<string>
do
alias <- typeof<'T>.Name.ToTableAlias();
overrideMethodLookup.Each(fun (method : MethodInfo) ->
let mutable step = Unchecked.defaultof<obj>
let mutable eventType = method.GetParameters().Single<ParameterInfo>().ParameterType;
if eventType.Closes(typedefof<Event<_>>) then
eventType <- eventType.GetGenericArguments().Single();
step <- typedefof<EventAggregationStep<_,_>>.CloseAndBuildAs<obj>(method, [| typeof<'T>; eventType |]);
else
step <- typedefof<AggregationStep<_,_>>.CloseAndBuildAs<obj>(method, [| typeof<'T>; eventType |]);
aggregations.Add(eventType, step)
) |> ignore
static let ApplyMethod = "Apply"
new() = new Aggregator<'T>(typeof<'T>.GetMethods()
|> Seq.where (fun x -> x.Name = ApplyMethod &&
x.GetParameters().Length = 1))
member this.Add<'TEvent>(aggregation: IAggregation<'T, 'TEvent>) =
if aggregations.ContainsKey(typeof<'TEvent>) then
aggregations.[typeof<'TEvent>] <- aggregation
else
aggregations.Add(typeof<'TEvent>, aggregation)
this
member this.Add<'TEvent>(application: Action<'T, 'TEvent>) =
this.Add(new AggregationStep<'T, 'TEvent>(application));
interface IAggregator<'T> with
member this.AggregatorFor<'TEvent>() =
if aggregations.ContainsKey(typeof<'TEvent>) then
aggregations.[typeof<'TEvent>].As<IAggregation<'T, 'TEvent>>()
else
null
member this.Build(events, session, state) =
events.Each(fun (x : IEvent) -> x.Apply(state, this)) |> ignore
state
member this.Build(events, session) =
(this :> IAggregator<'T>).Build(events, session, new 'T());
member this.EventTypes =
aggregations.Keys.ToArray();
member this.AggregateType =
aggregateType
member this.Alias =
alias
member this.AppliesTo(stream) =
stream.Events.Any(fun x -> aggregations.ContainsKey(x.Data.GetType()));

Typing a record field to hold a function with type arguments

Given a function defined as let get<'T> var1 var2 : 'T option what type signature should the given to a record field that function will be assigned to?
I've tried various permutations of type MyType = {AFunc<'T> : obj -> obj -> 'T option} but but can't find any variant that lets me introduce the type argument.
I can do this type MyType = {AFunc: obj -> obj -> obj option} and that will let me create the record {AFunc = get} but then can't apply the function because the type argument is missing.
There's a bit of ambiguity in your question. Do you want to be able to store get<'t> in a record for one particular 't per record, or do you want to have the record itself store a "generic" function like get<_>?
If the former, then TeaDrivenDev's answer will work.
If the latter, then there's no completely straightforward way to do it with F#'s type system: record fields cannot be generic values.
However, there's a reasonably clean workaround, which is to declare an interface type with a generic method and store an instance of the interface in your record, like this:
type OptionGetter = abstract Get<'t> : obj->obj->'t option
type MyType = { AFunc: OptionGetter }
let get<'t> var1 var2 : 't option = None // your real implementation here
let myRecord = { AFunc = { new OptionGetter with member this.Get v1 v2 = get v1 v2} }
let test : int Option = myRecord.AFunc.Get "test" 23.5
You have to make the record type itself generic; only then will 'T be defined and usable.
type MyType<'T> = { AFunc : obj -> obj -> 'T option }

FSharp calling a generic method in a base class

This question is based on the Functional Random Generators in week one of this course: https://www.coursera.org/course/reactive
The course is Scala based, I'm trying to replicate it in FSharp.
Here's my problem:
I have an abstract generator
[<AbstractClass>]
type Generator<'a>() =
abstract member Generate: 'a
And I have an implementation of that that generates random integers
type IntGenerator() =
inherit Generator<Int32>()
let rand = new System.Random()
override this.Generate = rand.Next(Int32.MinValue, Int32.MaxValue)
Now, I want to add a Map method to my base class so that new types of generators can be created using code like this
let integers = new IntGenerator()
let booleans = integers.Map (fun x -> x > 0)
So, here's how I modified the base class
[<AbstractClass>]
type Generator<'a>() =
abstract member Generate: 'a
member this.Map (f:'a -> 'b) = { new Generator<'b>() with member this.Generate = f base.Generate }
Unfortunately that call to base.Generate seems to be constraining the type 'b to the same as 'a
I don't get why. I'm sure I'm tripping up on something simple.
The problem here is that you have to be careful with the instance names of the member methods:
I think this should work out:
[<AbstractClass>]
type Generator<'a>() =
abstract member Generate: unit -> 'a
static member Map (f:'a -> 'b) =
fun (g : Generator<'a>) ->
{ new Generator<'b>() with
member this.Generate () = g.Generate () |> f
}
or if you insist on the member-method:
[<AbstractClass>]
type Generator<'a>() =
abstract member Generate: unit -> 'a
member this.Map (f:'a -> 'b) : Generator<'b> =
{ new Generator<'b>() with
member __.Generate () = this.Generate () |> f
}
note the differences with the this and base and __ ;)
BTW this will even work without the unit -> (as you wrote it):
[<AbstractClass>]
type Generator<'a>() =
abstract member Generate: 'a
member this.Map (f:'a -> 'b) : Generator<'b> =
{ new Generator<'b>() with
member __.Generate = this.Generate |> f
}
but I would not recommend this as you get a value-looking method in disguise: Generate
more idiomatic way
type Generator<'a> = unit -> 'a
module Generator =
let map (f:'a -> 'b) (g : Generator<'a>) =
fun () -> g () |> f
let intGenerator : Generator<int> =
let rand = new System.Random()
fun () -> rand.Next(System.Int32.MinValue, System.Int32.MaxValue)
fun fact
As you can see here (if you look closely) you will see that this map is really just the Functor-map for unit -> * ;)
disclaimer but of course as the generators are sadly impure none of the functor-laws will really hold (if you don't fix your system time)

In F#, is there a way to simplify this wrapper for OpenFile and SaveFile dialogs'

type MyOpenFileDialog(dg: OpenFileDialog) =
member x.ShowDialog = dg.ShowDialog
member x.OpenFile = dg.OpenFile
type MySaveFileDialog(dg: SaveFileDialog) =
member x.ShowDialog = dg.ShowDialog
member x.OpenFile = dg.OpenFile
The following indicated 'T (^T) would escape it's scope:
type MyFileDialog<'T
when
'T : (member OpenFile:unit->Stream) and
'T : (member ShowDialog:unit->Nullable<bool>)
>(dg: 'T) =
member x.ShowDialog = dg.ShowDialog
member x.OpenFile = dg.op
Note, the constraint expression MyFileDialog<'T...>(dg: 'T)= should all be on one line. Is split for clarity (should be allowed in language I think :) )
Static member constraints let you do quite a lot of things, but they are somewhat cumbersome (they work well for generic numerical computations, but not so well as a general-purpose abstraction). So, I would be careful about using them.
Anyway, if you want to do this, then you can - to some point. As John mentioned in the comments, code that uses static member constraints need to be inline. But you can make that a static member, which captures the operations that your type uses:
type MyFileDialog
private(openFile:unit -> Stream, showDialog : unit -> DialogResult) =
member x.OpenFile() = openFile()
member x.ShowDialog() = showDialog()
static member inline Create(dg : ^T) =
MyFileDialog
( (fun () -> (^T : (member OpenFile:unit -> Stream) dg)),
(fun () -> (^T : (member ShowDialog:unit -> DialogResult) dg)) )
let d1 = MyFileDialog.Create(new OpenFileDialog())
let d2 = MyFileDialog.Create(new SaveFileDialog())

Better default values for a given type

Say I have DUs
type Zero = Zero
type Succ<'a> = Succ of 'a
I can use Unchecked.defaultof<Zero Succ Succ> when I'm passing the value to an inline function that only uses the type of that value. But how would I go about getting a proper value of a given type? One that isn't null.
Since the only valid value that has a type Zero Succ Succ is Succ(Succ(Zero)) I feel like I should be able to get it.
I don't care for intermediate type safety so I'm ok with typecasts.
I've tried this:
let rec makeProperSucc (a : Succ<'a>) = Succ(makeProperNum Unchecked.defaultof<'a>)
and makeProperNum (obj : obj) : obj =
if obj :? Zero then Zero :> obj
else makeProperSucc (obj :?> Succ<obj>) :> obj
let instance<'a>() = makeProperNum Unchecked.defaultof<'a> :?> 'a
Which doesn't work because defaultof gives me a null and all the type information is lost because I send it to a function expecting obj.
What about defining a static member with static constraints in the DU?
Here's a quick draft with a unary operator:
type Zero = Zero with
static member (!!) Zero = Zero
type Succ<'a> = Succ of 'a with
static member inline (!!) (Succ a) = Succ (!!a)
// Test
!!(Unchecked.defaultof<Succ<Succ<Succ<Zero>>>>)
// val it : Succ<Succ<Succ<Zero>>> = Succ (Succ (Succ Zero))
I used an operator to keep it simple, but you can also write the static constraints by hand or use the inline helper as you did before in your previous question.
UPDATE
If you want to use the inline helper module, used in FsControl 1.x, you can do it this way:
type Instance = Instance with
static member instance(Instance, _:unit ) = fun () -> ()
static member instance(Instance, _:Zero ) = fun () -> Zero
static member inline instance(Instance, _:Succ<'b>) = fun () ->
Succ (Inline.instance Instance ())
let inline instance() = Inline.instance Instance ()
let test:Succ<Succ<Zero>> = instance()
If you want to use the notation instance<'t>() you will have to disable the warning.

Resources