Storing requests in a List

Hello everyone.

I decided to build a chat bot with Elm to help me gain more experience with the language (I think it’s always good to practice with close-to-real projects). I chose a chat bot because I’ve built a few before in different languages, so I though it would be a fun experiment.

The API I got working for defining the “blueprint” of the bot looks like this:

root =
    Bot.tree "root"
        |> Bot.send "Hello, %name%, I hope you're doing fine."
        |> Bot.wait (1 * Time.second)
        |> Bot.send "Please choose an option"
        |> Bot.options
            [ ( "Option 1", option1Tree )
            , ( "Option 2", option2Tree )
            ]

This code works fine, and I’m fairly happy with how it looks. There are other features already, such as goto to navigate back and forth in the bot tree.

The part I’m having trouble with is HTTP requests. At some point, usually all bots needs to use an API to search or notify the server about something the user typed. The request should be stored in the bot tree so it can be called when the user gets to that point in the conversation.

From what I understood, a request should have:

  • URL (String)
  • Decoder a
  • Some message (Result Http.Error a -> Msg)
  • Formatter (Result Http.Error a -> BotMessage)

But I can’t seem to find a way to store a list of requests with different decoder types and different messages. The only type they share is the formatter output, that should always be a BotMessage.

The API I was hoping for defining requests would look something like this:

tree =
    Bot.tree
        |> Bot.ask "Tell me a github username" "username"
        |> Bot.request "https://api.github.com/users/%username%/repos" githubDecoder githubFormatter

But I couldn’t figure out the types to make it work.

Am I approaching this problem in the wrong way? I know this is an broad question, but any help is appreciated!

1 Like

Sounds like a fun project! To get a little more context, I’m curious why you want to store a list of requests? Also, what’s the difference between a message and a formatter? :slightly_smiling_face:

1 Like

Thank you for being interested!

To give a little more context, the bot’s blueprint has a unique ID and a list of Action. The action looks something like this:

type Action
    = SendMessage String
    | ShowOptions (Dict String BotTree)
    | Wait Float
    | SendRequest ??

When calling the functions I mentioned earlier (send, ask, wait, etc.) we’re just appending an Action to the bot’s blueprint. In order to execute the bot there is a different data structure called Sesssion that contains the bot’s blueprint, the history of messages, the user’s profile, the position in the list and what’s next.

The request is my biggest problem though, because I can’t find a way to encode it in an Action. The pseudo-code bellow shows my attempt to declare the function

tree =
    Bot.tree
        |> Bot.ask "Tell me a github username" "username"
        |> Bot.request "https://api.github.com/users/%username%/repos" githubDecoder githubFormatter

-- An attempt to implement request
request : String -> Decode.Decoder a -> (Result Http.Error a -> BotMsg) -> BotTree -> BotTree
request url decoder formatter bot =
    { bot | actions = append bot.actions, SendRequest ?? }

The formatter is what gets called after Elm calls the update function with the response, but in order to send the request, Http.send needs a message that also contains an a type.

Http.send ?? myRequest

I think I could get this to work if I choose, let’s say, String as the only type requests are allowed to decode.

Thanks for your time!

One option is to have all of the Decoder as be Decoder BotMsg.

1 Like

Ok, I think I understood what was missing for me. I though Decoders could not be manipulated before running them. For example, let’s say a have a Decoder (List String) that I wanna join into a single string with the elements separated by comma. I would perform this conversion in the update function after the requests completes. I didn’t know about the Decode.andThen function, which pretty much solves this problem.

botDecodeer =
    myComplexDecoder
        |> Json.andThen toBotMsg

In this way, all decoders would produce the same type! Thank you all for the help!

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.