Why isn't this generic function supported in my example? - f#

I'm trying to learn F# and have put together the following code:
open System.Collections.Generic
type Version = Version of int
type AggregateId = AggregateId of int
type IEventdata = interface end
type EventMeta = EventMeta of AggregateId * Version
type Event = Event of EventMeta * IEventdata
type ICommandData = interface end
type CommandMeta = CommandMeta of AggregateId * Version
type Command = CommandMeta * ICommandData
type EventStore = {
GetEvents : AggregateId -> Event seq;
Insert : Event seq -> unit
}
let commandDispatcherFactory (mapping: (Command -> Event seq -> Event seq), es: EventStore) =
(
fun (command: Command) ->
match command with
| (CommandMeta(aggregateId, version), commandData) ->
let events = es.GetEvents (aggregateId)
let resultingEvents = mapping command events
(es.Insert(resultingEvents))
)
type DummyCommand =
| Command1
interface ICommandData
type DummyCommand2 =
| Command2
interface ICommandData
type UnsupportedCommand =
| Command3
let dummyMapping = (fun ((command, commandData):Command) (events: Event seq) ->
match commandData with
| :? DummyCommand as dummyCommand ->
(printfn "Handling Command: %A" dummyCommand)
Seq.empty<Event>
| :? DummyCommand2 as dummyCommand ->
(printfn "Handling Command: %A" dummyCommand)
Seq.empty<Event>
| _ ->
(printfn "Unsupported command: %A" commandData)
Seq.empty<Event>
)
let dummyEs = {
GetEvents = (fun (aggregateId) -> Seq.empty<Event>);
Insert = (fun (events:Event seq) -> ())
}
dummyMapping (CommandMeta(AggregateId(1),Version(1)), Command1) Seq.empty<Event>
dummyMapping (CommandMeta(AggregateId(1),Version(1)), Command2) Seq.empty<Event> // Why does this work?
let commandDispatcher = commandDispatcherFactory(dummyMapping, dummyEs)
commandDispatcher (CommandMeta(AggregateId(1),Version(1)), Command1)
commandDispatcher (CommandMeta(AggregateId(1),Version(1)), Command2) // this doesn't work
I know it might be a wall of text, but I didn't manage to narrow it down. It's the last call to my commandDispatcher that doesn't work, and I can't figure out why. I know there probably are other things to do better, but that is something I'm working on :)

