5 ms·
This comes for free from Haskell's automatic partial application (that arises from Haskell's pervasive use of function currying). So several different Haskell v
by dwohnitmok 6y ago
This comes for free from Haskell's automatic partial application (that arises from Haskell's pervasive use of function currying). So several different Haskell variants of Clojure's `compose` correspond to different versions of Clojure's threading macros.
Clojure's threading macros arise because it's a little bit more awkward in Clojure to partially apply functions (either anonymous functions with # or use `partial`) and it reads more fluently to just use threading macros there.
However, Clojure can do fancier things with variants of the common threading macros (https://github.com/rplevy/swiss-arrows https://github.com/rplevy/swiss-arrows)
- thom 6y agoWhat's the syntax in Haskell for thread-first (-> in Clojure) which inserts the result of the previous operation as the first argument, versus thread-last (->>) which inserts it at the end, so is more amenable to composed curried functions?
- dwohnitmok 6y agoAh I was wrong! That's what I get for not coding in Haskell in a year and not checking my answer with GHC. Function application always has higher precedence than infix operators so I can't emulate thread-first with functions in Haskell. If that wasn't the case, this would work (this was what I was thinking of) (|*>) :: b -> (a -> b -> c) -> (a -> c) (|*>) x f = (\y -> f y x) xs = 5 |*> take [1, 2, 3] Otherwise, I can come close with this. import Data.Function ((&)) infix 9 *- (*-) :: (a -> b -> c) -> b -> (a -> c) (*-) f x = (\y -> f y x) xs :: [Int] xs = 5 & take *- [1, 2, 3] & filter (/= 1) & head & take *- [1, 2, 3]
- thewakalix 6y agoUsing right-associativity of function types, and by sugaring the lambda into another function argument, we can rewrite this as (*-) :: (a -> b -> c) -> b -> a -> c (*-) f x y = f y x This reveals that (*-) is simply flip, which is in the Prelude (re-exported from Data.Function). Alternatively, Hoogling the original type signature will reveal the same thing. So the example doesn't require any new functions: import Data.Function ((&)) xs :: [Int] xs = 5 & flip take [1, 2, 3] & filter (/= 1) & head & flip take [1, 2, 3] In general, it's common in Haskell to use flip, curry/uncurry, and other higher-level functions to manipulate functions so they fit into the needed context.
- eru 6y agoIf you don't like flip, you could also do xs :: [Int] xs = 5 & (`take` [1, 2, 3]) & filter (/= 1) & head & (`take` [1, 2, 3]) But I'm not sure that's a better style than flip. I really like `on` from Data.Function, too.
- johnday 6y agoFor maximum readability I'd be tempted to write the lambdas explicitly. xs = 5 & (\n -> take n [1, 2, 3]) & filter (/= 1) & head & (\n -> take n [1, 2, 3])
- eru 6y agoThat might be a wise choice. It depends on what you are used to and your tolerance thresholds. In a code base I worked on professionally, I came across the following idiom f a b `flip` d which at first used to confuse me to no end. Later I figured out that you are supposed to read it as: f a b <place-holder> d and thus \c -> f a b c d In the same vein that Scala uses an underscore in somewhat implicit anonymous function syntax. I still wasn't really happy with that usage, but at least I see why someone thought it would make sense. (Mostly I wasn't so happy because it only works for the penultimate argument. If the syntax had worked out so that f a `flip` c d would mean \b -> f a b c d I would have been more amenable to the idiom. But you need the ugly f a `flip` c `flip` d for that case. And that's clearly worse than the lambda syntax.
- johnday 6y agoOh good lord, that's horrible!
- truculent 6y agoI think the Prelude answer would be to use `flip`[1], along with composition (`.`) or application (`$`) as usual. flip :: (a -> b -> c) -> b -> a -> c [1]: https://hackage.haskell.org/package/base-4.14.0.0/docs/Prelude.html#v:flip https://hackage.haskell.org/package/base-4.14.0.0/docs/Prelu...