Thomas Leonard | 23 Oct 2011 12:41
Picon
Gravatar

Re: for-must-match

On 22 October 2011 15:00, Kevin Reid <kpreid <at> switchb.org> wrote:
> On Oct 22, 2011, at 8:17, Thomas Leonard wrote:
>
>> Here's a patch to add list comprehensions:
>>
>> ? def list := [1, 2, 3]
>> ? [2 * x for x in list]
>> # value: [2, 4, 6]
>>
>> http://gitorious.org/repo-roscidus/e-core/commit/9bfacc4f748b4ea6a7cf64dc40d7bc9dd1890d38
>>
>> And for map comprehensions:
>>
>> ? def map := ["alice" => 50, "bob" => 60]
>> ? [v => k for k => v in map]
>> # value: [50 => "alice", 60 => "bob"]
>>
>> http://gitorious.org/repo-roscidus/e-core/commit/980c8c93d41a69f242acdb5b83c9faccf918f4c3
>>
>> They don't currently support the 'match' keyword, so you can't use
>> them to filter (every item must be mapped to something).
>
>
> I reject this proposal.
>
> 1. I am uncomfortable with it because I suspect it of violating E's syntactic conventions about scoping
and evaluation order, though I would have to consult MarkM to be more precise.

It probably does. However, there's also consistency with other
languages to consider. The Wikipedia page has examples both ways,
though most seem to favour putting the expression first:

Some expr first example:

[2 * x for x in items]		# E (proposed)
[2 * x for x in items]		# Python
[2 * x for (x in items)]	# JavaScript
[2 * x | x <- items ]		# Haskell
(list-of (* 2 x) (x in items))	# Scheme (non-standard)

Expr last examples:

for (x in items) { 2 * x }	# JavaFX
for (x <- items) yield 2*x	# Scala

The Scala syntax could work in E, although it would interfere with
break(x) and involve creating a new keyword.

The JavaFX syntax (without the "yield" keyword) wouldn't work because
ordinary for loops are already defined to return the break() value, or
null. Even if that feature were removed, it would still be impossible
to tell whether a for loop was being evaluated for its value or for
its side-effects. e.g. in

  return when (list) -> { for x in list { foo(x) } }

there is no way to know whether the programmer intends to return a
promise for a list of foo() results, or merely let the caller know
when all the items have been processed.

> 2. Furthermore, we already have the accumulator syntax which covers these use cases. I recognize that the
accumulator syntax is ugly; its problem is that it is both too general and not general enough. This
proposal is not general enough.

I don't see why we need very general syntax. Syntax is for things you
do so often that short-cuts are useful. Creating a list by processing
each item in another list is a well-understood and common task, so
having syntax for it makes sense (e.g. replacing map() with list
comprehensions). Other things should only get special syntax once they
turn out to be useful (syntax for experiments, like lambda-args, is
fine, but I'm talking about enabled-by-default features for people who
want to use the language, not to experiment with it).

> I would rather see additional functional operations and use of lambda-args than additional syntax for
operations on lists and maps; especially if they are part of a system which solves the parallel iteration problem.
>
>
> As to your previous for-must-match pragma: I doubt its appropriateness for the language. However, it is
entirely appropriate to add it as a pragma as an *experiment* -- that's why we have pragmas. You may commit
for-must-match, provided that the documentation and *EVERY* code addition which supports it is marked
as experimental and part of for-must-match.
>
> One of the reasons I don't like it is that explicit testing in 'for' (especially with the ? such-that
pattern) is often useful.

$ cd esrc
$ grep 'for .*?.* in' **/*.emaker
[ no results ]

I also found no results in my code-base.

> Have you considered instead adding syntax which makes a pattern "must match", i.e. converts failure into
an exception (like Haskell's  <at>  "irrefutable patterns")? The default would be the opposite of what you
have, but the syntax would make it easy to express that case where it currently isn't.

It's the default that is the problem. In the code-base I'm looking at
now, I count 238 for loops, of which 2 should silently skip
non-matching items. The rest should throw an exception if there is no
match. The default should be for the 99% case, not the 1% case (maybe
other people use it differently, but these are the numbers for our
code).

Also, the current default tends to cause mysterious failures when
something silently fails to match. By contrast, forgetting to add the
"match" keyword causes a loud and obvious failure and is easily fixed.

--

-- 
Dr Thomas Leonard        http://0install.net/
GPG: 9242 9807 C985 3C07 44A6  8B9A AE07 8280 59A5 3CC1
GPG: DA98 25AE CAD0 8975 7CDA  BD8E 0713 3F96 CA74 D8BA

_______________________________________________
e-lang mailing list
e-lang <at> mail.eros-os.org
http://www.eros-os.org/mailman/listinfo/e-lang

Gmane