[Haskell-cafe] Prolog-style list syntax?

Sebastiaan Joosten sjcjoosten+haskell at gmail.com
Tue Jun 29 16:01:43 UTC 2021


On Tue, Jun 29, 2021 at 7:05 AM Anthony Clayden <anthony.d.clayden at gmail.com>
wrote:

>
> Because almost always you need to put parens round -- certainly in a
> pattern match.
>
>
This is an interesting point, and for the sake of having an
unambiguous parser, I don't think this requirement is needed: The arity of
all constructors is known, all constructors must have all of its arguments
(no currying in patterns), so Polish notation should suffice and
parentheses could be optional (an implementer would need to delay parsing
of anything to the lhs of an <- or = until all constructors are known).
With infix operators (`a:b:c` should not be parsed as `(a:b):c`) we simply
take fixity into. To a certain extent Haskell already does this, the
following works:

infixr 3 `member`

a `member` []                  = False

a `member` b : bs | b==a       = True

                  | otherwise  = a `member` bs

To make this work in general, any function symbols on the lhs of an = (and
their respective function application) should have the lowest precedence,
and constructor 'application' through spaces would seem to go in the
opposite direction of what is common. Following this convention, we could
have this amusing looking definition in the Prelude:
x:xs++ys = x:xs++ys
On the lhs, since ++ is not a constructor, it is the symbol that is being
defined and therefore of lowest precedence, so the lhs would read
(x:xs)++ys. On the rhs, (:) and (++) are infixr 5, so the rhs reads as
x:(xs++ys).
Just to be clear: none of this is a proposal, I would not be in favor of
this as I prefer consistency.

As to the original question: I see no technical issues in allowing
something like `[ x, y || ys]` as syntax for `(x:y:ys)`, or using any new
symbol in place of ||. As others have pointed out, we cannot have all three
of:
- `[ expr ]` stands for singleton list elements (as currently the case)
- keep the current meaning of `x : xs`
- let `[ x : xs ]` be 'shorthand' for `x:xs`
>From a Prolog perspective, dropping `x : xs` may seem the way to go
(arguments like: "in many patterns we need parentheses anyways" seem to
agree with this view), but Haskell code has `foo x y` instead of
`foo(x,y)`, and infix syntax follows the same principle: outer parentheses
are optional. Prolog follows the same principle for +, *, -, =, etc, just
not for relations.

Would I be violently against even something mild like the introduction of
`[ x,y || ys ]`? Not violently so but definitely fiercely, unless this is
tucked away behind a flag. I see it as pointless, wouldn't use it, and I
don't care to explain it to my students (or at least those who don't know
Prolog) either, and it would reduce 3rd party code readability because
there would now be two ways of writing the same thing, neither of which is
entirely obvious. In a new / different language I'd love to see all these
weird ideas tried out. Sometimes odd conventions (such as writing `foo x y`
instead of `foo(x,y)`) grow on me and I start to like them. A good place to
start might be to add outer parentheses to your language to distinguish `[x
: xs]` from `[(x : xs)]`, forbidding `x : xs`. This would be LISP with
infix notations and static typing... that actually sounds like it could be
nice.

While we're on the topic of changing list syntax, some more ideas that
might make sense (but are not a proposal!!):
 `[ x,y | binders ]`  as syntax for:  `concat [ [x,y] | binders ]`
 `[ x ..< y ]` as syntax for `FromEnumToExclusive x y` (i.e. `if x==y then
[] else [x..pred y]` by default), Isabelle/HOL has this.
 A keyword `get` in addition to `let` s.t. `[x | get Just x = y]` is syntax
for `[x | Just x <- [y]]` (using let produces a pattern match failure if
y=Nothing).
 While we're at it: `do {get Just x = ys; continuation}` as syntax for `do
{Just x <- return ys; continuation}`.
 `[ x | xs <- as; x <- xs]` as syntax for: `[x | xs <- as, x <- xs]` (the ;
would have the binders behave like `do-syntax`).
 While we're at it: `[ expr | binders]` as syntax for: `do {binders;return
expr} :: (Listlike f => f b)` with `default Listlike ([])`.
 Finally, `do {(binders1 | binders2) ; continuation}` could be syntax for:
`Data.Zip.zipWith (\x y -> continuation) (do binders1;return x) (do
binders2; return y)` (as inspired by parallel list comprehensions).

It's fun to think about these things, but each of these ideas comes with
syntactic change and therefore yet another language construct to learn.
Since syntax cannot be found on hoogle
<https://hoogle.haskell.org/?hoogle=..> and even google <http://what does
.. mean in haskell>, all of these things generally increase the burden to
the reader of your code, which is why none of this is a proposal.

Best,
Sebastiaan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20210629/7219a27b/attachment.html>


More information about the Haskell-Cafe mailing list