I am reletively new to functional programming, to be honest about 2 days. I am trying to print out values from a Dict Int Int, but I can't seem to figure out how to pass this Dict Int Int to a function.
The error I am receiving is below.
The 1st argument to
mapis not what I expect:32| div [] (Dict.map toLiDict dict) ^^^^^^^^ This
toLiDictvalue is a:Dict Int Int -> Html msgBut
mapneeds the 1st argument to be:Dict Int Int -> b
The function toHtmlDict and toLiDict is the one's causing me this issue, they are currently commented out below. Also I am calling this from the view, div [] [ toHtmlDict model.uniqueValues ], I have this commented out as well.
Here is what I am currently working with; I posted the whole code as it would be easier if you should need anything else.
Here's a link on Ellie here that can be ran.
module Main exposing (main)
import Browser
import Dict exposing (Dict)
import Html.Attributes
import Html exposing (Html, button, div, text, strong, p, li, ul)
import Html.Events exposing (onClick)
type alias Model =
{ currentNumber : Int, clicks : Int, outputList : List(String), uniqueValues : Dict Int Int }
--{ currentNumber : Int, clicks : Int, history : String, outputList : List(String) }
initialModel : Model
initialModel =
{ currentNumber = 0, clicks = 0, outputList = [""], uniqueValues = Dict.fromList [(1,1)] } --Dict.empty should be default here...
--{ currentNumber = 0, clicks = 0, history = "Current outputs ...", outputList = ["Current outputs ...", " "] }
-- applies a new div for each element in the list
toHtmlList : List String -> Html msg
toHtmlList strings =
div [] (List.map toLi strings)
-- creates a div along with the text to be shown
toLi : String -> Html msg
toLi s =
div [] [ text s ]
-- --applies a new div for each element in the dictionary
-- toHtmlDict : Dict Int Int -> Html msg
-- toHtmlDict dict =
-- div [] (Dict.map toLiDict dict)
-- -- creates a div along with the text to be shown
-- toLiDict : Dict Int Int -> Html msg
-- toLiDict k =
-- div [] [ text "What here?" ]
type Msg
= Increment
| Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
-- Note: when assigning the model.currentNumber and then concatenating the value, it will not be updated...
Increment ->
{ model | currentNumber = model.currentNumber + 1, clicks = model.clicks + 1, outputList = model.outputList ++ [addToPage (oddOrEven(model.currentNumber + 1)) (model.currentNumber + 1)], uniqueValues = model.uniqueValues }
--{ model | currentNumber = model.currentNumber + 1, clicks = model.clicks + 1, history = model.history ++ addToPage (oddOrEven(model.currentNumber + 1)) (model.currentNumber + 1), outputList = model.outputList ++ [addToPage (oddOrEven(model.currentNumber + 1)) (model.currentNumber + 1)] }
Decrement ->
{ model | currentNumber = model.currentNumber - 1, clicks = model.clicks + 1, outputList = model.outputList ++ [addToPage (oddOrEven(model.currentNumber - 1)) (model.currentNumber - 1)]}
--{ model | currentNumber = model.currentNumber - 1, clicks = model.clicks + 1, history = model.history ++ addToPage (oddOrEven(model.currentNumber - 1)) (model.currentNumber - 1), outputList = model.outputList ++ [addToPage (oddOrEven(model.currentNumber - 1)) (model.currentNumber - 1)]}
view : Model -> Html Msg
view model =
Html.div []
[ button [ onClick Increment ] [ strong [Html.Attributes.style "color" "black"] [ text "+1" ]]
, button [ onClick Decrement ] [ strong [Html.Attributes.style "color" "red"] [ text "-1" ]]
, p [] []
--, div [] [ text <| "The current number is: ", strong [Html.Attributes.style "color" "red"] [ text <| String.fromInt model.currentNumber ], text " and it's ", text (oddOrEven model.currentNumber) ]
, div [] [ text <| "The current number is: ", strong [Html.Attributes.style "color" <| evenOddColor model.currentNumber] [ text <| String.fromInt model.currentNumber ], text " and it's ", strong [Html.Attributes.style "color" <| evenOddColor model.currentNumber ] [ text <| oddOrEven model.currentNumber ] ]
, div [] [ text "Total clicks: ", strong [Html.Attributes.style "color" "red"] [ text <| String.fromInt model.clicks ]]
, p [] []
, div [] [ strong [Html.Attributes.style "color" "Blue"] [ text "Unique values ..." ]]
, p [] []
--, div [] [ toHtmlDict model.uniqueValues ]
--, div [] [ text <| "The current number is: " ++ String.fromInt model.currentNumber ++ " and it's " ++ oddOrEven model.currentNumber ]
--, div [] [ text "Total clicks: ", strong [Html.Attributes.style "color" "red"] [ text <| String.fromInt model.clicks ]]
--, p [] []
, div [] [ strong [Html.Attributes.style "color" "Blue"] [ text "History ..." ]]
, p [] []
--, div [] [ text <| model.history ] - TEMPORARY
, div [] [ toHtmlList model.outputList ]
]
-- appendToList string number =
-- "Number was " ++ String.fromInt number ++ " and it was " ++ string
addToPage string number =
"Number was " ++ String.fromInt number ++ " and it was " ++ string
-- determines if number is even or odd
oddOrEven number =
if modBy 2 number == 0 then
"even"
else
"odd"
-- call another function with param
evenOddColor number =
if oddOrEven(number) == "even" then
"green"
else
"red"
main : Program () Model Msg
main =
Browser.sandbox
{ init = initialModel
, view = view
, update = update
}
Dict.map is a function that will transform the values of a Dict and return another Dict with the new values. This is not what you want here, but lets try to grok its type anyway, because it's a useful learning experience.
A Dicts full type is Dick k v, meaning that the type variables correspond to the type of its keys and values respectively. Dict.map, according to the documentation, has the type (k -> a -> b) -> Dict k a -> Dict k b. As its first argument it takes a function of two arguments, k and a, and that returns a b. maps second argument is a Dict k a, and it returns a Dict k b.
We can see that the k is the same in both the input and return Dicts, which means its type will remain the same, but the type variable for the values is different, a in the input and b in the return Dict. And the function similarly takes a as one of its inputs, along with a k, and returns a b. So for each of the key-value pairs in the input Dict, the mapping function will be called with its key, 'k', and value 'a', and is expected to return a b value.
For a Dict Int Int as you have, both k and v are Ints. If we substitute these in the type of Dict.map we get (Int -> Int -> b) -> Dict Int Int -> Dict Int b. We don't know what b is yet since that will be determined by the function we pass it, but we can at least see that the function expects two Int arguments`.
Meanwhile, the function you give it, toLiDict, has the type Dict Int Int -> Html msg which takes one argument that isn't an Int. This is what the error message is clumsily trying to convey. We could rewrite toLiDict to conform to what Dict.map expects, as a function Int -> Int -> Html msg, but that would have Dict.map return a Dict Int (Html msg), which isn't what you want. So let's drop that.
In general, map functions conventionally transform the elements of a collection without changing the type of collection.
If you want instead is to transform the elements of a collection into something else entirely, and there isn't something more specific to use, a "fold" is usually the right tool. Dict provides foldl and foldr, which basically does the same thing but in different order, foldl iterates over the elements from the "left" and foldr from the "right". If the order doesn't matter, use foldl because it's more efficient.
The type signature of Dict.foldl is (k -> v -> b -> b) -> b -> Dict k v -> b. That is, the transformation function now takes three arguments, the key, k, value, v, and a b which we'll call the accumulator, and returns a b. foldl itself also takes an additional argument, again a b, which will be the initial b value passed to the transformation function. The third argument is the Dict, and the return value is again a b.
For each key-value pair in the input Dict, foldl will, just like map, call the transformation function with its key and value. But it also provides a b which initially is the b value passed to foldl itself, but for subsequent iterations will be the b value returned from the transformation function. In that way the "accumulator" accumulates the return value.
Let's rewrite your code to use Dict.foldl instead:
toHtmlDict : Dict Int Int -> Html msg
toHtmlDict dict =
div [] (Dict.foldl toLiDict [] dict)
toLiDict : Int -> Int -> List (Html msg) -> List (Html msg)
toLiDict k v acc =
div [] [ text "What here?" ] :: acc
Here, toHtmlDict remains largely the same, but uses Dict.foldl instead of Dict.map and provides it an initial value of an empty list, [].
toLiDict sees bigger changes. Its type has changed to Int -> Int -> List (Html msg) -> List (Html msg), mneaning it takes the arguments: The key and value, both of which are Ints, and the accumulator is a List (Html msg), as is the return value.
But the implementation has barely changed. Instead of just returning an element directly, it's appended to the accumulator with :: acc.
And that's all there is to it. The result of the fold is the accumulated list of Html elements, as expected. If you plop the above code into yours, it will work.
Finally, I noted earlier that foldl is a good choice if there aren't more appropriate specialized function. And since the end result you want is a list, either Dict.values or Dict.toList, as @bdukes has suggested, probably are. These aren't as efficient as a fold, since you'll iterate trough the elements twice, once to convert to a list and then to map them, but this rarely matters in practice. Specialized functions are also more descriptive and documents your intent better, so use them if you can.
The definition of Dict.map is (k -> a -> b) -> Dict k a -> Dict k b. So it takes a function and a Dict and returns a new Dict. That mapping function takes the key and value, and returns a new value.
In your case, you're looking to return a List (Html Msg), rather than a Dict of anything. So, rather than using Dict.map, I would call Dict.values to get a List of the values, and then use List.map to transform those values into Html Msg. If you need both the key and value to generate the Html Msg, use Dict.toList instead, to get a List (k, v) (i.e. a List of tuples, where the tuple has the key and value).
toHtmlDict : Dict Int Int -> Html Msg
toHtmlDict dict =
div [] (List.map viewDictEntry (Dict.toList dict))
viewDictEntry : (Int, Int) -> Html Msg
viewDictEntry (key, value) =
li [] [ text (String.fromInt key), text " = ", text (String.fromInt value) ]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With