问题
Given the following type and member function
type Result<'TSuccess, 'TError> =
| Success of 'TSuccess
| Error of 'TError list
with
member this.apply fn =
match (fn, this) with
| Success(f), Success(x) -> Success(f x)
| Error(e), Success(_) -> Error(e)
| Success(_), Error(e) -> Error(e)
| Error(e1), Error(e2) -> Error(List.concat [e1;e2])
and the following inline function
let inline (<*>) (f: ^A) (t:^A) =
let apply' = (^A : (member apply : ^A -> ^A) (t, f))
apply'
And this call site
let y () = Success (fun x -> x + 1) <*> (Success 3)
I get the following error
let y () = Success (fun x -> x + 1) <*> (Success 3);;
-----------^^^^^^^^^^^^^^^^^^^^^^^^
/Users/robkuz/stdin(473,12): error FS0001: Type constraint mismatch.
The type
Result<'a,'c>
is not compatible with type
Result<('a -> 'b),'c>
The resulting type would be infinite when unifying ''a' and ''a -> 'b'
This whole thing is an attempt to emulate Haskells Applicative and the signature should be
(<*>) :: forall f a b. Apply f => f (a -> b) -> f a -> f b
But I dont afaik there is no way to express that in F#
Any ideas on how to make that happen?
回答1:
In general, I think it is not a good idea to try to emulate Haskell patterns in F#. In Haskell, lot of code is written as very generic, because monads and applicatives are used more frequently.
In F#, I prefer writing more specialized code, because you do not need to write code polymorphic over monads or applicatives and it just makes it easier to see what is going on. So, I do not think I would ever want to write <*>
operator that works over any "applicative" in practice in F#.
That said, the issue with your code is that the <*>
operator uses the same ^A
type parameter for both arguments and the result (while they are different types in the call) - if you use three separate type parameters it works fine:
let inline (<*>) (f: ^B) (t:^A) : ^C =
let apply' = (^A : (member apply : ^B -> ^C) (t, f))
apply'
来源:https://stackoverflow.com/questions/37949413/how-to-do-a-applicative-in-f