this post was submitted on 22 Nov 2023
25 points (90.3% liked)

Programming

17444 readers
187 users here now

Welcome to the main community in programming.dev! Feel free to post anything relating to programming here!

Cross posting is strongly encouraged in the instance. If you feel your post or another person's post makes sense in another community cross post into it.

Hope you enjoy the instance!

Rules

Rules

  • Follow the programming.dev instance rules
  • Keep content related to programming in some way
  • If you're posting long videos try to add in some form of tldr for those who don't want to watch videos

Wormhole

Follow the wormhole through a path of communities !webdev@programming.dev



founded 1 year ago
MODERATORS
 

I just recently started documenting my code as it helped me. Though I feel like my documentations are a bit too verbose and probably unneeded on obvious parts of my code.

So I started commenting above a few lines of code and explain it in a short sentence what I do or why I do that, then leave a space under it for the next line so it is easier to read.

What do you think about this?

Edit: real code example from one of my projects:

async def discord_login_callback(request: HttpRequest) -> HttpResponseRedirect:
    async def exchange_oauth2_code(code: str) -> str | None:
        data = {
            'grant_type': 'authorization_code',
            'code': code,
            'redirect_uri': OAUTH2_REDIRECT_URI
        }
        headers = {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
        async with httpx.AsyncClient() as client:
            # get user's access and refresh tokens
            response = await client.post(f"{BASE_API_URI}/oauth2/token", data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET))
            if response.status_code == 200:
                access_token, refresh_token = response.json()["access_token"], response.json()["refresh_token"]

                # get user data via discord's api
                user_data = await client.get(f"{BASE_API_URI}/users/@me", headers={"Authorization": f"Bearer {access_token}"})
                user_data = user_data.json()
                user_data.update({"access_token": access_token, "refresh_token": refresh_token}) # add tokens to user_data

                return user_data, None
            else:
                # if any error occurs, return error context
                context = generate_error_dictionary("An error occurred while trying to get user's access and refresh tokens", f"Response Status: {response.status_code}\nError: {response.content}")
                return None, context

    code = request.GET.get("code")
    user, context = await exchange_oauth2_code(code)

    # login if user's discord user data is returned
    if user:
        discord_user = await aauthenticate(request, user=user)
        await alogin(request, user=discord_user, backend="index.auth.DiscordAuthenticationBackend")
        return redirect("index")
    else:
        return render(request, "index/errorPage.html", context)
you are viewing a single comment's thread
view the rest of the comments
[–] aluminium@lemmy.world 3 points 1 year ago* (last edited 1 year ago) (1 children)

For new code I'm writing I'm using mostly JsDoc function headers on public methods of classes and exported functions. With one or two sentences explaining what function does.

Also try to explain what to expect in edge cases, like when you pass am empty string, null, ... stuff of that nature - for which I then create unit tests.

I also always mention if a function is pure or not or if a method changes the state of its object. On a sidenote I find it odd that almost no language has a keyword for pure functions or readonly methods.

If I add a big new chunk of code that spans multiple files but is somewhat closed off, I create a md file explaining the big picture. For example I recently added my own closed off library to my angular frontend that handles websocket stuff like subscribing, unsubscribing, buffering, pausing,... for which a created a md file explaining it.

[–] TellusChaosovich@lemmy.world 3 points 1 year ago (1 children)

What is a pure function? Never heard that before.

[–] aluminium@lemmy.world 4 points 1 year ago* (last edited 1 year ago) (1 children)

Essentially a function that doesn't produce side effects, like modifying variables outside of its scope or modifying the function parameters. This something you should always try to incorporate into your code as it makes it much easier to test and makes the function's use less risky since you don't relay on external unrelated values.

To give you an example in JavaScript, here are two ways to replace certain numbers from an other list of numbers with the number 0

first a way to do it with a non pure function :

let bannedNumbers = [4,6]

const nums = [0,1,2,3,4,5,6,7,8,9]

function replaceWithZero(nums){
    for (let i = 0 ; i < nums.length; i++){
        if (bannedNumbers.includes(nums[i])){
            nums[i] = 0
        }
    }
}
replaceWithZero(nums)
console.log("numbers are : ", nums)

here the function replaceWithZero does two things that make it impure. First it modifies its parameter. This can lead to issues, for example if you have Second it uses a non-constant variable outside of its scope (bannedNumbers). Which is bad because if somewhere else in the code someone changes bannedNumbers the behavior of the function changes.

A proper pure implementation could look something like this :

const nums = [0,1,2,3,4,5,6,7,8,9]
function repalceWithZero(nums){
    const  bannedNumbers = [4,6]
    const result = []
    for(const num of nums){
        result.push(bannedNumbers.includes(num) ? 0 : num)
    }
    return result
}
const replaced = replaceWithZero(nums)
console.log("numbers are : ", replaced)

Here we are not modifying anything outside of the function's scope or its parameters. This means that no matter where, when and how often we call this function it will always behave the same when given the same inputs! This is the whole goal of pure functions.

Obviously in practice can't make everything 100% pure, for example when making a HTTP request you are always dependent on external factors. But you can try to minimize external factors by making the HTTP request, and the running the result only through pure functions.

[–] Miaou@jlai.lu 2 points 1 year ago

I really wouldn't call anything that hits the network pure, because errors are quite likely. But I guess we all put the bar at a different level, I would not count logging as a side effect yet I've been bitten by overly verbose logs in hot loops.

const-ness gives a mini version of purity, although nothing prevents someone from opening /etc/lol in a const function... I think GCC has a pure attribute but I don't think it's enforced by the compiler, only used for optimizations