-
-
Save 1tgr/1063484 to your computer and use it in GitHub Desktop.
| type OptionMonad = | OptionMonad with | |
| static member ret x = Some x | |
| static member bind (x, f) = Option.bind f x | |
| type ListMonad = | ListMonad with | |
| static member ret x = [x] | |
| static member bind (x, f) = List.collect f x | |
| let inline ret (monad : ^M) (x : 'a) : 'b = | |
| (^M : (static member ret : 'a -> 'b) (x)) | |
| let inline bind (monad : 'M) (x : 'a) (f : 'b) : 'd = | |
| (^M : (static member bind : 'a -> 'b -> 'd) (x, f)) | |
| let inline sequence (monad1 : ^M) (monad2 : ^M2) (ms : 'm1 list) : 'm2 = | |
| let mcons (p : 'm1) (q : 'm2) = | |
| bind monad1 p <| fun x -> | |
| bind monad2 q <| fun y -> | |
| ret monad2 (x :: y) | |
| List.foldBack mcons ms (ret monad2 []) | |
| let inline mapM monad1 monad2 f xs = | |
| sequence monad1 monad2 (List.map f xs) | |
| let test () = | |
| let f = mapM OptionMonad OptionMonad (fun x -> if x % 2 = 0 then Some (x / 2) else None) | |
| printfn "%A" (f [2; 4; 6]) | |
| printfn "%A" (f [1; 2; 3]) |
I think there are two monad types involved in 'sequence': one that yields a scalar when 'bind' is called, and one that yields a list. Both can be satisfied by the same F# type, but within sequence, 'bind' can't use one type on one line and a different type on another line.
I think we're doing what Haskell does it its dictionary of type classes. However, Haskell's dictionary is hidden from the programmer, whereas we must pass all necessary 'type class' implementations into each function.
Well, but the operator-based example works, does it not?
Somehow, with the operator-based bind F# first inlines, and then typechecks, working out two different types for two applications of bind. With the named function as above, it seems like it first to typecheck before inlining, or perhaps it 'optimizes' "bind monad" as a common expression, which leads it to incorrect type inference.
Yes with this encoding we need to pass the instances directly.
I can't work out the bind without errors either. Looks like it can't figure out that bind should be a generic method with two free parameters, nor there seems to be a way to tell it.