Haskell: How do I compose functions "backwards", like Clojure's thread (->)?

Viewed 373

new to Haskell here.

I'd like to write:

take 1 $ take 2 [1, 2, 3] -- = 1

reversed, like this pseudocode:

[1, 2, 3] -> take 2 -> take 1 -- = 1

In Clojure, we can do this with:

(->> [1 2 3]
  (take 2)
  (take 1)) ;=> (1)

Clojure does this because ->> is a macro that rewrites the expression to (take 1 (take 2 [1 2 3])), but because Haskell is lazy and has partials and whatnot, it seems like it should be easy.

I want to do this because having the data first then reading the functions in order that they are executed is a lovely way to read code. I have been spoiled by Clojure!

It's similar to the fluent interface / chain pattern of var.action1().action2() etc, in object oriented languages.

I imagine this is possible in Template Haskell, but surely there's a builtin way to do this that I don't yet know? Thanks!

2 Answers

There is (&), which you need to import from Data.Function.

> import Data.Function
> [1,2,3] & take 2 & take 1
[1]

If you really have an aversion to importing that module, the definition is simply (&) = flip ($).

chepner's answer is totally the correct one.

There are more tricks to abuse Haskell syntax, but the code below should not be used for any serious applications.

RebindableSyntax breaks users' expectations about the meaning of standard constructs. Variadic functions have terrible type inference, error reporting, and corner cases (those issues can be mitigated with an unreasonable amount of effort, but never fully solved).

Rebindable Syntax

The notation do {x ; y} is sugar for x >> y, and the RebindableSyntax extension allows you to rebind (>>). You can redefine it as (reversed) function composition for example:

y :: [Int]
y = [1,2,3] & do
  take 2
  take 1
 where
  (>>) = flip (.)

Variadics

You can use type classes to define (->>) as a variadic function.

z :: [Int]
z = (->>) [1,2,3]
  (take 2)
  (take 1)

class App a b where
  (->>) :: a -> b

instance (a ~ a', App b c) => App a ((a' -> b) -> c) where
  (->>) x y = (->>) (y x)

instance {-# OVERLAPPABLE #-} (a ~ a') => App a a' where
  (->>) = id

Full gist: https://gist.github.com/Lysxia/3461f489cc5057ea089e23a4eede375a

Related