Skip to content

Instantly share code, notes, and snippets.

@dillonkearns
Last active April 20, 2023 06:19
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dillonkearns/def823d06ef2b880bf600f0c53468e78 to your computer and use it in GitHub Desktop.
Save dillonkearns/def823d06ef2b880bf600f0c53468e78 to your computer and use it in GitHub Desktop.

GPT Prompt Engineering Elm

Your goal is the following:

  • You will be given some Elm module which contains (Debug.todo "REPLACE").
  • The Elm code is in a compiling state (no compiler errors). HOWEVER, you are to replace (Debug.todo "REPLACE") in the input code with something that has the correct type and compiles.

Debug.todo in Elm is a special value which has any value (it could even be a function). But because the program compiles, we know that some valid code exists which will satisfy the Elm types. Your job is to find that code.

  • Do not change any code other than (Debug.todo "REPLACE") in ANY of the steps (including the final solution)
  • You are encouraged to break the problem down into sub-problems. These sub-problems could even use (Debug.todo "REPLACEME"), however your sub-problem should progress towards a solution so your next steps will eventually get rid of any references to Debug.todo (it should not appear at all in the final code)

You will also be given a list of the available top-level values and functions. Your solution should combine those values to give valid types. You are encouraged to combine those into sub problems and show the types of each of the results from the sub problems as you build up towards the desired type.

Some background rules about how the Elm type system works and how to get valid values with the desired Elm type:

An Elm type annotation looks like this

hello : String
hello = "Hello!"

The first line (with the :) is the type, this tells you the type of the value that follows. The second line is an Elm value, this value must be of the type that the annotation states or else there will be a compiler error.

If an Elm type annotation contains arrows in its annotation (->), then it means it is a function which can be passed arguments. If you pass an argument in it will remove the first value, this is called partial application.

greetFull : String -> String -> String
greetFull last first =
    "The name's " ++ last ++ ". " ++ first ++ " " ++ last

greetDoe : String -> String
greetDoe =
    greet "Doe"

greeting1 : String
greeting1 =
    greetDoe "John"

greeting2 : String
greeting2 =
    greetDoe "Jane"

Here is an example of the steps:

Input:

addTwo : String -> Int -> Int
addTwo input int =
    let
        inputToInt =
            (Debug.todo "REPLACE")
    in
    int + inputToInt input

Available Elm functions (you must use some of these but not all of them will be needed or useful for the solution):

  • (+) : number -> number -> number
  • String.toInt : String -> Maybe Int
  • String.fromInt : Int -> String
  • Maybe.withDefault : a -> Maybe a -> a

Step 1:

We can add a type annotation in our let binding to make our target type that we are trying to fill in explicit.

addTwo : String -> Int -> Int
addTwo input int =
    let
        inputToInt : Int
        inputToInt =
            (Debug.todo "REPLACE")
    in
    int + inputToInt input

Step 2:

String.toInt ALMOST gives us the desired type (Int), but it is wrapped in a Maybe. As an intermediary step, we create a let binding with a type annotation, and add a Debug.todo to fill in that would turn it into the desired type.

addTwo : String -> Int -> Int
addTwo input int =
    let
        intermediateValue : Maybe Int
        intermediateValue =
            String.toInt input

        intermediateValue2 : Maybe Int -> Int
        intermediateValue2 =
            (Debug.todo "REPLACE")

        inputToInt : Int
        inputToInt =
            maybeIntToInt intermediateValue
    in
    int + inputToInt input

Step 3:

Now we can fill in the final helper:

addTwo : String -> Int -> Int
addTwo input int =
    let
        intermediateValue : Maybe Int
        intermediateValue =
            String.toInt input

        intermediateValue2 : Maybe Int -> Int
        intermediateValue2 maybeInt =
            maybeInt |> Maybe.withDefault 0

        inputToInt : Int
        inputToInt =
            maybeIntToInt intermediateValue
    in
    int + inputToInt input

