Stream: beginners

Topic: Idiomatic roc code


view this post on Zulip Chris Vouga (Aug 07 2024 at 06:18):

Hi all, I'm learning roc by building an app. I just wrote our this code to get something working

# https://github.com/roc-lang/basic-webserver/tree/main/examples
# https://roc-lang.github.io/basic-webserver/SQLite3/
module [init, Config]

import pf.Task
import pf.SQLite3
import KeyValueStore
import Logger exposing [Logger]

Config : {
    sqlitePath : Str,
    logger : Logger,
}

get : Config -> (Str -> Task.Task Str [NotFound, Errored Str])
get = \config -> \key ->
        executed <-
            SQLite3.execute {
                path: config.sqlitePath,
                query: "SELECT key, value FROM key_value WHERE key = :key;",
                bindings: [{ name: ":key", value: key }],
            }
            |> Task.attempt

        when executed is
            Err err -> Task.err (Errored (SQLite3.errToStr err))
            Ok rows ->
                firstRow = List.first rows
                when firstRow is
                    Err _ -> Task.err NotFound
                    Ok row ->
                        firstColumn = List.first row
                        when firstColumn is
                            Err _ -> Task.err (Errored "No value")
                            Ok columnValue ->
                                when columnValue is
                                    String columnValueStr -> Task.ok columnValueStr
                                    _ -> Task.err (Errored "No value")

I'm now going to refactor this code to make it flat. I'm curious how any you guys would refactor this code? Is there a single right way or multiple right ways?

view this post on Zulip Luke Boswell (Aug 07 2024 at 06:54):

Maybe something like this... I haven't tested it as it's only a module.

get : Config -> (Str -> Task.Task Str [NotFound, Errored Str])
get = \config -> \key ->
    rows =
        SQLite3.execute {
            path: config.sqlitePath,
            query: "SELECT key, value FROM key_value WHERE key = :key;",
            bindings: [{ name: ":key", value: key }],
        }
        |> Task.result!

    when rows is
        Ok [] -> Task.err NotFound
        Ok [[String _, String value]] -> Task.ok value
        Ok _ -> Task.err (Errored "unexpected values returned, expected key and value")
        Err (SQLError _ msg) -> Task.err (Errored msg)

edits: some fixes

view this post on Zulip Luke Boswell (Aug 07 2024 at 06:54):

It's very similar to the text editor example I just posted

view this post on Zulip Luke Boswell (Aug 07 2024 at 06:56):

Also, note the API for Sqlite will hopefully change soon with some awesome new improvements Brendan has made. If you're interested to see what that looks like here is the upgrade for the roc-htmx-tailwindcss-demo app I've been using to test that upgrade.

view this post on Zulip Luke Boswell (Aug 07 2024 at 06:57):

It uses the new record builders to decode each row into a record, so you can can easily get a List User or whatever you're expecting.

view this post on Zulip Chris Vouga (Aug 07 2024 at 19:35):

That refactor is clean. I'm gonna use that. Thanks! The upcoming sqlite API looks very clean too. For the demo app, what are you guys using for db migrations? I've been using https://github.com/amacneil/dbmate.

view this post on Zulip Brendan Hansknecht (Aug 07 2024 at 21:24):

One other change. Why select key when it isn't being used

view this post on Zulip Brendan Hansknecht (Aug 07 2024 at 21:24):

Just drop it from the sql query

view this post on Zulip Brendan Hansknecht (Aug 07 2024 at 21:25):

And in the new API will just be queryOne


Last updated: Jul 06 2025 at 12:14 UTC