Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

List comprehension

1 view
Skip to first unread message

Mike Austin

unread,
Nov 18, 2009, 3:29:29 AM11/18/09
to
It seems every language is adopting list comprehensions, even JavaScript [1].
I understand its roots, but can it can be done much simpler, and without
specialized syntax?

For example:

[n * n | n <- [1..5], even n]

While shorter than using map and filter, it's a specialized syntax. If a
predicate were introduced into normal lambdas, you could get a more concise
result with no special syntax (other than the introduction of predicates):

mapf (\n |even n| -> n * n) [1..5]

A type 'void' would be used to indicate no value, and hence no concatenate.
It could also be used for loop constructs (sorry, destructive operations :)

x = 0
while (|x < 10| -> x = x + 1)

and a replacement for pattern matching:

x = |x > 1| -> 1;
|x < -1| -> -1;
_ -> x

Mike

[1] http://en.wikipedia.org/wiki/List_comprehension#Javascript_1.8

Torben Ægidius Mogensen

unread,
Nov 18, 2009, 7:34:11 AM11/18/09
to
Mike Austin <mi...@mike-nospam-austin.com> writes:

> It seems every language is adopting list comprehensions, even
> JavaScript [1]. I understand its roots, but can it can be done much
> simpler, and without specialized syntax?
>
> For example:
>
> [n * n | n <- [1..5], even n]
>
> While shorter than using map and filter, it's a specialized syntax.
> If a predicate were introduced into normal lambdas, you could get a
> more concise result with no special syntax (other than the
> introduction of predicates):
>
> mapf (\n |even n| -> n * n) [1..5]
>
> A type 'void' would be used to indicate no value, and hence no
> concatenate.

You would probably want to make 'void' a value rather than a separate
type, as the latter would preclude static typing.

Adding void values to all types can be tricky as you need to specify how
all operations behave on void values.

> It could also be used for loop constructs (sorry, destructive operations :)
>
> x = 0
> while (|x < 10| -> x = x + 1)
>
> and a replacement for pattern matching:
>
> x = |x > 1| -> 1;
> |x < -1| -> -1;
> _ -> x

You can indeed do so, and list comprehensions are typically translated
into combinations of maps and filters -- sometimes with subsequent
optimisation, but not necessarily so.

As you note, this requires closures to be implemented in the language.
You suggest adding predicates to lambdas so they define partial
functions (i.e., may be undefined) and define mapf etc. so they skip
undefined results. Many functional languages allow patterns or guards
in anonymous functions. Haskell (which allows both) has no mechanism
for catching the undefined cases -- it would just stop with an error
message, but Standard ML (which has patterns but not guards on lambdas)
throws an exception that can be caught by a function like mapf. But in
both cases it is more common to do one of:

1. Separate the test and operation into two functions, so you first use
the test to filter out unwanted elements and then apply the
operation to the rest.

2. Make a combined test-and-operate function return a value that is
either NONE or SOME(v), where v is the result. A function like mapf
can then ignore the NONE values and construct a list of the SOME
values. NONE and SOME are usually implemented so NONE is a null
value and SOME(v) is a non-null value v.

Note that you can translate list comprehensions to code that doesn't use
closures, so you can add them to languages that don't allow true
functional values.

Torben

Mike Austin

unread,
Nov 18, 2009, 4:05:30 PM11/18/09
to
Torben �gidius Mogensen wrote:
> Mike Austin <mi...@mike-nospam-austin.com> writes:
>
>> It seems every language is adopting list comprehensions, even
>> JavaScript [1]. I understand its roots, but can it can be done much
>> simpler, and without specialized syntax?
>>
>> For example:
>>
>> [n * n | n <- [1..5], even n]
>>
>> While shorter than using map and filter, it's a specialized syntax.
>> If a predicate were introduced into normal lambdas, you could get a
>> more concise result with no special syntax (other than the
>> introduction of predicates):
>>
>> mapf (\n |even n| -> n * n) [1..5]
>>
>> A type 'void' would be used to indicate no value, and hence no
>> concatenate.
>
> You would probably want to make 'void' a value rather than a separate
> type, as the latter would preclude static typing.

Or, as you state below, use Just and Nothing correct?

