Skip to content

Instantly share code, notes, and snippets.

@1tgr
Created July 4, 2011 15:24
Show Gist options
  • Select an option

  • Save 1tgr/1063484 to your computer and use it in GitHub Desktop.

Select an option

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])
@t0yv0
Copy link
Copy Markdown

t0yv0 commented Jul 4, 2011

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.

@1tgr
Copy link
Copy Markdown
Author

1tgr commented Jul 4, 2011

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.

@t0yv0
Copy link
Copy Markdown

t0yv0 commented Jul 4, 2011

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment