I Wonder… Trinary Operators in Haskell

Often, while out for my daily run, I think about programming. Sometimes I think about my project, sometimes I think about upcoming projects, sometimes I think about the blog. Then there’s the times where I think about completely silly things like “I wonder if you can make an operator that takes three arguments?”

Wouldn’t that be grand.

I suppose I could just google it, but where’s the fun in that? Let’s give it a shot!

(+++) :: (Num a) => a -> a -> a -> a (+++) a b c = a + b + c

Here I’ve created a fairly straight-forward function: It takes three arguments, and adds them, returning the sum. Let’s load this up in ghci and see if it barfs.

Prelude> :l SuperSum.hs [1 of 1] Compiling SuperSum ( SuperSum.hs, interpreted ) Ok, modules loaded: SuperSum. *SuperSum>

…so far so good, let’s put it through the paces!

*SuperSum> :t (+++) (+++) :: Num a => a -> a -> a -> a

…this checks out, let’s call it as prefix…

*SuperSum> (+++) 1 2 3 6

…check! Let’s partially apply it with two arguments like a regular operator…

*SuperSum> :t 1 +++ 2 1 +++ 2 :: Num a => a -> a

…makes sense, this returns a function that takes a Num and returns a Num. Let’s quit beating around the bush and call it!

*SuperSum> 1 +++ 2 3 <interactive>:21:3: No instance for (Num a0) arising from a use of `+++' The type variable `a0' is ambiguous Possible fix: add a type signature that fixes these type variable(s) Note: there are several potential instances: instance Num Double -- Defined in `GHC.Float' instance Num Float -- Defined in `GHC.Float' instance Integral a => Num (GHC.Real.Ratio a) -- Defined in `GHC.Real' ...plus three others In the expression: 1 +++ 2 3 In an equation for `it': it = 1 +++ 2 3 <interactive>:21:7: No instance for (Num (a1 -> a0)) arising from the literal `2' Possible fix: add an instance declaration for (Num (a1 -> a0)) In the expression: 2 In the second argument of `(+++)', namely `2 3' In the expression: 1 +++ 2 3 <interactive>:21:9: No instance for (Num a1) arising from the literal `3' The type variable `a1' is ambiguous Possible fix: add a type signature that fixes these type variable(s) Note: there are several potential instances: instance Num Double -- Defined in `GHC.Float' instance Num Float -- Defined in `GHC.Float' instance Integral a => Num (GHC.Real.Ratio a) -- Defined in `GHC.Real' ...plus three others In the first argument of `2', namely `3' In the second argument of `(+++)', namely `2 3' In the expression: 1 +++ 2 3

Yikes! It’s like we’re doing some Java! Well, that message is certainly unhelpful, let’s see what ghci thinks the type of this is:

*SuperSum> :t 1 +++ 2 3 1 +++ 2 3 :: (Num a, Num (a1 -> a), Num a1) => a -> a

Also unhelpful. As far as I can tell, the issue here is precedence. Basically, if we add some parenthesis or a dollar sign, this will work just fine:

*SuperSum> (1 +++ 2) 3 6 *SuperSum> :t (1 +++ 2) 3 (1 +++ 2) 3 :: Num a => a *SuperSum> 1 +++ 2 $ 3 6 *SuperSum> :t 1 +++ 2 $ 3 1 +++ 2 $ 3 :: Num a => a

When we are explicit about our precedence, the functions work as expected, and we get an “infix function with more than two arguments”. The moral of the story? You can do it, but you shouldn’t.

Now that we’ve solved this mystery by ourselves, let’s see if there’s any documentation on the issue.

Google turns up very little on the subject, however the first result seems promising. From the bottom of that page:

for a function taking more than two arguments, you can do it but it’s not nearly as nice

Now let us never speak of this again…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: