Skip to content

Instantly share code, notes, and snippets.

@SchlenkR
Last active April 18, 2020 19:10
Show Gist options
  • Save SchlenkR/2f338c31ec644f9f1d0627d4b7ac3817 to your computer and use it in GitHub Desktop.
Save SchlenkR/2f338c31ec644f9f1d0627d4b7ac3817 to your computer and use it in GitHub Desktop.
CurryN: An example of how to deal with generic n argument functions
module CurryN =
open System
module Constraints =
/// Constrain 't to be a nested tuple of <'t1,'t2,'t3,'t4,'t5,'t6,'t7,'tr>
let inline whenNestedTuple (t: 't) =
(^t: (member Item1: 't1) t), (^t: (member Item2: 't2) t), (^t: (member Item3: 't3) t), (^t: (member Item4: 't4) t), (^t: (member Item5: 't5) t), (^t: (member Item6: 't6) t), (^t: (member Item7: 't7) t), (^t: (member Rest: 'tr) t)
#nowarn "0042" // retype
let inline retype (x: 'T) : 'U = (# "" x: 'U #)
type Curry =
static member inline Invoke f =
let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member Curry: _*_ -> _) b, a)
call_2 (Unchecked.defaultof<Curry>, Unchecked.defaultof<'t>) (f: 't -> 'r) : 'args
static member inline Curry (t: 't, _: Curry) = fun f t1 t2 t3 t4 t5 t6 t7 ->
Curry.Invoke (fun tr ->
let _f _ = Constraints.whenNestedTuple t : ('t1*'t2*'t3*'t4*'t5*'t6*'t7*'tr)
f (Tuple<'t1,'t2,'t3,'t4,'t5,'t6,'t7,'tr> (t1, t2, t3, t4, t5, t6, t7, tr) |> retype))
static member Curry (_: Tuple<'t1> , _: Curry) = fun f t1 -> f (Tuple<_> t1)
static member Curry ((_, _) , _: Curry) = fun f t1 t2 -> f (t1, t2)
static member Curry ((_, _, _) , _: Curry) = fun f t1 t2 t3 -> f (t1, t2, t3)
static member Curry ((_, _, _, _) , _: Curry) = fun f t1 t2 t3 t4 -> f (t1, t2, t3, t4)
static member Curry ((_, _, _, _, _) , _: Curry) = fun f t1 t2 t3 t4 t5 -> f (t1, t2, t3, t4, t5)
static member Curry ((_, _, _, _, _, _) , _: Curry) = fun f t1 t2 t3 t4 t5 t6 -> f (t1, t2, t3, t4, t5, t6)
static member Curry ((_, _, _, _, _, _, _), _: Curry) = fun f t1 t2 t3 t4 t5 t6 t7 -> f (t1, t2, t3, t4, t5, t6, t7)
type Uncurry =
static member inline Invoke f t =
let inline call_2 (a: ^a, b: ^b) = ((^a or ^b) : (static member Uncurry: _*_ -> _) b, a) f
call_2 (Unchecked.defaultof<Uncurry>, t) : 'r
static member inline Uncurry (t: 't, _: Uncurry) = fun f ->
let (tr: 'tr) = (^t : (member Rest : 'tr) t)
let (t7: 't7) = (^t : (member Item7 : 't7) t)
let (t6: 't6) = (^t : (member Item6 : 't6) t)
let (t5: 't5) = (^t : (member Item5 : 't5) t)
let (t4: 't4) = (^t : (member Item4 : 't4) t)
let (t3: 't3) = (^t : (member Item3 : 't3) t)
let (t2: 't2) = (^t : (member Item2 : 't2) t)
let (t1: 't1) = (^t : (member Item1 : 't1) t)
Uncurry.Invoke (f t1 t2 t3 t4 t5 t6 t7) tr
static member Uncurry (x: Tuple<'t1> , _: Uncurry) = fun f -> f x.Item1
static member Uncurry ((t1, t2) , _: Uncurry) = fun f -> f t1 t2
static member Uncurry ((t1, t2, t3) , _: Uncurry) = fun f -> f t1 t2 t3
static member Uncurry ((t1, t2, t3, t4) , _: Uncurry) = fun f -> f t1 t2 t3 t4
static member Uncurry ((t1, t2, t3, t4, t5) , _: Uncurry) = fun f -> f t1 t2 t3 t4 t5
static member Uncurry ((t1, t2, t3, t4, t5, t6) , _: Uncurry) = fun f -> f t1 t2 t3 t4 t5 t6
static member Uncurry ((t1, t2, t3, t4, t5, t6, t7), _: Uncurry) = fun f -> f t1 t2 t3 t4 t5 t6 t7
/// Takes a function expecting a tuple of any N number of elements and returns a function expecting N curried arguments.
let inline curryN (f: (^``T1 * ^T2 * ... * ^Tn``) -> 'Result) : 'T1 -> '``T2 -> ... -> 'Tn -> 'Result`` = fun t -> Curry.Invoke f t
/// Takes a function expecting any N number of curried arguments and returns a function expecting a tuple of N elements.
let inline uncurryN (f: 'T1 -> '``T2 -> ... -> 'Tn -> 'Result``) (t: (^``T1 * ^T2 * ... * ^Tn``)) = Uncurry.Invoke f t : 'Result
module Test =
module CurryNTest =
let uncurriedF (a: int, b: int, c: int, d: int) = a - b - c - d
let curriedF = CurryN.curryN uncurriedF
assert (curriedF 100 20 5 2 = 73)
module UnurryNTest =
let curriedF a b c d = a - b - c - d
// only works only a) with type annotations of b) when applied immediately
// let uncurriedF1 = CurryN.uncurryN curriedF
// let uncurriedF2 = CurryN.uncurryN curriedF (100, 20, 5, 2)
let uncurriedF3 : (int * int * int * int -> int) = CurryN.uncurryN curriedF
assert (uncurriedF3(100, 20, 5, 2) = 73)
@SchlenkR
Copy link
Author

It’s from FSharpPlus

@SchlenkR
Copy link
Author

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