Macaulay2 / M2

The primary source code repository for Macaulay2, a system for computing in commutative algebra, algebraic geometry and related fields.
https://macaulay2.com
339 stars 228 forks source link

Better list comprehension #3185

Open mahrud opened 4 months ago

mahrud commented 4 months ago

Sometimes writing sum apply(L, a -> f(a)) gets too tedious, so we have defined syntax like sum(L, a -> f(a)). However, doing this to every similar function (e.g. directSum) is also tedious.

Can we allow something like { f : L } to mean the same thing as apply(L, f), so that we can write something like this, for instance?

N = directSum { a -> M(a) : 0..5 }

I think the precedence for : and -> might need to be adjusted.

Alternatively, a flipped for loop like this might be nice:

N = directSum list M(a) for a from 0 to 5
d-torrance commented 4 months ago

We already have f \ L and L / f as shortcuts for apply(L, f). That seems pretty compact!

mahrud commented 4 months ago

Compactness is not the main problem, mostly proximity to math notation and readability. directSum(L / a -> (...)) seems worse to me.

mahrud commented 4 months ago

To be more specific, here's why I don't think f \ L and L / f are any better:

i1 : max({1,2,3} / i -> 10*i)
stdio:1:13:(3): error: expected parenthesized argument list or symbol

Maybe this is solvable independently, but even then I think L / f and f \ L are good for piping and other purposes, not set comprehension.

d-torrance commented 4 months ago

-> has really low precedence, so maybe adding a new operator with even lower precedence would work. But I'm not sure what that would be. The natural choices I think would be : or | to look like set-builder notation, but both already exist in the language with higher precedence.

mikestillman commented 4 months ago

Personally, I generally prefer the L/f, f/L notation to list comprehensions in e.g. python, not just due to compactness, but also readability, although of course, this kind of like/dislike is a personal judgement. I suggest we try to improve usability of these notions to fix the kind of issues @mahrud found.

mahrud commented 4 months ago

Maybe the examples I gave weren't good ones, but I don't consider L/f and f\L to be list comprehension at all. For instance, here's an example from python:

fruits = ["apple", "banana", "cherry", "kiwi", "mango"]
newlist = [x for x in fruits if "a" in x]

We can't do this with L/f or even apply alone, we need select. This is what I think of as list comprehension: (ignore that this one is infinite) image

Haskell's is pretty nice:

s = [ 2*x | x <- [0..], x^2 > 3 ]
mahrud commented 4 months ago

In light of #1978, it would be great if the following were equivalent:

{ 2^x for x in {1..5,3,2:2} if odd x } -- syntactic sugar akin to { 2^x | x \in {1,2,3,4,5,3,2,2}, odd x }
for x in splice(1..5,3,2:2) list if odd x then x^2 else continue

The benefit is that if we wanted an array, we could simply switch to:

[2^x for x in {1..5,3,2:2} if odd x]

As a first step, at least this should work:

for x in splice(1..5,3,2:2) if odd x then x^2