Re:A philosophical difference between Haskell and Lisp (2015)
by thom on Sunday August 02, 2020 @06:11PM
(#24032223)
What'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?
[ Parent
| on HN ]
Re:A philosophical difference between Haskell and Lisp (2015)
by dwohnitmok on Sunday August 02, 2020 @06:44PM
(#24032441)
Ah 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]
Re:A philosophical difference between Haskell and Lisp (2015)
by thewakalix on Sunday August 02, 2020 @09:11PM
(#24033204)
Using 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.
Re:A philosophical difference between Haskell and Lisp (2015)
by eru on Monday August 03, 2020 @12:03AM
(#24034116)
If 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.
Re:A philosophical difference between Haskell and Lisp (2015)
by johnday on Monday August 03, 2020 @05:51AM
(#24035811)
For 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])
Re:A philosophical difference between Haskell and Lisp (2015)
by eru on Monday August 03, 2020 @11:35PM
(#24046287)
That 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.
Re:A philosophical difference between Haskell and Lisp (2015)
by johnday on Sunday August 09, 2020 @01:40PM
(#24101292)
Oh good lord, that's horrible!
Re:A philosophical difference between Haskell and Lisp (2015)
by truculent on Monday August 03, 2020 @06:04AM
(#24035904)
I 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/Prelu...