Shai Ben-Dor
Shai Ben-Dor

Reputation: 573

Is there a way to pipe two arguments to a function in Elm?

Having the function:

f : Int -> Int -> Int
f a b = 
  a + b

is there a way to pipe both a and b?

Iv'e got only this far, but I would like to know if there is a way to get rid of the brackets:

main =
  1 |> (2 |> f) |> toString |> text

Upvotes: 3

Views: 3133

Answers (5)

Marcelo Lazaroni
Marcelo Lazaroni

Reputation: 10239

The |> pipe evaluates the left side first, which is what you want, and applies the first argument to the second, which is not what you want. The <| pipe evaluates the right side first, which is not what you want, and applies the second argument to the first, which is not what you want.

You can get round it with lambdas that invert the function application. Like this

add a b = a + b

add
   |> \f -> f 2
   |> \f -> f 3

-- outcome: 5

Or you can write a helper function

invert v f = f v

add
   |> invert 2
   |> invert 3

Upvotes: 1

Emmanuel Rosa
Emmanuel Rosa

Reputation: 9885

You can achieve a similar flow like this:

1 :: 2 :: [] |> List.foldl f 0 |> toString |> text

The idea is to use :: to create a List containing the arguments. Then, use foldl to apply the function f to the arguments.

Of course, there are a number of superfluous items in the syntax: the empty List and the starting value needed by foldl. Continuing with your example function, you can use composition to hide the starting value:

let
    g = List.foldl f 0
in
    1 :: 2 :: [] |> g |> toString |> text

The downside is you'd still have a starting value to deal with, which you probably wish was simply the head of the list (ex. 1). But, if you try to use the head of the list as the starting value, you'd end up with another problem:

If there's no starting value (an empty list) then fold cannot return anything, and all Elm functions have a return value.

Regardless, hopefully this approach will help.

Upvotes: 0

mgold
mgold

Reputation: 6366

Don't pipe for piping's sake. It's a lot cleaner to say 1 |> f 2 |> toString |> text than what you have in your example. The argument that gets piped is frequently the data structure, since by convention it's always the last argument. Let's look at a more realistic example:

"40"
  |> String.toInt
  |> Maybe.map (\n -> n + 2)
  |> Maybe.withDefault 2

The direct arguments (the lambda, 2) affect the operation while the data structure (a Maybe Int for part of the chain) is piped though. Contrast: Maybe.withDefault 2 (Maybe.map (\n -> n + 2) (String.toInt "40")). This ugly version isn't even faster, since pipes are optimized by the compiler.

If you pipe in this style, of transformations on a data structure, you will not need to pipe two items in.

Upvotes: 4

whatoncewaslost
whatoncewaslost

Reputation: 2256

I would imagine not. I've never seen it. The reason it's so unlikely is that technically, all elm functions only take one argument. But what about things like this?

add 1 2 3

Well, Elm actually curry's the function, and returns an anonymous function after the first argument that takes the second argument, and that takes the second argument and returns a third argument. If you've ever wondered why the type annotations look like there's no seperation between the multiple arguments and the return statement .....

add : Int -> Int -> Int

... that's because there's not!

Upvotes: 0

Tosh
Tosh

Reputation: 36030

Although it may not satisfy you, one simple possibility is:

(1, 2) |> uncurry f

Upvotes: 7

Related Questions