Home > front end >  Map.insert prompting an error in Haskell without reason
Map.insert prompting an error in Haskell without reason

Time:04-15

This is the function I am using:

threeWordFunction :: [String] -> Map.Map String [String] -> String
threeWordFunction [x, y, z] orbitingData = do
     Map.insert z [x, y] orbitingData
     ""

Resulting in the following error:

src\MyLib.hs:40:6: error:
    * Couldn't match type `Map.Map String' with `[]'
      Expected type: [[String]]
        Actual type: Map.Map String [String]
    * In a stmt of a 'do' block: Map.insert z [x, y] orbitingData
      In the expression:
        do Map.insert z [x, y] orbitingData
           ""
      In an equation for `threeWordFunction':
          threeWordFunction [x, y, z] orbitingData
            = do Map.insert z [x, ....] orbitingData
                 ""
   |
40 |      Map.insert z [x, y] orbitingData
   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I've tried a whole bunch of fixes but found nothing that could cause this error. The function is just trying to add the strings to the map, nothing fancy. Any help would be appreciated.

CodePudding user response:

The problem

As some contributors says, the do is the problem. However, let me try to decompose what you want (as I can see) and what you do.

threeWordFunction :: [String] -> Map.Map String [String] -> String

declares the type of your function. It is what you plan to do. With a list of String and a Map you want to provide a String. If I read this, in Haskell, I will deduce that you probably want to use the Map to extract something useful to build a String. Indeed, the only output of your function is a String. Since the Map is not an output any modification will be "lost".

When I read what you want to do, you claimed that you want to insert a value in the Map. So I suppose that you want to output your resulting Map. And here I can deduce your first error : you think with side effect. I suppose that you wanted to modify the Map inside your function, and you've expected that the Map will be modified outside the function. But it is not how Haskell works. This is how most languages with imperative features works.

So to do this you want to use a do word. The state of mind behind this is to provide a sequence of instruction like in a C-block with curly braces. And this is wrong. In Haskell, the do word is syntaxic sugar and points the fact that you are in a monad... So the result of your computation will probably be a monad. But this is not what the function type said. So the compiler produces an error.

What can you do to be consistent with your type

Considering the type, a solution will be the one provide by Chi.

threeWordFunction [x, y, z] orbitingData = let newMap = Map.insert z [x, y] orbitingData in ""

The compiler will complain no more. You build a new Map newMap that is modify the way you want. And you provide the String "" as an output. However, the newMap will never be reachable outside your function. So the insert is useless.

What can you do to achieve your need

Change the type... The type in Haskell provide a specification that clearly defines the expected valid solutions (more or less).

So you can want something that explicitly provide a Map.

threeWordFunction :: [String] -> Map.Map String [String] -> Map.Map String [String]

or a Map and a String

threeWordFunction :: [String] -> Map.Map String [String] -> (Map.Map String [String], String)

You can also use monad like State but it may be too much for your level.

Considering the new type, you can now return an entity that is what you need.

threeWordFunction :: [String] -> Map.Map String [String] -> (Map.Map String [String], String)
threeWordFunction [x, y, z] orbitingData = 
    let newMap = Map.insert z [x, y] orbitingData 
    in (newMap, "") 

NB if you always want to provide a "" as a String, this probably indicates that you don't need it.

Hope this will help you understand the problem.

  • Related