F# higher-order functions on discrete unions -


i trying write rest client in f# uses strongly-typed values define resource "signatures". have decided use discrete unions represent signature list of optional arguments. planning on providing generic way convert parameter value list of tuples represent key/value pairs used create request. learning exercise trying use idomatic f#.

i've gotten stuck, trying define 2 different discrete unions have similar signatures. there way me dynamically select right pattern matching function @ runtime?

type =     | first of string     | second of string  type b =     | first of string     | second of string     | third of string  let createa f s =     [a.first f; a.second s;]  let createb f s t =     [b.first f; b.second s; b.third t]  let toparama elem =     match elem     | a.first f -> "first", f     | a.second s -> "second", s  let toparamb elem =     match elem     | b.first f -> "first", f     | b.second s -> "second", s     | b.third t -> "third", t  let rec toparam f state args =     match args     | [] -> state     | head::tail ->          let state' = (f head)::state         toparam f state' tail  let arga = createa "one" "two" let argb = createb "one" "two" "three"  let resulta = arga |> toparam toparama [] let resultb = argb |> toparam toparamb [] 

the result correct, i'm not happy api:

val resulta : (string * string) list = [("second", "two"); ("first", "one")] val resultb : (string * string) list = [("third", "three"); ("second", "two"); ("first", "one")] 

update:

the question asked call like?

let resulta = arga |> toparam [] 

then toparam figure out whether call toparama or toparamb.

i think i've realized original approach works fine current situation. however, i'd still interested know if pseudo-code possible.

i think vanilla f#-way explicitly state api method you're constructing parameter list:

type apiargs = apia of list | apib of b list 

and conflate toparama , toparamb functions this:

let toparam = function | apia args ->     let toparama = function     | a.first x -> "first", x     | a.second x -> "second", x      list.map toparama args  | apib args ->     let toparamb = function     | b.first x -> "first", x     | b.second x -> "second", x     | b.third x -> "third", x      list.map toparamb args  

i see 2 possibilities improvement here. first, code repetitive , boring. can generate code type provider api, or use reflection @ run time conversion.

second, polymorphic behavior of converting either a or b (string * string) list happens @ run time, think can pull off @ compile time:

type x = x     static member ($) (_, args : list) =         let toparama = function         | a.first x -> "first", x         | a.second x -> "second", x          list.map toparama args      static member ($) (_, args : b list) =         let toparamb = function         | b.first x -> "first", x         | b.second x -> "second", x         | b.third x -> "third", x          list.map toparamb args  let inline toparam' args = x $ args 

if inspect toparam''s inferred type, similar this:

val inline toparam' :   args: ^a ->  ^_arg3     when (x or  ^a) : (static member ( $ ) : x *  ^a ->  ^_arg3) 

(the ^a notation so-called "hat type", read more here)

and calling toparam' arguments of different type yield correct results:

> toparam' (createa "one" "two");; val : (string * string) list = [("first", "one"); ("second", "two")] > toparam' (createb "1" "2" "3");; val : (string * string) list =   [("first", "1"); ("second", "2"); ("third", "3")] > 

this technique described in detail in this blog, believe outdated way it. more inspiration, take @ these projects: fscontrol, fsharpplus, higher.


Popular posts from this blog