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 map is not what I expect:
32| div [] (Dict.map toLiDict dict)
^^^^^^^^ This toLiDict value is a:
Dict Int Int -> Html msg
But map needs 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.
Dict.map
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.
Dict.foldl
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.
Dict.values and Dict.toList
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) ]
I'm new to functional programming and I want to create my own structure/signature called Dictionary. So far I have this in file called dictionary-en.sml:
(* The signature DICTIONARY defines a type and a programming interface for
the dictionary data structure. The data structure allows us to store
data in the form of (key, value) pairs and to query the data using a key. *)
signature DICTIONARY =
sig
(* The structure has to implement a dictionary type. It defines key type,
which has to support equality checking, and a value type for the data
stored in the dictionary. *)
type (''key, 'value) dict
(* Creates an empty dictionary. *)
val empty: (''key, 'value) dict
(* Returns true if a key exists in the dictionary. *)
val exists: (''key, 'value) dict -> ''key -> bool
end
And I have this in file solution.sml:
structure Dictionary :> DICTIONARY =
struct
type (''key, 'value) dict = (''key * 'value) list
val empty = []
fun exists dict key =
case dict of
[] => false
| (k, _ )::rep => if k = key
then true
else exists rep key
end
But I don't how to use this.
When I wrote in REPL:
- Dictionary.exists [(3,"c"), (5, "e"), (7, "g")] 3;
I got this error:
stdIn:1.2-3.7 Error: operator and operand do not agree [tycon mismatch]
operator domain: (''Z,'Y) Dictionary.dict
operand: ([int ty] * string) list
in expression:
Dictionary.exists ((3,"c") :: (5,"e") :: (<exp>,<exp>) :: nil)
Can someone please help me? I don't know what I did wrong.
In the function
fun exists dict key =
case dict of
[] => []
| (k, _ )::rep => if k = key
then true
else exists rep key
I spot two issues:
You can't return [] in one place and true in another.
Instead of if P then true else Q, write P orelse Q.
You're using :> which means that the module is opaque, so you can only access the things specified in the signature. The internal list representation is not mentioned in the signature, so you can't refer to a dict as a list, even though you may know that that's how it's implemented. This is a feature.
I would probably call exists for member, since List.exists is a higher-order predicate, e.g. List.exists (fn x => x > 5) [3, 6, 9]. You could also deviate from any standard library naming and say containsKey and containsValue, or something like that.
Besides the insert function that molbdnilo suggested, you may like to have a fromList function.
Here's a refactored version (comments omitted for brevity, but I think your comments are good!):
signature DICTIONARY =
sig
type (''key, 'value) dict
val empty: (''key, 'value) dict
val member: ''key -> (''key, 'value) dict -> bool
val insert: (''key * 'value) -> (''key, 'value) dict -> (''key, 'value) dict
val fromList: (''key * 'value) list -> (''key, 'value) dict
end
structure Dictionary :> DICTIONARY =
struct
type (''key, 'value) dict = (''key * 'value) list
val empty = []
fun member key [] = false
| member key ((key2, _)::dict) =
key = key2 orelse member key dict
fun insert (key, value) [] = [(key, value)]
| insert (key, value) ((key2, value2)::dict) =
if key = key2
then (key, value) :: dict
else (key2, value2) :: insert (key, value) dict
fun fromList pairs = foldl (fn (pair, dict) => insert pair dict) empty pairs
end
But since you're building a dictionary module, there are two things you want to consider:
Make it possible to use some kind of binary tree as internal representation, requiring that the keys can be ordered rather than compared for equality.
Since Standard ML doesn't have special syntax like ''key to express that something can be ordered (Haskell generalises this as type classes, but Standard ML has only the special syntax ''key), this is a good case for using functors, which is the name given to higher-order modules, aka parameterised modules.
Here's an example signature, functor and structure that you can fill out:
signature ORD = sig
type t
val compare : t * t -> order
end
signature DICT = sig
type key
type 'value dict
val empty: 'value dict
val member: key -> 'value dict -> bool
val insert: key * 'value -> 'value dict -> 'value dict
val fromList: (key * 'value) list -> 'value dict
end
functor Dict (Ord : ORD) :> DICT = struct
type key = Ord.t
type 'value dict = (key * 'value) list
val empty = ...
fun member _ _ = raise Fail "not implemented"
fun insert _ _ = raise Fail "not implemented"
fun fromList _ = raise Fail "not implemented"
end
At this point you can change type 'value dict into using a binary tree, and when you need to decide whether to go left or right in this binary tree, you can write:
case Ord.compare (key1, key2) of
LESS => ...
| EQUAL => ...
| GREATER => ...
And when you need a dictionary where the key is some particular orderable type, you can create a module using this functor:
structure IntDict = Dict(struct
type t = int
val compare = Int.compare
end)
structure StringDict = Dict(struct
type t = string
val compare = String.compare
end)
See also Standard ML functor examples for more examples.
You can't access the internal representation; the entire interface is given by the signature.
You need to add to the signature some way to create a dictionary without depending on the representation used in a particular structure.
For instance,
val insert : (''key * 'value) -> (''key, 'value) dict -> (''key, 'value) dict
would let you write
Dictionary.exists (Dictionary.insert (3,"c") Dictionary.empty) 3;
Implementation left as an exercise.
I have to create a type tree which would be used to store words, like every node of the tree would hold a letter and the list of the next characters (so words with the same root would share the same "part/branch of the tree). the tree is basically a n-ary one, used as a dictionnary.
All using Caml language
Well, I don't know if it's a homework or not but I'll still answer :
First, we need to define a signature type for letters.
module type LS = sig
type t
val compare : t -> t -> int
end
Then, we need to define our structure :
module Make (L : LS) = struct
module M = Map.Make(L)
type elt = L.t list
type t = { word : bool; branches : t M.t }
let empty = { word = false; branches = M.empty }
let is_empty t = not t.word && M.is_empty t.branches
let rec mem x t =
match x with
| [] -> t.word
| c :: cl -> try mem cl (M.find c t.branches)
with Not_found -> false
let rec add x t =
match x with
| [] -> if t.word then t else { t with word = true }
| c :: cl ->
let b = try M.find c t.branches with Not_found -> empty in
{ t with branches = M.add c (add cl b) t.branches }
end
Now, step by step :
module Make (L : LS) = struct is a functor that will return a new module if we give it a module of type LS as an argument
module M = Map.Make(L)
type elt = L.t list
type t = { word : bool; branches : t M.t }
This is the complex point, once you have it, everything begins clear. We need to represent a node (as you can see in the Wikipedia page of tries). My representation is this : a node is
a truth value stating that this node represent a word (which means that all the letters from the root to this node form a word)
the branches that goes from it. To represent this branches, I need a dictionary and luckily there's a Map functor in OCaml. So, my field branches is a field associating to some letters a trie (which is why I wrote that branches : t M.t). An element is then a list of letters and you'll find out why I chose this type rather than a string.
let empty = { word = false; branches = M.empty } the empty trie is the record with no branches (so, just the root), and this root is not a word (so word = false) (same idea for is_empty)
let rec mem x t =
match x with
| [] -> t.word
| c :: cl -> try mem cl (M.find c t.branches)
with Not_found -> false
Here it becomes interesting. My word being a list of letters, if I want to know if a word is in my trie, I need to make a recursive functions going through this list.
If I reached the point where my list is empty it means that I reached a node where the path from the root to it is composed by all the letters of my word. I just need to know, then, if the value word at this node is true or false.
If I still have at least one letter I need to find the branch corresponding to this letter.
If I find this branch (which will be a trie), I just need to make a recursive call to find the rest of the word (cl) in it
If I don't find it I know that my word doesn't exist in my trie so I can return false.
let rec add x t =
match x with
| [] -> if t.word then t else { t with word = true }
| c :: cl ->
let b = try M.find c t.branches with Not_found -> empty in
{ t with branches = M.add c (add cl b) t.branches }
Same idea. If I want to add a word :
If my list is empty it means that I added all the letters and I've reached the node corresponding to my word. In that case, if word is already true it means that this word was already added, I don't do anything. If word is false I just return the same branch (trie) but with word equal to true.
If my list contains at least a letter c, I find in the current node the branch corresponding to it (try M.find c t.branches with Not_found -> empty) and I there's no such branch, I just return an empty one and then I recursively add the rest of my letters to this branch and add this new branch to the branches of my current node associated to the letter c (if this branch already existed, it will be replaced since I use a dictionary)
Here, we start with the empty trie and we add the word to, top and tea.
In case we don't want to use functors, we can do it this way :
type elt = char list
type t = { word : bool; branches : (char * t) list }
let empty = { word = false; branches = [] }
let is_empty t = not t.word && t.branches = []
let find c l =
let rec aux = function
| [] -> raise Not_found
| (c', t) :: tl when c' = c -> t
| _ :: tl -> aux tl
in aux l
let rec mem x t =
match x with
| [] -> t.word
| c :: cl -> try mem cl (find c t.branches)
with Not_found -> false
let rec add x t =
match x with
| [] -> if t.word then t else { t with word = true }
| c :: cl ->
let b = try find c t.branches with Not_found -> empty in
{ t with branches = (c, (add cl b)) :: t.branches }
I try to make a function which take a record and a list of property names and return the values as a list of string:
Model -> (List String) -> (List String)
The goal is later to be able to render dynamically a record into an HTML table :
Model -> (List String) -> (Html Cmd)
I succeed to implement this:
import Html exposing (text)
type Field
= FieldInt Int
| FieldString String
type alias Model =
{ name: Field
, age: Field
}
user1 = Model (FieldString "foo") (FieldInt 3)
field2text: Field -> String
field2text field =
case field of
FieldInt value ->
toString value
FieldString value ->
value
updateModel: (List (a -> Field)) -> (List String)
model2values fns model =
List.map (\fn -> field2text <| fn model) fns
main =
text <| toString (model2values [.name, .age] user1)
How one would update the model2values function to be able to change the last line into:
main =
text <| toString (model2values ["name", "age"] user1)
There is no nice way, so you are left with:
toFieldGetter : String -> (a -> Field)
toFieldGetter : name =
case name of
"name" -> .name
...
model2values_ = model2values << List.map toFieldGetter
main =
text <| toString (model2values_ ["name", "age"] user1)
Use recursion and list pattern matching to define a function exists with the following type:
exists : (’a -> bool) -> ’a list -> bool
The function should have the following behavior: for any predicate (function with boolean range) p and list l, a well-typed invocation (exists p l) should evaluate to true iff there exists some element x of l for which p x evaluates to true.
I am new to OCaml and do not know where to begin, any help would be great.
#let rec exists p = function
[] -> false
| x::_ when p x -> true
| _::tl -> exists p tl;;
val exists : ('a -> bool) -> 'a list -> bool = <fun>
test :
# exists ((=)2) [4;3;2;1];;
- : bool = true
# exists ((=)2) [1;4];;
- : bool = false
About pattern matching :
let is_one = function
1 -> true
| x -> false
or
let is_one = function
1 -> true
| _ -> false (* _ in any case *)
or
let is_one = function
x when x=1 -> true
| _ -> false
test
# is_one 1;;
- : bool = true