Try   HackMD

Here is a programming language more Pythonic than Python

First, let's start with what do we mean by being pythonic.

By googling this term, here is the list of top 3 links:

Have List Comprehension

From those links, I think it's fare to say using list comprehension is a big part of being pythonic. Especially this one is mentioned in all three links.

Let's take one example from second link:

Not so pythonic:

arr = [1, 2, 3, 4, 5, 6]
length = len(arr)
for i in range(0, length):
    if arr[i] % 2 == 0:
        arr[i] *= 2

Pythonic:

arr = [1, 2, 3, 4, 5, 6]
arr = [x * 2 if x % 2 == 0 else x for x in arr]

Unpack

And this one is brought up in 1st and 3rd links.

Pythonic

a, (b, c) = 1, (2, 3)

Use some builtin functions

Also from second link

Not so pythonic:

a = 10
b = 1000
total_sum = 0
while b >= a:
    total_sum += a
    a += 1

Pythonic:

total_sum = sum(range(10, 1001))

There are other languages as pythonic as python

There are also for-in syntax but I believe we've had some sense on what do we refer when we say "pythonic".

So let's see how another language H looks like, without revealing its full name for now.

List comprehension

Python:

arr = [1, 2, 3, 4, 5, 6]
arr = [x * 2 if x % 2 == 0 else x for x in arr]

H:

arr  = [1, 2, 3, 4, 5, 6]
arr' = [if x `mod` 2 == 0 then x * 2 else x | x <- arr]

The differences are

  • | <- instead of for in
  • if then else instead of if else

Unpack

Python

a, (b, c) = 1, (2, 3)

H:

(a, (b, c)) = (1, (2, 3))

Difference: the parentheses of tuples are more consistent.

Use some builtin functions

Python:

total_sum = sum(range(10, 1001))

H:

total_sum = sum(range(10, 1000))

Difference: the second parameter of range is inclusive.

And TBH, normally it would be written like this:

total_sum = sum [10..1000]

Let's reveal the full name of the language

So what is that language?

The answer is Haskell!

And next, let's have some examples showing Haskell is more pythonic than python in my opinion.

Repeated side effects

Let's say if we want to do print hello world 5 times:

in Python

for _ in range(5):
  print('hello world')

in Haskell

replicateM_ 5 (putStrLn "Hello World")

Why I think later one is more pythonic? Because it doesn't introduce new syntax, replicateM_ is simply just a function which repeats the given action. Where as in Python code, it's recommended to use for-in for this purpose, in which we need to introduce an unused variable binding _.

Pattern Matching

The "unpack" aforementioned can be seen as a special case of Pattern Matching. And pattern matching in Haskell is more versatile.

list = [Right 1, Left 2, Right 3]
[x | (Right x) <- list] 
-- return: [1, 3]

This means getting list of item only having Right as the contructor.

Pythonic is about Simplicity.

Above are two examples, which reveal some aspects of simplicity, for which Haskell can do better. However, this is the very tip of that, and there are many more other interesting concepts in Haskell, which, without using extra syntax, can be used to compose more complex logic I consider Pythonic. For example: async-await is a relatively new syntax in Python. And there aren't many pythonic discussions about it. On the contrary, Haskell can use the same syntax to cover them all.

What's my point?

I think

  1. Haskell is extremely concise, pretty.
  2. If you think learning Python is easy, then it shouldn't be hard to learn Haskell.

You might ask, why should I learn a language is not widely used?

My answer to that is, although in the end, you might not use Haskell directly in your job, the knowledge gained from learning it is extremely universal and can be adopted on almost all programming problems.

And since I don't think it should be hard to learn whatsoever, I'll try to write a beginner tutorial which follows the common python beginner curriculum structure.