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 toDebug.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:
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
}
)
)