question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

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:

let juxt2 f g x = f x, g x
let juxt3 f g h x = f x, g x, h x
let juxt4 f g h i x = f x, g x, h x, i x
// etc

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 called construction.

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 with item and uncurry/uncurryN make point-free programming with tuples easier. Here are some examples:

open FSharpPlus

// Basic examples
let square: float -> float =
    juxt2 id id >> uncurry ( * )

let avg: list<float> -> float =
    juxt2 List.sum (List.length >> float) >> uncurry (/)

// Grouping and ungrouping tuples
let ``(a,b),c``   (a,b,c)   = (a,b),c
let ``~(a,b),c~`` ((a,b),c) = a,b,c

``(a,b),c``(1,2,3)     = (juxt2 (juxt2 item1 item2) item3)(1,2,3) // ((1, 2), 3)
``~(a,b),c~``((1,2),3) = (juxt3 (item1 >> item1) (item1 >> item2) item2)((1,2),3) // (1, 2, 3)

Please let me know if this function already exists in F#+ (or F# for that matter) and I missed it. Thank you!

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:9 (6 by maintainers)

github_iconTop GitHub Comments

3reactions
cannorincommented, Aug 4, 2022

I’ve just got a working generic juxt:

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:

  • do we really need this in F#+?
  • do we generalize this to a single juxt or just make juxt2, juxt3, juxt4, …?
  • do we extend this to support Arrow<'T, 'U> or should we only support 'T -> 'U?
  • what the name of this function should be?
    • juxt?
      • I don’t think this name is popular among the users of other functional languages than clojure or python
    • fanoutN?
      • I have a feeling that if we call this fanoutN we should have faninN, leftN, rightN too, which would require 4X 2X effort

What do you think? @gusty @wallymathieu

2reactions
gustycommented, Aug 4, 2022

Yes, I agree in that Arrow should be supported, mainly for consistency.

Read more comments on GitHub >

github_iconTop Results From Across the Web

JUXT TAPHOUSE
119 W. Holly St. Bellingham, WA 98225 · M | W | TH 12 PM - 11 PM · FRI | SAT 12...
Read more >
Juxt - Luxury Apartments in Seattle, WA
With a range of spacious floor plans including studio, 1-bedroom, 2-bedroom, and 3-bedroom apartments, and a top-notch amenity package, JUXT offers homes ...
Read more >
JUXT
We are a unique software engineering firm, with a track record of rapidly delivering high-quality IT systems to meet challenging business objectives.
Read more >
JUXT Taphouse | Bellingham WA
JUXT Taphouse, Bellingham, Washington. 1024 likes · 29 talking about this · 484 were here. Locally Owned Taphouse w/ Craft Beer, Cocktails, and...
Read more >
JUXT
JUXT is a creative innovation agency focused on inventing digital solutions that are practical, sophisticated, and beautiful. We have a fierce determination ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found