Last active
April 18, 2020 19:10
-
-
Save SchlenkR/2f338c31ec644f9f1d0627d4b7ac3817 to your computer and use it in GitHub Desktop.
CurryN: An example of how to deal with generic n argument functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It’s from FSharpPlus