Finally, we can simplify the solution by performing an inline refactoring on some of the values:

addTwo : String -> Int -> Int
addTwo input int =
    let
        intermediateValue : Maybe Int
        intermediateValue =
            String.toInt input

        intermediateValue2 : Maybe Int -> Int
        intermediateValue2 maybeInt =
            maybeInt |> Maybe.withDefault 0

        inputToInt : Int
        inputToInt =
            input
                |> String.toInt
                |> Maybe.withDefault 0
    in
    int + inputToInt input

Following that same process with the following input.

Input:

expectJsonResponse : Decoder a -> Expect (Response a)
expectJsonResponse decoder =
    let
        findThis : Result Decode.Error a -> Result String a
        findThis =
            (Debug.todo "REPLACE")
    in
    Http.expectStringResponse
        (\response ->
            response.body
                |> Decode.decodeString decoder
                |> findThis
                |> Result.map
                    (\a ->
                        { body = a
                        , url = response.url
                        , status = response.status
                        , headers = response.headers
                        }
                    )
        )

Available Elm functions (you must use some of these but not all of them will be needed or useful for the solution):

  • Decode.string : Decoder String
  • Decode.bool : Decoder Bool
  • Decode.nullable : Decoder a -> Decoder (Maybe a)
  • Decode.list : Decoder a -> Decoder (List a)
  • Decode.array : Decoder a -> Decoder (Array a)
  • Decode.decodeString : Decoder a -> String -> Result Error a
  • Decode.decodeValue : Decoder a -> Value -> Result Error a
  • Decode.errorToString : Error -> String
  • Result.map : (a -> value) -> Result x a -> Result x value
  • Result.andThen : (a -> Result x b) -> Result x a -> Result x b
  • Result.mapError : (x -> y) -> Result x a -> Result y a
  • Result.withDefault : a -> Result x a -> a
  • Result.toMaybe : Result x a -> Maybe a
  • Result.fromMaybe : x -> Maybe a -> Result x a

Step 1:


GPT RESPONSE

We can add a type annotation in our let binding to make our target type that we are trying to fill in explicit.

expectJsonResponse : Decoder a -> Expect (Response a)
expectJsonResponse decoder =
    let
        findThis : Result Decode.Error a -> Result String a
        findThis =
            (Debug.todo "REPLACE")
    in
    Http.expectStringResponse
        (\response ->
            response.body
                |> Decode.decodeString decoder
                |> findThis
                |> Result.map
                    (\a ->
                        { body = a
                        , url = response.url
                        , status = response.status
                        , headers = response.headers
                        }
                    )
        )

Step 2:

We can use Result.mapError function to convert the Result Decode.Error a into Result String a. The Result.mapError function takes a function that converts the error type (Decode.Error in this case) to the desired error type (String in this case).

expectJsonResponse : Decoder a -> Expect (Response a)
expectJsonResponse decoder =
    let
        findThis : Result Decode.Error a -> Result String a
        findThis result =
            (Debug.todo "REPLACE")
    in
    Http.expectStringResponse
        (\response ->
            response.body
                |> Decode.decodeString decoder
                |> findThis
                |> Result.map
                    (\a ->
                        { body = a
                        , url = response.url
                        , status = response.status
                        , headers = response.headers
                        }
                    )
        )

Step 3:

Now we can fill in the final helper:

expectJsonResponse : Decoder a -> Expect (Response a)
expectJsonResponse decoder =
    let
        findThis : Result Decode.Error a -> Result String a
        findThis result =
            Result.mapError Decode.errorToString result
    in
    Http.expectStringResponse
        (\response ->
            response.body
                |> Decode.decodeString decoder
                |> findThis
                |> Result.map
                    (\a ->
                        { body = a
                        , url = response.url
                        , status = response.status
                        , headers = response.headers
                        }
                    )
        )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment