Picking up from other languages
Tags: python, go, rust, languages
Published: 2019-08-13 07:28:58

---
title: Picking up from other languages
published: true
tags: python, go, rust, languages
canonical_url: https://ivergara.github.io/picking-up-from-other-languages.html
---

After many years of doing Python, first as a hobbyist and then in a professional setting, I’ve had to do some amount of Go programming. Also, I’ve been trying to explore other languages, primarily Rust, but have been touching some C while following some projects ([[1]](http://www.buildyourownlisp.com) and [[2]](https://handmadehero.org)).

There is value in learning from other languages for the sake of itself, one can end up deepening knowledge and getting enlightenment through it. I remember having that feeling while doing some Ruby stuff in a MOOC years ago. In this article, I want to explore some of the learnings I’ve had lately. For a comprehensive view on this topic and how it reflects mainly in Python [this article](https://www.curiousefficiency.org/posts/2015/10/languages-to-improve-your-python.html) is a great and insightful read.

## Typing

At the beginning of my Go tenure, it was slightly strange to be in the realm of a statically typed language enforced at compile time. Over the years, I had had some experience with compiled languages (Pascal, C, C#, Java), but never in any serious way. However, I quickly started enjoying the clarity that static types can provide, mostly through what the type checking enables in an IDEs like Jetbrain’s Goland.

While working with Python alongside Go, I found myself missing the clarity and helping “hand” that the static typing offers and enables. I was aware of `mypy` and the whole effort to add some type annotation into Python, but I was observant on it. This experience with Go drove me to start using `mypy`. In general, I’ve found it to be a great tool, and I’m trying to annotate as much as possible. It was a great help when doing a large scale refactoring/recycling of code, and in other instances, it has helped me to find some latent bugs “hidden” in the code. On the other hand, sometimes it can be slightly tricky to annotate arguments and variables accurately. Personally, I haven’t found the typing system in Python to be wrong or particularly bad as some people in the community have expressed, most likely because the things I’m doing are rather straightforward and don’t use really any metaprogramming or abuse the dynamic nature of Python (too much).

## from Go import

Additionally, from Go, I did pick up some of the implicit and explicit conventions of the broader community. The most prominent ones that come to mind being- Avoid the use of frivolous `else` statements ([link](https://medium.com/@matryer/line-of-sight-in-code-186dd7cdea88))- Use `gofmt` to format code as a standard- Conditional assignments ([link](https://www.calhoun.io/one-liner-if-statements-with-errors/))

The first point in the list is rather easy to implement while writing Python code. Although `try-except` blocks make the implementation of this idea to be slightly more indented. But one can still aim to use such a style pattern when possible.

For the second point, there are some tools like `autopep8`, `yapf`, and `black`. I personally favor the last one because it is intentionally designed to have very few options that the user can control. You might like or not the decisions, like with `gofmt`, but it keeps a high degree of consistency across different code bases and liberates you of trying (and failing) to follow a particular style. And I certainly prefer not having to worry and have some uniformity than pleasing my personal sense of code aesthetics. After some months of using **blackened** code, it’s hard for me to read messily styled code.

Now, regarding the last one, I’m waiting for Python 3.8 where the [_expression assignment_](https://www.python.org/dev/peps/pep-0572/) operator is being included. A simple example from PEP-572

```python
# Python 3.7
env_base = os.environ.get("PYTHONUSERBASE", None)
if env_base:
    return env_base

# Python 3.8
if env_base := os.environ.get("PYTHONUSERBASE", None):
    return env_base
```

This one surprised me because it’s not something I ever thought that I could need, but once I got to use it in Go, then I kept finding places where I’d have used it in Python.

## from \_ import

Since this post is not called something like “taking Go teachings to Python”, then it is time to introduce another language. As mentioned in the opening of this article, in my reduced spare time, I’ve been exploring Rust as it seems like a very complementary tool to have. I’ll not explain why Rust is cool and worthy, but I’ll mention that it has something extremely interesting that touches the topic of type annotations in Python.

Rust has a `Result<T, Err>` and `Option<T>` “types” that are used as return “values” of functions. They encode different possibilities of returned values.

In the first case, `Result<T, Err>`, a composite “object” is returned that either holds a value of type T or an error. Somehow, in my opinion, it feels rather similar to what Go 1.X does for error handling.

```go
// Go
x, err := double_number("10")
if err {
    fmt.Println("Error: ", err)
} else {
    fmt.Println("Doubled number: ", x)
}
```
```rust
// Rust
match double_number("10") {
    Ok(n) => println!("Doubled number: {:?}", n),
    Err(err) => println!("Error: {:?}", err),
}
```

This doesn’t have a direct equivalent in Python since it has `exceptions` instead of error values/types, but one could certainly make a similar pattern than the one in Go by catching exceptions and returning some value instead.

The second, `Option<T>`, encodes a value of type T or something akin to `None` in Python. Taking the example from the [documentation](https://doc.rust-lang.org/std/option/) ([playground](https://play.rust-lang.org/?code=%23!%5Ballow(unused)%5D%0Afn%20main()%20%7B%0Afn%20divide(numerator%3A%20f64%2C%20denominator%3A%20f64)%20-%3E%20Option%3Cf64%3E%20%7B%0A%20%20%20%20if%20denominator%20%3D%3D%200.0%20%7B%0A%20%20%20%20%20%20%20%20None%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20Some(numerator%20%2F%20denominator)%0A%20%20%20%20%7D%0A%7D%0A%0A%2F%2F%20The%20return%20value%20of%20the%20function%20is%20an%20option%0Alet%20result%20%3D%20divide(2.0%2C%203.0)%3B%0A%0A%2F%2F%20Pattern%20match%20to%20retrieve%20the%20value%0Amatch%20result%20%7B%0A%20%20%20%20%2F%2F%20The%20division%20was%20valid%0A%20%20%20%20Some(x)%20%3D%3E%20println!(%22Result%3A%20%7B%7D%22%2C%20x)%2C%0A%20%20%20%20%2F%2F%20The%20division%20was%20invalid%0A%20%20%20%20None%20%20%20%20%3D%3E%20println!(%22Cannot%20divide%20by%200%22)%2C%0A%7D%0A%7D))

```rust
fn divide(numerator: f64, denominator: f64) -> Option<f64> {
    if denominator == 0.0 {
        None
    } else {
        Some(numerator / denominator)
    }
}

// The return value of the function is an option
let result = divide(2.0, 3.0);

// Pattern match to retrieve the value
match result {
    // The division was valid
    Some(x) => println!("Result: {}", x),
    // The division was invalid
    None    => println!("Cannot divide by 0"),
}
```

After studying Rust a bit, I started to use the `Optional` type annotation in Python more often to explicitly signal that a function/method returns a value or `None`. Certainly returning or receiving a `None` in Python is something rather frequent, but formalizing it in a type makes it very explicit and clear. Taking the previous Rust example ported to Python, it’d look like the following

```python
from typing import Optional

def divide(numerator: float, denominator: float) -> Optional[float]:
    if denominator == 0.0:
        return None
    return numerator / denominator

result = divide(2.0, 3.0)

if result:
    print(f"Result: {result}")
else:
    print("Cannot divide by 0.")
```

Probably not the best example, but it shows how to apply this same pattern in a properly annotated form. Annotating and then running mypy will force you to cover your bases, thus reducing some silly bugs in the code.

Quite interestingly, I found [this](https://sobolevn.me/2019/02/python-exceptions-considered-an-antipattern) post showing some sort of implementation of these two ideas in Python. It’s worthy of checking it out, a very interesting experiment! I hope to try it out at some point.

Furthermore, the `Optional` type can also be used for argument annotations for arguments that might have a particular value of a type or `None`, as it could be when they have `None` as a default.

```python
from typing import Optional

def fib(n: int) -> int:  # declare n to be int
    a, b = 0, 1
    for _ in range(n):
        b, a = a + b, b
    return a

def cal(n: Optional[int]) -> None:
    print(fib(n))
```

If mypy is run over this code, it’ll complain at the `print(fib(n))` line saying `Argument 1 to "fib" has incompatible type "Optional[int]"; expected "int"`. It’s clear, at that point `n` could be some integer or `None`. This forces us to assure that `n` is actually a number beforehand and thus avoiding some ill-defined call and a highly likely runtime error. Thus, the annotation error in the previous code is fixed by modifying in a verbose mode the `cal` function as follows

```python
def cal(n: Optional[int]) -> None:
    if n:
        print(fib(n))
    else:
        print("No number provided!")
```

In my opinion, `mypy` has been a great tool when working on a moderately sized project. Others of the like of Dropbox and Instagram have found it invaluable in their efforts to move from Python 2 to Python 3.

## Closing remarks

Go and explore other languages! It will most likely provide great insight into your preferred language and your own programming style. There is no need to be an expert, but already, the challenge of understanding a new language provides plenty of teachings.

And regarding Python itself, use `black` and annotate code, particularly the code that gets into production and is being produced by a team.