
On 02/22/2016 03:40 AM, Dennis Raddle wrote:
...So I had to write something like this.
compute :: [Int] -> [Int] -> [Int] -> [(Int,Int)]
func :: [Int] -> [(Int,Int)] func xs = compute xs (filter isSmall xs) (filter isLarge xs)
but I could also write
compute :: ([Int],([Int],[Int])) -> [(Int,Int)]
func = compute . (id &&& filter isSmall &&& filter isLarge)
So I had to change the inputs to 'compute' into this kind of awkward tuple form, but I eliminated four 'xs', plus eliminated the need to come up with the name 'xs'.
Can I get a comment on whether this makes sense as way to do things?
It's pushing it. Once you've seen the trick with (&&&) it's tempting to use it. When I see (id &&& f &&& g), in my head I get a picture of some argument coming in and being split along three wires that get fed into id, f, and g simultaneously. The output from those three black-boxes then get fed into compute: ----f---- / \ compute <------g------<-- xs \ / ----id--- So while details with the nested tuple are ugly, what will happen is pretty clear to me. But how long did you have to play with the arrow combinators to make this work? If you-two-weeks-ago were to see that line, compute :: ([Int],([Int],[Int])) -> [(Int,Int)] func = compute . (id &&& filter isSmall &&& filter isLarge) how long would it have taken you to figure out what it did? How does it impact your documentation for the "compute" function? I imagine it was something like "takes a list of Ints, the small half of that list, and the large half of that list, and then computes something cool." Now it will be "takes a pair whose first component is a list of Ints, and the second component is a pair whose first component is a list of the small half of the list in the first component of the big pair, and..." It's hard to explain because it's weird to think about. In this case, the second and third arguments to compute can be... computed... from the first, so in practice I would do something like, compute_stuff :: [Int] -> [(Int,Int)] compute_stuff xs = compute xs small_xs big_xs where small_xs = filter isSmall xs big_xs = filter isLarge xs compute :: [Int] -> [Int] -> [Int] -> [(Int,Int)] compute = -- whatever it's supposed to do (Unrelated: it might make more sense to loop through "xs" once and return a pair of the small/large elements. That way you don't have to make two passes. Depends on the size of the list.) If you're just code golfing, you don't need to change the signature of the compute function. Hitting it with "uncurry" once makes it take a tuple, and hitting it with (uncurry . uncurry) makes it take a tuple whose first component is a tuple: -- oh god what have i done func :: [Int] -> [(Int, Int)] func = ((uncurry . uncurry) compute . ((id &&& filter isSmall) &&& filter isLarge))