#Is this the best way to skip elements in decoder ?

1 messages · Page 1 of 1 (latest)

cerulean jackal
#

Hoi !
I'm very new in Gleam and so I wanted to create a tiny application to grab and fetch music.
So I learned how to do http and start to understand how to wired my brain to use the Result type however I'm not yet flaunt in my thinking.
So earlier I needed to parse from the Tidal API tracks from albums but in their response data they send both a type for video and track but I only cared about track.
So I wanted to make a decoder using the decode package however I didn't found somewhere some builtin function to like decode list with a filter.
So I did find a code that work by making the list decode Option(Track) than later use a filter_map to extract the Some(Track) but is there better way to approach the problem or do you think this is the best I can do ?

pub fn album_tracks_decoder() -> decode.Decoder(AlbumTracks) {
  use limit <- decode.field("limit", decode.int)
  use offset <- decode.field("offset", decode.int)
  use total_number_of_items <- decode.field("totalNumberOfItems", decode.int)
  use tracks <- decode.field(
    "items",
    {
      use typed <- decode.field("type", decode.string)
      case typed {
        "track" -> {
          use track <- decode.field("item", track_decoder())
          decode.success(Some(track))
        }
        _ -> decode.success(None)
      }
    }
      |> decode.list,
  )
  let tracks = list.filter_map(tracks, fn(x) { option.to_result(x, Nil) })

  decode.success(AlbumTracks(limit:, offset:, total_number_of_items:, tracks:))
}
maiden raptor
#

result.values and decode.map can simplify that a bit

pub fn album_tracks_decoder() -> decode.Decoder(AlbumTracks) {
  use limit <- decode.field("limit", decode.int)
  use offset <- decode.field("offset", decode.int)
  use total_number_of_items <- decode.field("totalNumberOfItems", decode.int)
  use tracks <- decode.field(
    "items",
    {
      use type_ <- decode.field("type", decode.string)
      case type_ {
        "track" -> {
          use track <- decode.field("item", track_decoder())
          decode.success(Ok(track))
        }
        _ -> decode.success(Error(Nil))
      }
    }
      |> decode.list
      |> decode.map(result.values),
  )
  decode.success(AlbumTracks(limit:, offset:, total_number_of_items:, tracks:))
}

there's also option.values, but i think using Result here is the better fit

cerulean jackal
maiden raptor
#

In gleam it's not very typical for anything to return an Option

#

Option is usually for situations where you want to optionally accept a value, not for indicating the lack of a return value, (see for example how dict.get in the stdlib returns a Result(a, Nil))

#

From the docs in the option module

cerulean jackal
maiden raptor
#

Yeah

cerulean jackal
# maiden raptor From the docs in the option module

Oh yes I did read that twice carefully. I remember that’s make me think if I make a database for getting a data, I should make an error for not found and not returning a « None ». But I did not realize until now EVERYTHING is a function. So this logic applied EVERYWHERE.

maiden raptor
#

You don't need to make a single variant error for something like NotFound if that's the only possible error. In that case it's more conventional to return Result(a, Nil) like dict.get does

#

But for situations where there's more than one thing that can go wrong, then yeah make a custom error type

cerulean jackal
#

(Except when I really need to filter error than I need specialized error type)

#

(Or if this was a Library of course)

maiden raptor
#

Snag is mainly for collecting application errors that will be presented to a user, not for errors that a library returns or that will be handled elsewhere in your application

cerulean jackal
maiden raptor
#

Cool c:

cerulean jackal
#

Tbh I’m still debating to myself if I prefer thiserror or anyhow in Rust. SO having the same dilemma in Gleam is hard despair