How does the 'infix' work?
Asked Answered
C

2

28

I'm playing with the infixr, infixl and infix declarations. I understand how infixr and infixl works:

-- Test expression: 40 +++ 20 +++ 50 +++ 10 * 10

-- infixr 8 +++ -- Calculated as: (40 +++ (20 +++ (50 +++ 10))) * 10. Result: 630.
-- infixl 8 +++ -- Calculated as: (((40 +++ 20) +++ 50) +++ 10) * 10. Result: 800.

-- infixr 6 +++ -- Calculated as: 40 +++ (20 +++ (50 +++ (10 * 10))). Result: 75.
-- infixl 6 +++ -- Calculated as: ((40 +++ 20) +++ 50) +++ (10 * 10). Result: 125.

(+++) :: Int -> Int -> Int
a +++ b = a + (b `div` 2)

But I don't understand how the infix keyword works. Am I right in thinking that with infix you always need to specify the order with parenthesis? If so, why is the numeric argument necessary considering that brackets have the highest precedence)?

Cory answered 4/1, 2015 at 20:39 Comment(1)
The associativity (l/r/<nothing>) tells how to group when there are many copies of that operator; the precedence (1-9) tells how to group when there are many different operators.Gingrich
B
50

tl;dr

The r and l refer to the associativity, the number you specify refers to the operator precedence. When you don't specify the associativity you get an operator that can be associated only by explicit parenthesis or when the associativity is non-ambiguous.

Our test data structure

Let's use a data structure to define operators on and understand how associativity works:

data Test = Test String deriving (Eq, Show)

It will contain the string built with the below operators.

Associativity with infixr and infixl

Now let's define right- and left- associative operators:

(>:) :: Test -> Test -> Test
(Test a) >: (Test b) = Test $ "(" ++ a ++ " >: " ++ b ++ ")"

(<:) :: Test -> Test -> Test
(Test a) <: (Test b) = Test $ "(" ++ a ++ " <: " ++ b ++ ")"

infixr 6 >:
infixl 6 <:

These operator will construct the string of the resulting operator by explicitly adding the parenthesis to our associated terms.

If we test it out we see that it works correctly:

print $ (Test "1") >: (Test "2") >: (Test "4")
-- Test "(1 >: (2 >: 4))"

print $ (Test "1") <: (Test "2") <: (Test "4")
-- Test "((1 <: 2) <: 4)"

"Associativity" with infix

An infix declaration does not specify associativity. So what should happen in those cases? Let's see:

(?:) :: Test -> Test -> Test
(Test a) ?: (Test b) = Test $ "(" ++ a ++ " ?: " ++ b ++ ")"

infix 6 ?:

And then let's try it:

print $ (Test "1") ?: (Test "2") ?: (Test "4")

Woops, we get:

Precedence parsing error cannot mix `?:' [infix 6] and `?:' [infix 6] in the same infix expression

As you can see the language parser noticed that we didn't specify the associativity of the operator and doesn't know what to do.

If we instead remove the last term:

print $ (Test "1") ?: (Test "2")
-- Test "(1 ?: 2)"

Then the compiler doesn't complain.

To fix the original term we would need to explicitly add parenthesis; for example:

print $ (Test "1") ?: ((Test "2") ?: (Test "4"))
-- Test "(1 ?: (2 ?: 4))"

Live demo

Bobodioulasso answered 4/1, 2015 at 21:16 Comment(0)
R
0

Because

1 +++ 2 < 3

works fine.

Rustication answered 4/1, 2015 at 20:45 Comment(3)
Downvote - Please anyone tell me how that's an informative and well-researched answer to the question at hand, and why it shouldn't just be an obscure comment somewhere on the page.Yes
@toraritte: The question: “If so, why is the numeric argument necessary considering that brackets have the highest precedence)?” Here’s an example of an expression without brackets, therefore requiring the operator to have a precedence. It’s short and to the point. If you don’t find it helpful, well, that’s perfectly within your rights.Rustication
The question was "How does the 'infix' work?", and then they expanded on it, ending with a couple questions, probably to clarify their inquiry. Plus, I'm just wasting energy belaboring the obvious, and I should get a life instead.Yes

© 2022 - 2024 — McMap. All rights reserved.