It's common for Haskellers to have combining functions where the
arguments to each combinator changes the overall type. Writing out
combine x (combine y z)
gets old, and the heterogenous types means
that we can't use a simple foldr
/foldl
, so we invent e.g. x % y % z
. The problem with this for me is that it introduces its own
redundancy and viewing/editing overhead.
Lispers are used to writing (* x y z)
and I see this as a reasonable
and predictable syntactic improvement. For Lispers like me, being able
to just write an identifier out (like combine) and read that in plain
English has always been something I wish was more catered to in
Haskell. As far as syntax goes, there is a lot of Perl-like
influence in the Haskell world, and I generally try to
contribute to the Lisp-like side of that balance.
Suppose we had a keyword like infix
—in fact, we do! It's already
taken up at the declaration level, but it's only disabled at the
expression level, not used. Parsers don't allow this to be used as an
identifier, and editors syntax highlight it as a keyword. So supposing
that we define a variadic infix
keyword which accepts an identifer
and arguments:
infix <op> → (<op>)
infix <op> a → (a <op>)
infix <op> a b c d → a <op> b <op> c <op> d
infix name a → (a `name`)
infix name a b c d → a `name` b `name` c `name` d
In other words, infix
takes the following identifier and expands it
to an infix interspersal. The left/right precedence will be determined
later by whatever has been declared for that operator or name
in the
usual way. (infixl
/infixr
could also be supported, but it'd
probably be more confusing to read nonce fixities.)
The precedence of this is determined like regular function application, so:
infix <op> arg1 arg2 % …
is equiv. to
(infix <op> arg1 arg2) % …
The spaces bind tighter than any other operators.
- Some things are foldable cleanly like Monoid instances, so you can just
mconcat [x,y,z]
, but that will not be inlined in GHC 7.8.4. - Other things like
x <|> y
is not the same asasum [x,y]
due to the additionalempty
being introduced. You can also use foldl1 kind of functions, but they are partial and therefore not desirable. - Finally, things like
<*>
,$=
,$
,.
,#
/:&
(e.g. in HList/vinyl) can't be folded at all, because the types are different.
The third use-case doesn't have a solution that I'm aware of. So this solves that. It also solves the second use-case, which has only a partial (he he) solution. The first use-case is just a bonus.
Instead of:
foo <> bar <> mu <> zot
You can write:
infix <> foo bar mu zot
infix <> "Your name is " name " and your age is" age
You often see people using a writer monad to acheive this kind of convenience.
or
infix mappend foo bar mu zot
→
foo `mappend` bar `mappend` mu `mappend` zot
Instead of:
foo <*> bar <*> mu <*> zot
you can write:
infix ap foo bar mu zot
→
foo `ap` bar `ap` mu `ap` zot
Instead of:
foo <|> bar <|> mu <|> zot
you can write:
infix <|> foo bar mu zot
Conduits:
infix $= source foo bar mu zot $$ sink
Composition:
infix . foo bar mu zot
Convenience for application:
infix $ head group sort [1..5]
If desired there could possibly be special support for tuple's comma:
infix , a b c d e
Lists can be funnily expressed too:
infix : a b c d e []