Skip to content

Instantly share code, notes, and snippets.

@t0yv0
Created July 2, 2011 23:29
Show Gist options
  • Save t0yv0/1061780 to your computer and use it in GitHub Desktop.
Save t0yv0/1061780 to your computer and use it in GitHub Desktop.
Emulating higher kinds with F# inline functions.
(* F# lacks proper support for higher-kinded abstraction such as ML
functors or Haskell typeclasses. In particular it makes it seemingly
impossible to define functions that work for every type constructor.
The code below demonstrates that this functionality can be partially
recovered by using inline functions with static member constraints.
The approach requires explicitly passing typeclass instance values.
Error messages and derived types are horrible. Nonetheless, there is
some type safety - if the typeclass instances have been defined right,
type-unsafe code will be rejected by the typechecker, albeit with a
hairy message. Another disadvantage is the need to invent operators. *)
type ListFunctor = | ListFunctor with
static member ( >--> ) (ListFunctor, (f, x)) = List.map f x
type OptionFunctor = | OptionFunctor with
static member ( >--> ) (OptionFunctor, (f, x)) = Option.map f x
type OptionMonad = | OptionMonad with
static member (|!!|) (OptionMonad, x) = Some x
static member (|>>=|) (OptionMonad, (x, f)) = Option.bind f x
type ListMonad = | ListMonad with
static member (|!!|) (ListMonad, x) = [x]
static member (|>>=|) (ListMonad, (x, f)) = List.collect f x
let inline fmap def f x =
def >--> (f, x)
let inline ret monad x =
monad |!!| x
let inline bind monad x f =
monad |>>=| (x, f)
/// sequence : ('m :> Monad) -> List<'m<'a>> -> 'm<List<'a>>
let inline sequence monad ms =
let k e es =
bind monad e (fun x ->
bind monad es (fun xs ->
ret monad (x :: xs)))
List.foldBack k ms (ret monad [])
/// mapM : ('m :> Monad) -> ('a -> 'm<'b>) -> List<'a> -> 'm<List<'b>>
let inline mapM monad f xs =
sequence monad (List.map f xs)
let test () =
let f = mapM 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])
@psfblair
Copy link

I'm not well-versed in functional programming, so I may be way off-base here, but what about something like this:

  type Functor<'A, 'T> = abstract member Map : ('T -> 'U) -> Functor<'A, 'U>;;

  type ListFunctor<'T>(wrappedList: list<'T>) =     
     member this.Wrapped = wrappedList                    

     interface Functor<list<obj>, 'T> with                                      
        member this.Map (mappingFunction: 'T -> 'U) : Functor<list<obj>, 'U> =
           let result = List.map mappingFunction wrappedList                       
           new ListFunctor<'U>(result) :> Functor<list<obj>, 'U>

  type OptionFunctor<'T>(wrappedOption: option<'T>) =                                                
     member this.Wrapped = wrappedOption                    

     interface Functor<option<obj>, 'T> with                                      
        member this.Map (mappingFunction: 'T -> 'U) : Functor<option<obj>, 'U> =
           let result = Option.map mappingFunction wrappedOption                       
           new OptionFunctor<'U>(result) :> Functor<option<obj>, 'U>

  let fmap (f : 'T -> 'U) (x : Functor<'A, 'T>) : Functor<'A, 'U> = x.Map f


  let listFunctor = new ListFunctor<int>([1;2;3;4]) 
  let listFunctor2 = new ListFunctor<string>(["a";"b"])
  let optionFunctor = new OptionFunctor<string>(Some("body"))

  fmap ((+) 1) listFunctor
  // => val it : Functor<obj list,int> = FSI_0003+ListFunctor`1[System.Int32] {Wrapped = [2; 3; 4; 5];}

  fmap (fun (x:string) -> x.ToUpper()) listFunctor2
  // => val it : Functor<obj list,string> = FSI_0003+ListFunctor`1[System.String] {Wrapped = ["A"; "B"];}

  fmap (fun (x:string) -> x.ToUpper()) optionFunctor
  // => val it : Functor<obj option,string> = FSI_0003+OptionFunctor`1[System.String] {Wrapped = Some "BODY";}

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