Skip to content

Instantly share code, notes, and snippets.

@gusty
Created July 9, 2019 22:13
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gusty/47b53b21548304730341fe4799a1f050 to your computer and use it in GitHub Desktop.
Save gusty/47b53b21548304730341fe4799a1f050 to your computer and use it in GitHub Desktop.
Polyvariadic curry / uncurry
#nowarn "0042"
let inline retype (x: 'T) : 'U = (# "" x: 'U #)
open System
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 (t1: 't1) = if false then (^t : (member Item1 : 't1) t) else t1
let (t2: 't2) = if false then (^t : (member Item2 : 't2) t) else t2
let (t3: 't3) = if false then (^t : (member Item3 : 't3) t) else t3
let (t4: 't4) = if false then (^t : (member Item4 : 't4) t) else t4
let (t5: 't5) = if false then (^t : (member Item5 : 't5) t) else t5
let (t6: 't6) = if false then (^t : (member Item6 : 't6) t) else t6
let (t7: 't7) = if false then (^t : (member Item7 : 't7) t) else t7
let (tr: 'tr) = if false then (^t : (member Rest : 'tr) t) else tr
f (Tuple<_,_,_,_,_,_,_,_>(t1, t2, t3, t4, t5, t6, t7, tr) |> retype))
static member Curry (x: Tuple<'t1> , _: Curry) = fun f t1 -> f (Tuple<_> t1)
static member Curry ((t1, t2) , _: Curry) = fun f t1 t2 -> f (t1, t2)
static member Curry ((t1, t2, t3) , _: Curry) = fun f t1 t2 t3 -> f (t1, t2, t3)
static member Curry ((t1, t2, t3, t4) , _: Curry) = fun f t1 t2 t3 t4 -> f (t1, t2, t3, t4)
static member Curry ((t1, t2, t3, t4, t5) , _: Curry) = fun f t1 t2 t3 t4 t5 -> f (t1, t2, t3, t4, t5)
static member Curry ((t1, t2, t3, t4, t5, t6) , _: Curry) = fun f t1 t2 t3 t4 t5 t6 -> f (t1, t2, t3, t4, t5, t6)
static member Curry ((t1, t2, t3, t4, t5, t6, t7), _: Curry) = fun f t1 t2 t3 t4 t5 t6 t7 -> f (t1, t2, t3, t4, t5, t6, t7)
let inline curry f t = Curry.Invoke f t
///////////
// TESTS //
///////////
let f2 (x, y) = [x + y]
let f3 (x, y, z) = [x + y + z]
let f7 (t1, t2, t3, t4, t5, t6, t7) = [t1+t2+t3+t4+t5+t6+t7]
let f8 (t1, t2, t3, t4, t5, t6, t7: float, t8: char) = [t1+t2+t3+t4+t5+t6+ int t7 + int t8]
let f9 (t1, t2, t3, t4, t5, t6, t7: float, t8: char, t9: decimal) = [t1+t2+t3+t4+t5+t6+ int t7 + int t8+ int t9]
let f15 (t1, t2, t3, t4, t5, t6, t7: float, t8: char, t9: decimal, t10, t11, t12, t13, t14, t15) = [t1+t2+t3+t4+t5+t6+ int t7 + int t8+ int t9+t10+t11+t12+t13+t14+t15]
let f16 (t1, t2, t3, t4, t5, t6, t7: float, t8: char, t9: decimal, t10, t11, t12, t13, t14, t15, t16) = [t1+t2+t3+t4+t5+t6+ int t7 + int t8+ int t9+t10+t11+t12+t13+t14+t15+t16]
let f17 (t1, t2, t3, t4, t5, t6, t7: float, t8: char, t9: decimal, t10, t11, t12, t13, t14, t15, t16, t17) = [t1+t2+t3+t4+t5+t6+ int t7 + int t8+ int t9+t10+t11+t12+t13+t14+t15+t16+t17]
let f1 (x:Tuple<_>) = [x.Item1]
let x2 = curry f2 1 2
let x3 = curry f3 1 2 3
let x7 = curry f7 1 2 3 4 5 6 7
let x8 = curry f8 1 2 3 4 5 6 7. '8'
let x9 = curry f9 1 2 3 4 5 6 7. '8' 9M
let x15 = curry f15 1 2 3 4 5 6 7. '8' 9M 10 11 12 13 14 15
let x16 = curry f16 1 2 3 4 5 6 7. '8' 9M 10 11 12 13 14 15 16
let x17 = curry f17 1 2 3 4 5 6 7. '8' 9M 10 11 12 13 14 15 16 17
let x1 = curry f1 100
open System
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 (t1: 't1) = (^t : (member Item1 : 't1) t)
let (t2: 't2) = (^t : (member Item2 : 't2) t)
let (t3: 't3) = (^t : (member Item3 : 't3) t)
let (t4: 't4) = (^t : (member Item4 : 't4) t)
let (t5: 't5) = (^t : (member Item5 : 't5) t)
let (t6: 't6) = (^t : (member Item6 : 't6) t)
let (t7: 't7) = (^t : (member Item7 : 't7) t)
let (tr: 'tr) = (^t : (member Rest : 'tr) 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
let inline uncurry f t = Uncurry.Invoke f t
///////////
// TESTS //
///////////
let f2 x y = [x + y]
let f3 x y z = [x + y + z]
let f7 a b c d e f g = [a + b + c + d + e + f + g]
let f8 t1 t2 t3 t4 t5 t6 (t7: float) (t8: char) = [t1+t2+t3+t4+t5+t6+ int t7 + int t8]
let f9 t1 t2 t3 t4 t5 t6 (t7: float) (t8: char) (t9: decimal) = [t1+t2+t3+t4+t5+t6+ int t7 + int t8+ int t9]
let f12 t1 t2 t3 t4 t5 t6 (t7: float) (t8: char) (t9: decimal) t10 t11 t12 = [t1+t2+t3+t4+t5+t6+ int t7 + int t8+ int t9+t10+t11+t12]
let f15 t1 t2 t3 t4 t5 t6 (t7: float) (t8: char) (t9: decimal) t10 t11 t12 t13 t14 t15 = [t1+t2+t3+t4+t5+t6+ int t7 + int t8+ int t9+t10+t11+t12+t13+t14+t15]
let f16 t1 t2 t3 t4 t5 t6 (t7: float) (t8: char) (t9: decimal) t10 t11 t12 t13 t14 t15 t16 = [t1+t2+t3+t4+t5+t6+ int t7 + int t8+ int t9+t10+t11+t12+t13+t14+t15+t16]
let x2 = uncurry f2 (1, 2)
let x3 = uncurry f3 (1, 2, 3)
let x7 = uncurry f7 (1, 2, 3, 4, 5, 6, 7)
let x8 = uncurry f8 (1, 2, 3, 4, 5, 6, 7. , '8')
let x9 = uncurry f9 (1, 2, 3, 4, 5, 6, 7. , '8', 9M)
let x12 = uncurry f12 (1, 2, 3, 4, 5, 6, 7. , '8', 9M, 10 , 11, 12)
let x15 = uncurry f15 (1, 2, 3, 4, 5, 6, 7. , '8', 9M, 10 , 11, 12, 13, 14, 15)
let x16 = uncurry f16 (1, 2, 3, 4, 5, 6, 7. , '8', 9M, 10 , 11, 12, 13, 14, 15, 16)
let x1 = uncurry id (Tuple<_> 1)
@abelbraaksma
Copy link

Cool idea! This could be useful in many scenarios, but hard to grasp, maybe you could write a blog post about this technique?

And I'm curious, why are the repeated if false required? Fooling the compiler?

@gusty
Copy link
Author

gusty commented Jul 11, 2019

I owe the community long time a blog post about all these polyvariadic functions.

The if false statements are a reflection of my laziness to write by hand (at least try to, it might not be possible) the whole signature with all the constraints.

For instance this let (t1: 't1) = if false then (^t : (member Item1 : 't1) t) else t1 pretends that it will extract the item1 from the unused parameter, but it won't, because it's unused and because otherwise it will throw a null exception, the goal is to drive type inference to add the Item1 constraint to the signature, though in this case requiring Rest alone might be good enough, not sure, because it also makes the "link" between the constraints and the 't1 type. But of course, there should be a way to write all this directly in the signature.

PS: planning to add this to F#+ -> fsprojects/FSharpPlus#155

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