Here's a cut down repro:
let f x (y:System.IComparable) = ()
let g = f 1
g 3
g "test" // not okay
let g' : System.IComparable -> unit = f 1
g' 3
g' "test" // okay
let g'' x = f 1 x
g'' 3
g'' "test" // okay
What happens is that the compiler infers a generic type for g (g:('a -> unit) when 'a :> System.IComparable in the case of my example). However, g is a syntactic value and therefor can't be generic (see questions about the value restriction), so the compiler needs to pick a specific type. If you don't use g at all, you'll get a compiler error. If you use it at exactly one concrete type, the compiler will pick that type. If you use it at more than one type, the compiler will specialize based on the first type used and throw an error after that.
Two workarounds are to either annotate with a non-generic type or to eta-expand so that g isn't a syntactic value.

The compiler is specializing the type of commandDispatcher based on the initial usage. You can fix it with a cast:
commandDispatcher (CommandMeta(AggregateId(1),Version(1)), Command1 :> ICommandData)
commandDispatcher (CommandMeta(AggregateId(1),Version(1)), Command2) //works

Related

How to implement Task.Map

Have I correctly implemented map for Task?
let map continuation (t: Task<'A>) =
t.ContinueWith(fun (antecedent: Task<'A>) ->
if antecedent.Status <> TaskStatus.Canceled &&
antecedent.Status <> TaskStatus.Faulted then
continuation antecedent.Result
else
raise antecedent.Exception // must I?
)
I got the TaskStatus checks from the docs. I feel most uncertain about raise antecedent.Exception, but I can't think of another way to handle it.
As background, yes I'm aware of Async, but my current stack uses Entity Framework and Blazor, so I have a backend that uses things like .ToListAsync() and a front end in C#, so I'd rather just not deal with converting from Task to Async then back again.
I would suggest implementing your solution in terms of the interfaces behind the concept of awaitable in the TPL, namely INotifyCompletion and ICriticalNotifyCompletion. Also, to implement map correctly, you should really do it in terms of bind. This is something that there are already some existing solutions for in F#, such as the TaskBuilder library. Personally, I have been using the following in a library for years without any issues:
open System.Runtime.CompilerServices
open System.Threading.Tasks
type TaskStep<'result> =
| Value of 'result
| AsyncValue of 'result Task
| Continuation of ICriticalNotifyCompletion * (unit -> 'result TaskStep)
and StateMachine<'a>(firstStep) as this =
let methodBuilder = AsyncTaskMethodBuilder<'a Task>()
let mutable continuation = fun () -> firstStep
let nextAwaitable() =
try
match continuation() with
| Value r ->
methodBuilder.SetResult(Task.FromResult(r))
null
| AsyncValue t ->
methodBuilder.SetResult(t)
null
| Continuation (await, next) ->
continuation <- next
await
with
| exn ->
methodBuilder.SetException(exn)
null
let mutable self = this
member __.Run() =
methodBuilder.Start(&self)
methodBuilder.Task
interface IAsyncStateMachine with
member __.MoveNext() =
let mutable await = nextAwaitable()
if not (isNull await) then
methodBuilder.AwaitUnsafeOnCompleted(&await, &self)
member __.SetStateMachine(_) =
()
type Binder<'out> =
static member inline GenericAwait< ^abl, ^awt, ^inp
when ^abl : (member GetAwaiter : unit -> ^awt)
and ^awt :> ICriticalNotifyCompletion
and ^awt : (member get_IsCompleted : unit -> bool)
and ^awt : (member GetResult : unit -> ^inp) >
(abl : ^abl, continuation : ^inp -> 'out TaskStep) : 'out TaskStep =
let awt = (^abl : (member GetAwaiter : unit -> ^awt)(abl))
if (^awt : (member get_IsCompleted : unit -> bool)(awt))
then continuation (^awt : (member GetResult : unit -> ^inp)(awt))
else Continuation (awt, fun () -> continuation (^awt : (member GetResult : unit -> ^inp)(awt)))
module TaskStep =
let inline bind f step : TaskStep<'a> =
Binder<'a>.GenericAwait(step, f)
let inline toTask (step: TaskStep<'a>) =
try
match step with
| Value x -> Task.FromResult(x)
| AsyncValue t -> t
| Continuation _ as step -> StateMachine<'a>(step).Run().Unwrap()
with
| exn ->
let src = new TaskCompletionSource<_>()
src.SetException(exn)
src.Task
module Task =
let inline bind f task : Task<'a> =
TaskStep.bind f task |> TaskStep.toTask
let inline map f task : Task<'b> =
bind (f >> Value) task
FsToolkit.ErrorHandling implements it here. I'll paste the current version below as it's quite short. It uses the TaskBuilder library Aaron mentioned.
module Task =
let singleton value = value |> Task.FromResult
let bind (f : 'a -> Task<'b>) (x : Task<'a>) = task {
let! x = x
return! f x
}
let map f x = x |> bind (f >> singleton)
Additionally, FSharpPlus has an independent implementation of Task.map here.
Throwing the exception again in the continuation would make for an incorrect stack trace.
It's a mapping from 'A -> 'B, so it's probably best to lay it out explicitly.
let rec map (continuation: 'A -> 'B) (t: Task<'A>) =
let rec map_resolved (task: Task<'A>) =
match task.Status with
| TaskStatus.RanToCompletion -> Task.FromResult(continuation task.Result)
| TaskStatus.Faulted -> Task.FromException<'B>(task.Exception)
| TaskStatus.Canceled -> Task.FromCanceled<'B>(CancellationToken.None)
| _ -> task.ContinueWith(map_resolved).Unwrap()
map_resolved t

Why currying with obj doesn't work?

Can you explain why one works but not the other?
Given
//fu : unit -> unit
let fu() = ();;
This works
//exec : (unit -> unit) -> int -> unit
let exec (f:(unit -> unit)) (data:int) = f();;
//this works, and p : int -> unit
let p = exec fu;;
And it works for other types of data such as string, long, etc.
This doesn't work
//exec : (unit -> unit) -> obj -> unit
let exec (f:(unit -> unit)) (data:obj) = f();;
let p = exec fu;;
and I get the following error:
error FS0030: Value restriction. The value 'p' has been inferred to have generic type
val p : ('_a -> unit)
Either make the arguments to 'p' explicit or, if you do not intend for it to be generic, add a type annotation.
Notice the only difference between these cases is the type of the data parameter.
When it is obj or System.Object or 'a - it doesn't work.
Another thing is that if data has type obj then the following happens:
//Data type is obj
let exec (f:(unit -> unit)) (data:obj) = f();;
//specifying parameters explicitly
let p x = exec fu x;;
Now p has the signature of 'a -> unit, not obj -> unit.
So the question is: why the "shortcuted" currying doesn't work when data is obj or 'a and why the type of p is 'a -> unit when data was obj?
So I think the problem is that F# appears to be generalizing at the wrong point (from your point of view):
Here is a version of your code that at first glance shouldn't typecheck:
let exec (f:unit -> unit) (data:obj) = f();;
let p:int -> unit = exec (fun () -> ());;
This seems weird as int <> obj.
Also, here is an even simpler example which shows your behaviour (from the spec with modifications)
type Base() =
member b.X = 1
type Derived(i : int) =
inherit Base()
member d.Y = i
let exec (f:unit -> unit) (data:Base) = f();;
let p = exec (fun () -> ());;
which produces a value restriction error.
This makes it more clear that as F# inserts an implicit upcast before the function calls, the code is valid where it is a function, but once you make it an explicit value, this cast can't be used.
If you want your code to compile, you need to move the location of the upcast by adding a type annotation:
let exec (f:unit -> unit) (data:obj) = f()
let p:obj -> unit = exec (fun () -> ());;

Possible F# type inference limitation

I'm quite sure that I run into some kind of limitation, but I do not understand it:
type IRunner =
abstract member Run : (string -> 'a) -> 'a
type T() =
let run4 doFun = doFun "4"
let run5 doFun = doFun "5"
let parseInt s = System.Int32.Parse(s)
let parseFloat s = System.Double.Parse(s)
let doSomething () =
let i = parseInt |> run4
let f = parseFloat |> run4
f |> ignore
// Make it more generic ->
//let doSomething2 (runner:(string->'a)->'b) =
let doSomething2 runner =
// Error on the following lines with both declarations
let i = parseInt |> runner
let f = parseFloat |> runner
f |> ignore
// Want to do something like
let test () =
doSomething2 run4
doSomething2 run5
// Workaround
let workaround (runner:IRunner) =
let run f = runner.Run f
let i = parseInt |> run
let f = parseFloat |> run
f |> ignore
Can somebody bring some light over this? I did not find any related question, sorry if i duplicated something.
The problem is, if doSomething2 has type ((string->'a) -> 'b) -> unit, then 'a and 'b are fixed during each invocation of doSomething2, which isn't what you want - in your case 'a needs to treated as both int and float during a single invocation of doSomething2.
It seems like what you really want is more like: doSomething2 : (forall 'a. (string -> 'a) -> 'a) -> unit, but that kind of direct universal quantification doesn't exist in F#. As you've discovered, the way to work around this is to use a type with a generic method.
And even if F# did support forall types, as I mentioned in a comment inference still wouldn't be possible. Consider your doSomething2 function - we know that runner needs to be able to take an input of type string -> int to some output type and an input of type string -> float to some (possibly different) output type. Here are several different signatures for doSomething2 that all meet this requirement:
forall 'a. 'a -> 'a
forall 'a. (string -> 'a) -> 'a
forall 'a. 'a -> unit
Note that none of these types is more general than the others, they are all incompatible. In the first case, we could pass id to the function, in the second case, we could pass run4 to it, and in the third case, we could pass ignore to it (but none of those functions is compatible with the other possible signatures!).

How to Get the F# Name of a Module, Function, etc. From Quoted Expression Match

I continue to work on a printer for F# quoted expressions, it doesn't have to be perfect, but I'd like to see what is possible. The active patterns in Microsoft.FSharp.Quotations.Patterns and Microsoft.FSharp.Quotations.DerivedPatterns used for decomposing quoted expressions will typically provide MemberInfo instances when appropriate, these can be used to obtain the name of a property, function, etc. and their "declaring" type, such as a module or static class. The problem is, I only know how to obtain the CompiledName from these instances but I'd like the F# name. For example,
> <# List.mapi (fun i j -> i+j) [1;2;3] #> |> (function Call(_,mi,_) -> mi.DeclaringType.Name, mi.Name);;
val it : string * string = ("ListModule", "MapIndexed")
How can this match be rewritten to return ("List", "mapi")? Is it possible?
FYI, here is my final polished solution from Stringer Bell and pblasucci's help:
let moduleSourceName (declaringType:Type) =
FSharpEntity.FromType(declaringType).DisplayName
let methodSourceName (mi:MemberInfo) =
mi.GetCustomAttributes(true)
|> Array.tryPick
(function
| :? CompilationSourceNameAttribute as csna -> Some(csna)
| _ -> None)
|> (function | Some(csna) -> csna.SourceName | None -> mi.Name)
//usage:
let sourceNames =
<# List.mapi (fun i j -> i+j) [1;2;3] #>
|> (function Call(_,mi,_) -> mi.DeclaringType |> moduleSourceName, mi |> methodSourceName);
You can use F# powerpack for that purpose:
open Microsoft.FSharp.Metadata
...
| Call(_, mi, _) ->
let ty = Microsoft.FSharp.Metadata.FSharpEntity.FromType(mi.DeclaringType)
let name = ty.DisplayName // name is List
However, I don't think if it's possible to retrieve function name with powerpack.
Edit:
As hinted by pblasucci, you can use CompilationSourceName attribute for retrieving source name:
let infos = mi.DeclaringType.GetMember(mi.Name)
let att = infos.[0].GetCustomAttributes(true)
let fName =
(att.[1] :?> CompilationSourceNameAttribute).SourceName // fName is mapi

Working with Nullable<'T> in F#

I'm wondering what others have come up with for dealing with Nullable<'T> in F#. I want to use Nullable<'T> on data types so that serialization works properly (i.e., doesn't write out F# option type to XML). But, I don't want my code stuck dealing with the ugliness of dealing with Nullable<'T> directly. Any suggestions?
Is it better to use active patterns to match directly on Nullable, or just a converter to option and use Some/None matching?
Additionally, I'd love to hear ideas on dealing with nullable references in a nice manner too. If I use, say "string option", then I end up with the F# option type wrapping things. If I don't then I can't distinguish between truly optional strings and strings that shouldn't be null.
Any chance .NET 4 will take on an Option<'T> to help out? (If it's part of the BCL, then we might see better support for it...)
As active patterns as options plays nicely with pattern matching, but is seems by using active patterns (i.e. typeof and ??) your code will eat more ticks.
The base question is how you will deal with your nullable references?
In case your code is long chained computations it's nice to use monadic syntax:
type Maybe<'a> = (unit -> 'a option)
let succeed x : Maybe<'a> = fun () -> Some(x)
let fail : Maybe<'a> = fun () -> None
let run (a: Maybe<'a>) = a()
let bind p rest = match run p with None -> fail | Some r -> (rest r)
let delay f = fun () -> run (f ())
type MaybeBuilder() =
member this.Return(x) = succeed x
member this.Let(p,rest) = rest p
member this.Bind(p,rest) = bind p rest
member this.Delay(f) = delay f
let maybe = new MaybeBuilder()
let add (a:'a) (b:'a) =
maybe {
match TryGetNumericAssociation<'a>() with
| Some v -> return (v.Add(a,b))
| _ -> return! fail
}
let add3 (a:'a) (b:'a) (c:'a) =
maybe {
let! ab = add a b
let! abc = add ab c
return abc
}
> let r1 = add 1 2;;
val r1 : (unit -> int option)
> r1();;
val it : int option = Some 3
> let r2 = add "1" "2";;
val r2 : (unit -> string option)
> r2();;
val it : string option = None
> let r3 = add3 "one" "two" "three";;
val r3 : (unit -> string option)
> r3();;
val it : string option = None

Resources