2

List comprehensions (or ZF-expressions) include a sequence of qualifiers, which can be generators or Boolean-valued expressions ("filter expressions") acting as guards.

A list comprehension with no qualifier – for instance, [1 | ] – is (apparently) valid in Miranda1 (p. 130), but is invalid in Haskell2, 3 (p. 42) –I tried it in the ghci interpreter– and is (apparently) invalid in Clean4.

(Of course, we could simulate it by adding a True guard, for instance [1 | True]. But this is more verbose.)

An example of use of a list comprehension with no qualifier in the literature1 (pp. 134-136) is the following instance of equational reasoning:

[E | ] ++ L = [E] ++ L = (E:[]) ++ L = E:L

Why did Haskell and Clean programming language designers decide against list comprehensions without qualifiers? Is there something that would cause bad feature interactions in these languages but not in Miranda?


References:

  1. Simon L. Peyton Jones. The Implementation of Functional Programming Languages. Prentice Hall. 1987.

  2. The Haskell 98 Report, section 3.11 "List Comprehensions". 1998.

  3. Peter Wentworth. An Introduction to Functional Programming Using Hugs. 2013.

  4. Rinus Plasmeijer; Marko van Eekelen; John van Groningen. Clean Language Report, version 2.2. 2011.

Antonielly
  • 71
  • 1
  • 4
  • I doubt there is any complex feature interaction. It just seems unnecessary -- why allow a one element list to be written with an extra useless bar? If I were a compiler, I would guess that the user made some kind of mistake, and they should probably be told about it... – luqui Aug 11 '22 at 21:43
  • 2
    What would be the point of `[ 1 | ]` instead of `[1]`? If you could specify some possibly empty list of generators and guards (`let gens = [] in [1 | gens ]`) that would be equivalent to an empty generator list, it might make sense. – chepner Aug 11 '22 at 21:46
  • When was the last time you wanted to write `[ 1 | ]` instead of `[1]`? Also, to me, it looks a little visually weird when you consider there is also the somewhat similar-looking Template Haskell quotation syntax `[| ... |]`. – David Young Aug 11 '22 at 23:39
  • @luqui @chepner @david-young It does have relevant applications. A theoretical application: equational reasoning. Some practical use cases: (1) automatic source **code generation of list comprehensions** (being a natural "base case" to progressively add generators and filter conditions without nasty workarounds); (2) [simulating relational database queries](https://wiki.c2.com/?TableDum) (in [relationally complete](https://books.google.com/books?id=bTK-DwAAQBAJ&pg=PT20&dq=%22relationally+complete%22+%22degree+zero%22) query languages) by means of list comprehensions in the form `[() | ...]`. – Antonielly Aug 13 '22 at 13:47

1 Answers1

0

I think the obvious answer is that there is no technical reason to disallow list comprehensions with an empty sequence of qualifiers. The requirement is a purely syntactic one. It just so happens that the original Haskell98 report section 3.11 specifies a grammar that requires the list of qualifiers to be non-empty, and the translation rules to turn it into a kernel expression assume this is the case. GHC obeys this grammar, so an expression like [ 10 | ] is rejected with a parse error.

From a technical standpoint, simply relaxing the grammar and adding a single translation rule:

[ e | ] = [ e | True ]

would address this.

In fact, the documentation for the GHC MonadComprehensions extension actually provides a translation for this case:

[ e | ] = return e

and the rest of the translation rules basically assume that cases of the form:

[ e | q, Q ]

apply to the special case:

[ e | q ]

with Q taken to be empty.

Once you get past the parser, the rest of GHC handles this special case just fine. In particular, if you use Template Haskell to bypass the parser and construct a comprehension with no qualifiers, you can see that it evaluates as expected. (The separate modules here are needed because of the TH phase restricution.)

--
-- EmptyComprehension.hs
--

module EmptyComprehension where

import Language.Haskell.TH

-- equivalent to "[ 10 | ]"
x :: ExpQ
x = compE [noBindS (litE (integerL 10))]


--
-- Test.hs
--

{-# LANGUAGE TemplateHaskell #-}

import EmptyComprehension

main = print $(x)  -- will print `[10]`

As for why the language was designed this way, I imagine it was either an oversight or perhaps it was considered at some point but the syntax [10 | ] was thought to be too bizarre to allow.

On a related note, empty cases were also disallowed by the original Haskell98 report but then permitted via an EmptyCase extension when it was discovered that they could actually be useful.

I think you might have trouble convincing anyone that a similar extension for comprehensions is worthwhile. In the comments, you mention automatic source generation, but it's easy to handle that case specially or add an extra True qualifier to every comprehensive (or -- if you're generating source via TemplateHaskell -- just bypass the restriction directly).

K. A. Buhr
  • 45,621
  • 3
  • 45
  • 71