Issue utilising Free.fold
See original GitHub issueDescription
I’m trying to use Free.fold
to write an interpreter for my free monad, but I’m struggling to satisfy the type checker with my implementation of the mapping function f
. It’s entirely possible that there is a problem with my code, but oddly I can satisfy the compiler (and run the code without errors) if I copy the definition of Free.fold
into my project and remove all type hints from the signature.
Repro steps
I have a DSL and associated workflow like so:
open FSharpPlus
open FSharpPlus.Data
type FooId = FooId of string
type Foo = { Id: FooId }
module GetFoo =
type Error = NotFound
type Instruction<'next> =
| Read of (FooId * (Foo option -> 'next))
static member Map(instruction, f: 'a -> 'b) =
match instruction with
| Read (id, next) -> Read(id, next >> f)
type Program<'a> = Free<Instruction<'a>, 'a>
let read fooId = Read(fooId, id) |> Free.liftF
type Request = { Id: FooId }
type Response = Result<Foo, Error>
let handle request: Program<Response> =
monad {
let! foo = read request.Id
return foo |> Option.toResultWith NotFound
}
Then, to keep things simple, in my tests I would like to write an interpreter which targets the identity monad. So I have written:
let interpreter: Program<Response> -> Response =
Free.fold (function
| Read (fooId, next) -> monad { return Some({Id = fooId}) |> next })
>> Identity.run
Expected behavior
I would expect this to compile
Actual behavior
But it produces the following compiler error:
Type mismatch. Expecting a
'Program<Response> -> 'a'
but given a
'Free<Instruction<Response>,Response> -> 'b'
Type mismatch. Expecting a
'Instruction<Response>'
but given a
'Instruction<Free<Instruction<Response>,Response>>'
The type 'Response' does not match the type 'Free<Instruction<Response>,Response>'
Am I missing a type hint somewhere, or is my implementation of the mapping function from Instruction
to Identity
monad fundamentally wrong somehow?
Known workarounds
Currently I can get this to compile by redefining fold
without the type hints in the signature:
open FSharpPlus
open FSharpPlus.Data
[<RequireQualifiedAccess>]
module Free =
let inline fold2 f x =
let rec loop f x =
match Free.run x with
| Pure a -> monad { return a }
| Roll x -> f x >>= loop f
loop f x
And then switching the above implementation of the interpreter to use Free.fold2
.
Related information
- Operating system - Mac OSX
- FSharpPlus Version - 1.1.5
- .NET Runtime - .NET 5.0
- F# lang version - 5
Issue Analytics
- State:
- Created 3 years ago
- Comments:10 (10 by maintainers)
Top GitHub Comments
@gusty thanks for the tip regarding
result x
instead ofmonad { return x }
.Fixed by #406