Search code examples
dictionaryhaskellcompiler-errorstype-mismatchdo-notation

Why this dictionary function is not working


I am trying to create and use a dictionary with code from here:

import Data.List (lookup)

insert :: Eq a => (a,b) -> [(a,b)] -> [(a,b)]
insert (a,b)  []           = [(a,b)]
insert (a,b) ((c,d):rest) = if a == c
    then (a,b) : rest
    else (c,d) : insert (a,b) rest

dict :: [(String, String)]
dict = [("", "")]

main = do 
    insert ("onekey", "onevalue") dict
    print dict
    print $ lookup "onekey" dict

But I am getting following error:

$ runghc rndict.hs

rndict.hs:22:1: error:
    • Couldn't match expected type ‘IO t0’ with actual type ‘[()]’
    • In the expression: main
      When checking the type of the IO action ‘main’

rndict.hs:24:9: error:
    • Couldn't match type ‘IO’ with ‘[]’
      Expected type: [()]
        Actual type: IO ()
    • In a stmt of a 'do' block: print dict
      In the expression:
        do { insert ("onekey", "onevalue") dict;
             print dict;
             print $ lookup "onekey" dict }
      In an equation for ‘main’:
          main
            = do { insert ("onekey", "onevalue") dict;
                   print dict;
                   print $ lookup "onekey" dict }

rndict.hs:25:9: error:
    • Couldn't match type ‘IO’ with ‘[]’
      Expected type: [()]
        Actual type: IO ()
    • In a stmt of a 'do' block: print $ lookup "onekey" dict
      In the expression:
        do { insert ("onekey", "onevalue") dict;
             print dict;
             print $ lookup "onekey" dict }
      In an equation for ‘main’:
          main
            = do { insert ("onekey", "onevalue") dict;
                   print dict;
                   print $ lookup "onekey" dict }

What is the problem and what is the correct way to use dictionaries in Haskell?


Solution

  • You need to use let to bind the new dictionary to a name:

    import Data.List (lookup)
    
    insert :: Eq a => (a,b) -> [(a,b)] -> [(a,b)]
    insert (a,b)  []           = [(a,b)]
    insert (a,b) ((c,d):rest) = if a == c
        then (a,b) : rest
        else (c,d) : insert (a,b) rest
    
    dict :: [(String, String)]
    dict = [("", "")]
    
    main = do
        let d = insert ("onekey", "onevalue") dict
        print d
        print $ lookup "onekey" d
    

    You ask about inserting multiple elements. For this you could use a fold to write a function called insertMany. You should probably be using foldl' but I'll leave that as an exercise to find out why.

    import Data.List (lookup)
    
    insert :: Eq a => (a,b) -> [(a,b)] -> [(a,b)]
    insert (a,b)  []           = [(a,b)]
    insert (a,b) ((c,d):rest) = if a == c
        then (a,b) : rest
        else (c,d) : insert (a,b) rest
    
    insertMany :: Eq a => [(a,b)] -> [(a,b)] -> [(a,b)]
    insertMany elements dict =
      foldl (flip insert) dict elements
    
    dict :: [(String, String)]
    dict = [("", "")]
    
    main = do
        let d = insert ("onekey", "onevalue") dict
        print d
        print $ lookup "onekey" d
        print $ insertMany [("onekey", "newvalue"), ("anotherkey", "anothervalue")]
          dict