I'm new in Haskell and I need to define an empty Data.map and assigning a "list of integers" (e.g. [1,2,3]) to its keys by using insert function and also updating the values. Then looking up the key values.
What I have tried so far is :
import qualified Data.Map
foo num =
let
my_map = Data.Map.empty
new_map = bar my_map num 1
in
Data.Map.lookup 1 new_map
bar my_map num c =
if c > num then my_map
else
Data.Map.insert c [c] my_map
bar my_map num c+1
This code doesn't work.
Could you have a simple example please?
4 Answers 4
People normally import the Data.Map
module with this boilerplate:
import Data.Map (Map)
import qualified Data.Map as Map
The idea is that since many of the names in the module clash with the Prelude
and other modules, you want to use them as qualified names—but not for the Map
type itself. And the as Map
bit in the second line saves you from having to type as much—you just say Map.map
, Map.empty
, etc.
Now, the easiest and most common way of constructing a map is to use the fromList
function in the module. This constructs a Map
from a list of key/value pairs: Map.fromList :: Ord k => [(k, v)] -> Map k v
. To construct this list of key/value pairs you can use the full power of Haskell's list processing functions, like in this example:
myMap :: Integer -> Map Integer [Integer]
myMap n = Map.fromList (map makePair [1..n])
where makePair x = (x, [x])
Example output in GHCI:
>>> myMap 3
fromList [(1,[1]),(2,[2]),(3,[3])]
Note that the Map
type even prints itself as a fromList
call that would reconstruct it. Why? Because again, this function really is the most common way to build a Map
.
In contrast, what you're doing in your code is you're trying to write an imperative-style loop that successively augments an initial empty map with entries one at a time. The Haskell equivalent of loops is list functions. In my version I used the following:
[1..n]
—generate a list of the integers from1
up ton
.map
—apply a function to each element of the list.Map.fromList
—build aMap
from a list of key/value pairs.
And to further demonstrate that point, if you look at the source code for Map.fromList
, it's actually defined using a list fold function.
My advise to you: study lists and the Data.List
module first before you tackle Map
. In particular:
- Learn what functions are available there and what to do.
- Study the
foldr
function from that module—how to use it, and how to write it. - Learn how to write your own versions of
map
,filter
andfind
in terms offoldr
.
Here's a small program to demonstrate this functionality:
module Main where
import qualified Data.Map as M
main = do
let emptyMap = M.empty
mapWithKeys = M.insert 5 "Four" emptyMap
mapWithKeys' = M.insert 5 "Five" mapWithKeys
putStrLn $ mapWithKeys' M.! 5
The program will insert "Four" with the key 5, then update the value to "Five", and finally look it up and print it.
1 Comment
let
for each binding. Just one, with appropriate indentation, is enough.Take a look at Data.Map
in base.
While this package does export empty
which creates a map, it is easier to build one with
myMap = Data.Map.fromList [(1,"hello"), (3,"goodbye")]
fromList
takes a list of (key,value) tuples, and creates a map. This is if you know all the key-value pairs you need at the time of construction.
You can use (!)
or Data.Map.lookup
to access elements
2 Comments
fromList
than empty
?Your code suggests you have a few misunderstandings.
import qualified Data.Map
foo num =
let
my_map = Data.Map.empty
new_map = bar my_map num 1
in
Data.Map.lookup 1 map
I see you use the map
variable but probably meant new_map
. Since Haskell defines a function named map
, the compiler would tell you a type error. Purely for readability, reducing white space and adding type signatures helps a lot.
-- foo takes an `Int` and produces a Maybe [Int].
foo :: Int -> Maybe [Int]
foo num =
let my_map = Data.Map.empty
new_map = bar my_map num 1
in Data.Map.lookup 1 new_map
Now let's see bar
:
bar my_map num c =
if c > num then my_map
else
Data.Map.insert c [c] my_map
bar my_map num c+1
There are a few problems here:
- The
then
andelse
keywords should be in the same column - Your
c+1
should have parentheses - You need to use a
let
binding - this is not a mutable map, so by inserting you are actually creating a new map with the new value.
So:
bar :: Map Int [Int] -> Int -> Int -> Map Int [Int]
bar my_map num c =
if c > num
then my_map
else let my_new_map = Data.Map.insert c [c] my_map
in bar my_new_map num (c+1)
2 Comments
foo
was passing [Int]
while bar
was only accepting Int
- my mistake. It wasn't that bar
was comparing Int
to [Int]
(notice the type was always accepting just Int
), but I mis-used bar
in foo
. I'm made a quick adjustment to foo
to make it pass just Int
and not a singleton list of Int
s.
Data.Map
.