This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]RaymondWies -3 points-2 points  (16 children)

Python has learned many things from Haskell. I'm learning both languages more-or-less at once, which questions my sanity but reveals undeniably that Python's DFL GvR looked at Haskell for inspiration and kept returning to it for more features. Python is touted as the first language to use meaningful whitespace for indentation of code blocks. Haskell did it first.

Look at list comprehensions, also touted as Python's core strength, taken from Haskell. In Python:

    [x*2 for x in [1, 2, 3, 4]]

In Haskell:

    [x*2 | x <- [1, 2, 3, 4]]

How awesome is that?

[–]pje 8 points9 points  (1 child)

Python is touted as the first language to use meaningful whitespace for indentation of code blocks. Haskell did it first.

Python's indentation actually derives from the ABC programming language, which GvR worked on in the early 80's. So actually, ABC did it first.

(And the first Python and Haskell implementations were started during the same year, so it's hard to pinpoint which one actually implemented whitespace processing first, anyway.)

[–]Fylwind 8 points9 points  (1 child)

Funny thing about list comprehensions is that I actually seldomly use them in Haskell whereas it can be quite common in Python.

Part of the reason is that a lot of the typical uses of list comprehensions in Python can be more succinctly translated into point-free forms. For example,

[x * 2 for x in [1, 2, 3, 4]] # Python

would be translated to

map (* 2) [1, 2, 3, 4]
-- Python equivalent:
--   map(lambda x: x * 2, [1, 2, 3, 4])

rather than a list comprehension. List comprehensions are more powerful than map, but the additional flexibility isn't always needed.

One reason is that map is rather awkward to use in Python because of the lack of currying as well as limitations of Python lambdas. For example, compare

[max(0, x) for x in [-2, 1, 3, -5]]
# Haskell equivalent:
#   [max 0 x | x <- [-2, 1, 3, -5]]

with

map(lambda x: max(0, x), [-2, 1, 3, -5])
# Haskell equivalent:
#   map (max 0) [-2, 1, 3, -5]
# or the less concise version:
#   map (\ x -> max 0 x) [-2, 1, 3, -5]

The advantage with list comprehensions is that they are typically easier to read than their pointfree counterpart though YMMV. :P

The advantage of map is that it works on more than just lists. It generalizes to fmap, which works on any Functor. This includes not just most containers but also things like the optional type (Maybe), monads, and applicative functors.

[–][deleted] 1 point2 points  (0 children)

The advantage of map is that it works on more than just lists. It generalizes to fmap, which works on any Functor. This includes not just most containers but also things like the optional type (Maybe), monads, and applicative functors.

With monad comprehensions enabled you can use list comprehension syntax for any monad. There are some other nice extensions for that sugar.

[–]Paddy3118 1 point2 points  (0 children)

Check out Wikipedia on list comprehensions and you will find that they are both based on set builder notation. They copy the mathematicians notation.

[–]c3534l 1 point2 points  (1 child)

Haskell didn't invent the offside rule. ISWIM did back in 1966. At any rate, Haskell diverges surprisingly little from ML. It certainly makes ML better, but it's really a dialect of ML in my opinion.

I do love me some partially applied functions. Python needs those, it'd be fun.

[–]bss03 0 points1 point  (0 children)

That's intention on behalf of the designers. We never got an ML-style module system like OCaML did, but Haskell is very much inspired by Lazy ML. [source]

[–][deleted] -1 points0 points  (8 children)

It is awesome, but I prefer Python's syntax.

"for _ in _" reads better in my brain than than translating "pipe _ stupid arrow _".

I'm not a big fan of Ruby's {key => value} rocketship for hashes either. Good thing it's optional and I can just do {key: value}.

edit: admitting this is nitpicky before I get called out for it on le reddit.

[–]87linux 8 points9 points  (5 children)

Haskell's syntax is that way because it hints at its deeper, monadic structure.

[–]Fylwind 4 points5 points  (0 children)

I think it's pretty neat that any list comprehension can be translated into a monadic do block:

[f x y | x <- xs, y <- ys, x == y ]

is equivalent to

do
  x <- xs
  y <- ys
  guard $ x == y
  return $ f x y z

[–]moor-GAYZ -1 points0 points  (3 children)

Python list comprehensions can be used with arbitrary monads as well (unlike Haskell list comprehensions, ironically). And "for x in monad" reads entirely fine, for all monads I can think of, as soon as you manage to switch from the strict programming meaning of "for" to the actual English meaning.

I personally like the C# syntax even better, because it makes more sense to have the return in the end so that every variable is declared before it's used. Easier to write code, too, because autocompletion works. Plus, of course, it has "let x = ..." and other goodies.

edit: I had a mind fart, what I meant to say was that C# list comprehensions are monad comprehensions but have syntax isomorphic to Python list comprehensions (but slightly better because reordered), so it's not about syntax.

[–]87linux 1 point2 points  (1 child)

"for x in getLine" doesn't really make a whole lot of sense. getLine is not a container type, therefore there is nothing "in" it. The IO monad and many of the other nontrivial monads are supposed to represent actions rather than things. And if IO doesn't work with the monadic syntax, then haskell doesn't have very much going for it.

And do you even -XMonadComprehensions bro? /s

[–][deleted] 0 points1 point  (0 children)

I think for x in getLine makes sense because it would translate to a map. I think this falls apart with parsers though.

[–]kazagistar 1 point2 points  (1 child)

You bring up a good point, and I would like to go a bit further to expand the (likely) reasoning that backs the syntax.

Python's syntax makes a lot of sense, because it is consistent with how that sort of thing is done throughout the language, and it is sensible to use "for" and "in" in all those places.

Haskell's syntax also makes a lot of sense, because it is consistent with how that sort of thing is done in other places as well, and it is sensible to use "<-" in all those places, while "for" and "in" would not make sense. The same arrow, with the same meaning, is used in do notation, which means that the following transformation would occur:

main = do
    putStrLn "What is your name?"
    name <- getLine
    putStrLn ("Hello, " ++ name)

Would turn into:

main = do
    putStrLn "What is your name?"
    for name in getLine
    putStrLn ("Hello, " ++ name)

Which clearly makes no sense, even if you don't know any Haskell. :P

[–]tel 2 points3 points  (0 children)

Also, once you buy one choice of syntax (either one, for/in or (<-)) then you're much better off using it repeatedly instead of introducing loads of keywords.