I'm playing around with custom operators, infix
, infixl
and infixr
. Now I'm confused.
I've written a custom operator for list-multiplication, and thought, that declaring it as a simple infix-operator with no directional associativity, would automatically provide both cases, nr * list
and list * number
, as they can be interchanged at will.
import Prelude hiding ((*))
infix 6 *
(*) :: Int -> [a] -> [a]
n * l = if n < 0 then []
else l (n - 1) * l
Now, 3 * [1, 2, 3]
returns [1, 2, 3, 1, 2, 3, 1, 2, 3]
as expected, but [1, 2, 3] * 3
throws an error, because I never explicitly defined list * nr
.
My question: What is the unique functionality of infix
and why not allways use infixl
or infixr
instead, as it should make no difference?
I understand "no directional associativity" / infix
as a synonym to "is commutative":
a b c
has no directional associativity and is commutative and can be written as (a b) c
, a (b c)
, b a c
, (b a) c
, and so on...
For my example 2 * [1, 2] * 1
is the same as 1 * (2 * [1, 2])
, and all other combinations of that, so i dont really get, why there is no implicit reshaping for commutative operator declarations, even with different typed operands.
CodePudding user response:
The fixity declarations only affect parsing, not the definitions of the operators.
If you use infixl
, then a * b * c
is parsed as (a * b) * c
.
If you use infixr
, then a * b * c
is parsed as a * (b * c)
.
If you use infix
, then you are saying that a * b * c
cannot be parsed; you must use parentheses to specify whether you mean (a * b) * c
or a * (b * c)
. Compare
Prelude> infix 6 ***; (***) = ( )
Prelude> 1 *** 2 *** 3
<interactive>:8:1: error:
Precedence parsing error
cannot mix ‘***’ [infix 6] and ‘***’ [infix 6] in the same infix expression
Prelude> infixl 6 ***; (***) = ( )
Prelude> 1 *** 2 *** 3
6
In your case, *
is not fully associative, because the types don't line up. It can be right-associative, because 3 * (6 * [])
typechecks but not left-associative because (3 * 6) * []
does not. Using infix
, you disallow 3 * 6 * []
. If you used infixr
, then you could write that and the parser will treat it as 3 * (6 * [])
.
Making an operator like this commutative is tricky, because at the type level they are two different operators. That's easy enough to define:
-- Ignoring the fact that both of these operators are already
-- used by the Applicative class for different purposes.
(*>) :: Int -> [a] -> [a]
0 *> l = []
n *> l = l (n-1) * l
(<*) :: [a] -> Int -> [a]
(<*) = flip (*>)
Making *
work as both Int -> [a] -> [a]
and [a] -> Int -> [a]
is tricky, if not impossible. (Maybe something involving a multi-parameter type family?
-- Compiles, but does not run. Not sure why...
{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, FlexibleContexts #-}
class Multiplable x y where
type Result x y
(***) :: x -> y -> Result x y
instance Multiplable Int [a] where
type Result Int [a] = [a]
0 *** l = []
n *** l = l ((n - 1) *** l)
instance Multiplable [a] Int where
type Result [a] Int = [a]
l *** 0 = []
l *** n = l (l *** (n - 1))
)
Your understanding of associativity and commutativity is incorrect. "Not associative" is not a synonym for "commutative". In fact, the two properties are orthogonal: a given operator can be both, or neither, or only one of the two.
Integer addition is associative and commutative.
Integer subtraction is neither associative nor commutative.
Matrix multiplication is associative, but not commutative. (
BA
can be different fromAB
or even undefined altogether.)The NAND operation (the negation of logical AND) is commutative, but not associative:
(True NAND True) NAND False == False NAND False == True True NAND (True NAND False) == True NAND True == False