Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: Get a prefix operator that works without parentheses

Tags:

haskell

One big reason prefix operators are nice is that they can avoid the need for parentheses so that + - 10 1 2 unambiguously means (10 - 1) + 2. The infix expression becomes ambiguous if parens are dropped, which you can do away with by having certain precedence rules but that's messy, blah, blah, blah.

I'd like to make Haskell use prefix operations but the only way I've seen to do that sort of trades away the gains made by getting rid of parentheses.

Prelude> (-) 10 1 

uses two parens.

It gets even worse when you try to compose functions because

Prelude> (+) (-) 10 1 2 

yields an error, I believe because it's trying to feed the minus operation into the plus operations rather than first evaluating the minus and then feeding--so now you need even more parens!

Is there a way to make Haskell intelligently evaluate prefix notation? I think if I made functions like

Prelude> let p x y = x+y
Prelude> let m x y = x-y

I would recover the initial gains on fewer parens but function composition would still be a problem. If there's a clever way to join this with $ notation to make it behave at least close to how I want, I'm not seeing it. If there's a totally different strategy available I'd appreciate hearing it.

I tried reproducing what the accepted answer did here:

Haskell: get rid of parentheses in liftM2

but in both a Prelude console and a Haskell script, the import command didn't work. And besides, this is more advanced Haskell than I'm able to understand, so I was hoping there might be some other simpler solution anyway before I do the heavy lifting to investigate whatever this is doing.

like image 593
Addem Avatar asked Feb 28 '17 20:02

Addem


People also ask

How do I define infix operator Haskell?

There's no way to define a function with an alphanumeric name as infix. Haskell's syntax rules only allow for functions with symbolic names or function names surrounded with backticks to be used infix - there's no way to change that.

What do parentheses mean in Haskell?

If you use parentheses it's only to delimit individual arguments (if they are expressions); and commas are used to separate tuple elements. So if you see something like this: f (a, b, c) you interpret it as a call to (application of) a function f to just one argument -- a tuple of three elements.

How do you write less than or equal to in Haskell?

Haskell provides a number of tests including: < (less than), > (greater than), <= (less than or equal to) and >= (greater than or equal to). These tests work comparably to == (equal to).

What are sections in Haskell?

Haskell Language Partial Application Sections Sectioning is a concise way to partially apply arguments to infix operators. For example, if we want to write a function which adds "ing" to the end of a word we can use a section to succinctly define a function. Notice how we have partially applied the second argument.


1 Answers

It gets even worse when you try to compose functions because

Prelude> (+) (-) 10 1 2 

yields an error, I believe because it's trying to feed the minus operation into the plus operations rather than first evaluating the minus and then feeding--so now you need even more parens!

Here you raise exactly the key issue that's a blocker for getting what you want in Haskell.

The prefix notation you're talking about is unambiguous for basic arithmetic operations (more generally, for any set of functions of statically known arity). But you have to know that + and - each accept 2 arguments for + - 10 1 2 to be unambiguously resolved as +(-(10, 1), 2) (where I've explicit argument lists to denote every call).

But ignoring the specific meaning of + and -, the first function taking the second function as an argument is a perfectly reasonable interpretation! For Haskell rather than arithmetic we need to support higher order functions like map. You would want not not x has to turn into not(not(x)), but map not x has to turn into map(not, x).

And what if I had f g x? How is that supposed to work? Do I need to know what f and g are bound to so that I know whether it's a case like not not x or a case like map not x, just to know how to parse the call structure? Even assuming I have all the code available to inspect, how am I supposed to figure out what things are bound to if I can't know what the call structure of any expression is?

You'd end up needing to invent disambiguation syntax like map (not) x, wrapping not in parentheses to disable it's ability to act like an arity-1 function (much like Haskell's actual syntax lets you wrap operators in parentheses to disable their ability to act like an infix operator). Or use the fact that all Haskell functions are arity-1, but then you have to write (map not) x and your arithmetic example has to look like (+ ((- 10) 1)) 2. Back to the parentheses!

The truth is that the prefix notation you're proposing isn't unambiguous. Haskell's normal function syntax (without operators) is; the rule is you always interpret a sequence of terms like foo bar baz qux etc as ((((foo) bar) baz) qux) etc (where each of foo, bar, etc can be an identifier or a sub-term in parentheses). You use parentheses not to disambiguate that rule, but to group terms to impose a different call structure than that hard rule would give you.

Infix operators do complicate that rule, and they are ambiguous without knowing something about the operators involved (their precedence and associativity, which unlike arity is associated with the name not the actual value referred to). Those complications were added to help make the code easier to understand; particularly for the arithmetic conventions most programmers are already familiar with (that + is lower precedence than *, etc).

If you don't like the additional burden of having to memorise the precedence and associativity of operators (not an unreasonable position), you are free to use a notation that is unambiguous without needing precedence rules, but it has to be Haskell's prefix notation, not Polish prefix notation. And whatever syntactic convention you're using, in any language, you'll always have to use something like parentheses to indicate grouping where the call structure you need is different from what the standard convention would indicate. So:

(+) ((-) 10 1) 2

Or:

plus (minus 10 1) 2

if you define non-operator function names.

like image 110
Ben Avatar answered Oct 04 '22 21:10

Ben