-
Notifications
You must be signed in to change notification settings - Fork 96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Juxt #486
Comments
Looks like a generalized version of open FSharpPlus.Operators.Arrows
let square : float -> float =
(id &&& id) >> uncurry ( * )
let avg : list<float> -> float =
(List.sum &&& (List.length >> float)) >> uncurry (/)
let grouped : (int * int) * int = ((item1 &&& item2) &&& item2) (1,2,3) We can't ungroup |
I've just got a working generic type Juxt =
static member inline Invoke (f: 'f, x) =
let inline call_2 (a: ^a, b: ^b) = ((^a or ^b): (static member Juxt:_*_*_->_) f,a,b)
let inline call (a: 'a, b: 'b) = call_2 (a, b)
call (x, Unchecked.defaultof<Juxt>)
static member inline Juxt (f: Tuple<_>, x, _: Juxt) = f.Item1 x
static member inline Juxt ((f1, f2), x, _: Juxt) = f1 x, f2 x
static member inline Juxt ((f1, f2, f3), x, _: Juxt) = f1 x, f2 x, f3 x
static member inline Juxt ((f1, f2, f3, f4), x, _: Juxt) = f1 x, f2 x, f3 x, f4 x
static member inline Juxt ((f1, f2, f3, f4, f5), x, _: Juxt) = f1 x, f2 x, f3 x, f4 x, f5 x
static member inline Juxt ((f1, f2, f3, f4, f5, f6), x, _: Juxt) = f1 x, f2 x, f3 x, f4 x, f5 x, f6 x
static member inline Juxt ((f1, f2, f3, f4, f5, f6, f7), x, _: Juxt) = f1 x, f2 x, f3 x, f4 x, f5 x, f6 x, f7 x
static member inline Juxt (f: 'f, x: 't, o: ^Juxt) =
let f1,f2,f3,f4,f5,f6,f7,frest : ('t->'u1)*('t->'u2)*('t->'u3)*('t->'u4)*('t->'u5)*('t->'u6)*('t->'u7)*'fr =
Constraints.whenNestedTuple f
let result =
Tuple<_,_,_,_,_,_,_,_>(
f1 x, f2 x, f3 x, f4 x, f5 x, f6 x, f7 x,
((^fr or ^Juxt): (static member Juxt: _*_*_->'ur) frest,x,o)
) |> retype
let _,_,_,_,_,_,_,_ : 'u1*'u2*'u3*'u4*'u5*'u6*'u7*'ur = Constraints.whenNestedTuple result
result
let inline juxt f x = Juxt.Invoke (f, x) which can be used like: let square : float -> float =
juxt (id, id) >> uncurry ( * )
let avg: list<float> -> float =
juxt (List.sum, List.length >> float) >> uncurry (/)
let grouped = juxt (juxt (item1, item2), item3) (1,2,3)
let ungrouped = juxt (item1 >> item1, item1 >> item2, item2) grouped
let test () =
let f1 x = x + 1
let f2 x = x > 0
let f3 x = (x, -x)
let f = (f1, f2, f3)
let x = juxt f 42
let g = f1, f2, f3, f1, f2, f3, f1, f2, f3
let y1,y2,y3,y4,y5,y6,y7,y8,y9 = juxt g 42
() I think we should be able to further generalize this to support Arrow. So the questions are:
What do you think? @gusty @wallymathieu |
Thank you so much, @cannorin! I absolutely love your generic version. I like the name |
I think it looks cool what you have done @cannorin ! 😄 I've not seen |
I think we can add it as |
There is already a 2-tuple version of FSharpPlus/src/FSharpPlus/Operators.fs Lines 440 to 446 in 879d652
and it supports
I guess Also, there is also a "reversed" version FSharpPlus/src/FSharpPlus/Operators.fs Lines 449 to 455 in 879d652
So I think we should probably add |
Yes, I agree in that Arrow should be supported, mainly for consistency. |
While browsing the F#+ docs, I found a few more Arrow functions that could be genericized as well. Namely, What's really interesting is that all of these functions can be derived from let square x = x * x
let double x = x + x
// Parallel, first, and second
(double *** square) (3,3) // (6, 9)
first double (3,3) // (6, 3)
second double (3,3) // (3, 6)
// Parallel, first, and second expressed as fanouts
(double *** square) (3,3) = ((item1 >> double) &&& (item2 >> square)) (3, 3)
first double (3,3) = ((item1 >> double) &&& item2) (3, 3)
second double (3,3) = (item1 &&& (item2 >> double)) (3, 3)
// Dup and swap, too
(id &&& id) 5 // (5, 5)
(item2 &&& item1) (1, 2) // (2, 1) Also, I'm trying to figure out what |
I've come up with an example of #r "nuget: FSharpPlus, 1.2.4"
open FSharpPlus
open FSharpPlus.Operators.Arrows
let fanout3 (f1, f2, f3) x = f1 x, f2 x, f3 x
let parallel3 (f, g, h) (a,b,c) = f a, g b, h c
let toList2 (a,b) = [a;b]
let toList3 (a,b,c) = [a;b;c]
// Source: https://github.com/python/cpython/blob/main/Lib/colorsys.py
let rgb_to_yiq (r, g, b) =
let y = 0.30 * r + 0.59 * g + 0.11 * b
let i = 0.74 * (r - y) - 0.27 * (b - y)
let q = 0.48 * (r - y) + 0.41 * (b - y)
(y, i, q)
let rgb_to_yiq' =
let calcY = toList3 >> List.map2 ( * ) [0.30; 0.59; 0.11] >> sum
let calcI = toList2 >> List.map2 ( * ) [0.74; 0.27] >> List.reduce (-)
let calcQ = toList2 >> List.map2 ( * ) [0.48; 0.41] >> sum
fanout3 (calcY, item1, item3)
>> fanout3 (item1, (item2 &&& item1) >> uncurry (-), (item3 &&& item1) >> uncurry (-))
>> fanout3 (item1, (item2 &&& item3), (item2 &&& item3))
>> parallel3 (id, calcI, calcQ)
rgb_to_yiq(0.2, 0.6, 0.8) = rgb_to_yiq'(0.2, 0.6, 0.8) // true If anything, this can be seen as an argument against adding these functions to F#+! But it does show that these functions can get you through some tricky point-free situations. |
Hello and thank you for creating F#+! I'd like to propose adding juxtapose functions to the Operators module. These functions are defined as follows:
This function is called
juxt
in Clojure and the Python toolz library. I believe this function was first introduced by John Backus in his paper called Can programming be liberated from the von Neumann style? where it was calledconstruction
.In Haskell and F#+ there's a similar function called
sequence
. Unfortunately, it returns a list and not a tuple, forcing all the result types to be the same.juxt
in combination withitem
anduncurry
/uncurryN
make point-free programming with tuples easier. Here are some examples:Please let me know if this function already exists in F#+ (or F# for that matter) and I missed it. Thank you!
The text was updated successfully, but these errors were encountered: