5

I want to apply a 3 argument function in different ways based on a boolean value (one of the arguments).

I'd like to be able to apply it in an infix manner so I can chain it (example below). something like the following but that actually works.

f :: Bool -> a -> a -> a

f True  i j = i
f False i j = j

... y `(f True)` z `(f False)` b

Do I need to have the Bool as the second variable? Or something else? I'm kind of lost.

P.S. the reason I wanted to do this was to have optional append function

Don Stewart
  • 137,316
  • 36
  • 365
  • 468
Roman A. Taycher
  • 18,619
  • 19
  • 86
  • 141
  • 1
    why not define two functions ftrue and ffalse and use them? (I agree this wouldn't work in the more general case though...) – Ptival Jun 15 '11 at 07:40

5 Answers5

7

The infix notation for functions with more than two arguments is possible but not very nice. In your case, given

f :: Bool -> a -> a -> a
f True i j = i
f False i j = j

you can write

(True `f` 1) 3

It's much easier (as suggested by others) to write:

let ft = f True
let ff = f False

then you can do

1 `ft` 3 `ff` 5

If you want to use f you should write:

(False `f` ((True `f` 1) 3)) 5

You can verify that

1 `ft` 3 `ff` 5 == (False `f` ((True `f` 1) 3)) 5
MarcoS
  • 13,386
  • 7
  • 42
  • 63
4

You can use a reverse application operator to achieve a similar effect. However, for every operator fixity you will have to define its own pair of operators.

This works:

infixl 5 -|
(-|) :: a -> (a -> b) -> b
(-|) = flip ($)

infixl 5 |-
(|-) :: (a -> b) -> a -> b
(|-) = ($)

infixr 5 =|
(|=) :: (a -> b -> c) -> b -> (a -> c)
(|=) = flip

infixr 5 |=
(=|) :: a -> (a -> c) -> c
(=|) = (-|)

f b x y = if b then x else y

main = do
    putStrLn $ "qwe" -| f True |- "asd" -| f False |- "kojo"
    putStrLn $ "qwe" =| f True |= "asd" =| f False |= "kojo"

And prints:

kojo
qwe

Here -| and |- brackets enclose left-associative infix expressions and =| and |= enclose right-associative expressions.

Be wary that as those are just pairs of independent operators, error messages when misusing them may be quite obscure.

Rotsor
  • 13,655
  • 6
  • 43
  • 57
3

While infix expression syntax have been proposed, they have not been adopted, to the best of my knowledge.

You might consider using if then else ternary functions, like so:

infix 0 ?
(?) :: Bool -> (t, t) -> t
c ? (t, e) = if c then t else e
Don Stewart
  • 137,316
  • 36
  • 365
  • 468
1

Well, for one thing, f would have type f :: Bool -> a -> a -> a

See http://www.haskell.org/haskellwiki/Infix_operator#Using_prefix_functions_with_infix_notation for more information on using prefix functions in an infix setting.

Hopefully somebody else is wiser and can explain why

1 `f True` 3 

doesn't work, since

ft = f True
1 `ft` 3

works fine, and this seems to violate referential transparency...

It would seem that there isn't a "nice", easy way to do this, but I'm sure there's some higher level pattern that this fits, or you could do something with, e.g., zipWith or folds...

tredontho
  • 1,273
  • 10
  • 14
  • 5
    `f True` is not allowed because it's syntactically not allowed by the Haskell98 report. http://www.haskell.org/onlinereport/exps.html#operators see section 3.2, the infix operators are also defined on "id"s which are single names like "f" whereas "f True" would be an expression – Phyx Jun 15 '11 at 08:24
0

Why do you need the boolean function at all? If the value is never used, why not define f as

f :: a -> a -> a  
f i j  
i `f` j

If the boolean is really needed, then as Pvital suggested, create a ftrue and ffalse and use those.

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
Phyx
  • 2,697
  • 1
  • 20
  • 35