As you can probably tell, I'm not a heavy user of functional languages. I
thank you for being so clear and concise. I finally installed Haskell (hugs)
so I could experiment, but there are a few things I don't quite get.

This is as close as I could get without errors, but it returns
[Just 4,Nothing,Just 16,Nothing], not exactly what I'm looking for.

mapf f [] = []
mapf f (x:xs) = let result = f x in
case result of
Just a -> Just a : map f xs
Nothing -> map f xs

foo = \n -> if even n then Just (n * n) else Nothing

print (mapf foo [1..5])

I would like to construct a list with only "a" in stead of "Just a", but I get
"unification would give infinite type". Also, I don't understand why "Nothing"
is getting prepended to the list.

> Note that you can translate list comprehensions to code that doesn't use
> closures, so you can add them to languages that don't allow true
> functional values.

That's quite true. Wait, there are languages that don't use closures? :)

> Torben

Mike

Torben Ægidius Mogensen

unread,
Nov 19, 2009, 5:50:04 AM11/19/09
to
Mike Austin <mi...@mike-nospam-austin.com> writes:


> As you can probably tell, I'm not a heavy user of functional
> languages. I thank you for being so clear and concise. I finally
> installed Haskell (hugs) so I could experiment, but there are a few
> things I don't quite get.
>
> This is as close as I could get without errors, but it returns
> [Just 4,Nothing,Just 16,Nothing], not exactly what I'm looking for.
>
> mapf f [] = []
> mapf f (x:xs) = let result = f x in
> case result of
> Just a -> Just a : map f xs
> Nothing -> map f xs
>
> foo = \n -> if even n then Just (n * n) else Nothing
>
> print (mapf foo [1..5])
>
> I would like to construct a list with only "a" in stead of "Just a",
> but I get "unification would give infinite type". Also, I don't
> understand why "Nothing" is getting prepended to the list.

This is caused by calling map rather than mapf in the recursive calls.
This is also why the type system forced you to add Just in the RHS of
the first case branch. The following should work:


mapf f [] = []
mapf f (x:xs) = let result = f x in
case result of

Just a -> a : mapf f xs
Nothing -> mapf f xs

foo = \n -> if even n then Just (n * n) else Nothing

print (mapf foo [1..5])

Torben

Mike Austin

unread,
Nov 19, 2009, 2:57:00 PM11/19/09
to
Torben �gidius Mogensen wrote:
> Mike Austin <mi...@mike-nospam-austin.com> writes:
>
>
>> As you can probably tell, I'm not a heavy user of functional
>> languages. I thank you for being so clear and concise. I finally
>> installed Haskell (hugs) so I could experiment, but there are a few
>> things I don't quite get.
>>
>> This is as close as I could get without errors, but it returns
>> [Just 4,Nothing,Just 16,Nothing], not exactly what I'm looking for.
>>
>> mapf f [] = []
>> mapf f (x:xs) = let result = f x in
>> case result of
>> Just a -> Just a : map f xs
>> Nothing -> map f xs
>>
>> foo = \n -> if even n then Just (n * n) else Nothing
>>
>> print (mapf foo [1..5])
>>
>> I would like to construct a list with only "a" in stead of "Just a",
>> but I get "unification would give infinite type". Also, I don't
>> understand why "Nothing" is getting prepended to the list.
>
> This is caused by calling map rather than mapf in the recursive calls.
> This is also why the type system forced you to add Just in the RHS of
> the first case branch. The following should work:

Oops, I was so focused on the syntax that I missed the details, thanks.

> mapf f [] = []
> mapf f (x:xs) = let result = f x in
> case result of
> Just a -> a : mapf f xs
> Nothing -> mapf f xs
>
> foo = \n -> if even n then Just (n * n) else Nothing
>
> print (mapf foo [1..5])
>
> Torben

I'm beginning to like Haskell - defining operators is very cool:

print $ mapf (\n -> even n ? n * n) [1..10]

Although, as with anything, power comes with responsibility.

----------

module Main where

import System.Environment

True ? x = Just x
False ? _ = Nothing
infix 0 ?

mapf f [] = []
mapf f (x:xs) = let result = f x in
case result of
Just a -> a : mapf f xs
Nothing -> mapf f xs

main = do
print (mapf (\n -> even n ? n * n) [1..10])


Mike

0 new messages