ivanibash
ivanibash

Reputation: 1491

another haskell IO issue

Ok, so let's say I have a function which takes a String and returns a certain value of that string specified by a text file. A text file will look like this.

hello - 2
bye - 3

so the function would return 2 given "hello" and 3 given "bye". Now, the function is of type Weight (I can't change it as this is the part of the framework):

type Weight = String -> Int

How do I implement such function given that it HAS TO be of the type Weight. The problem is that I don't know how to make weight aware of what value to return given a certain string. I can't hardcode the values because they will be different in different textfiles. And I can't have readFile inside the function or anything like that, right? Are there any other options?

Upvotes: 0

Views: 116

Answers (2)

nponeccop
nponeccop

Reputation: 13677

You cannot have assignWeight globally, but you can have it locally with the genuine non-IO type you wanted. I think the approach below is a common pattern used to separate monadic code from non-monadic.

import Data.Maybe
import Control.Applicative

parseFile :: IO [(String, Int)]
parseFile = read <$> readFile "Parse.txt"

main = do
        content <- parseFile
        let assignWeight x = fromJust $ lookup x content
        print $ process assignWeight

type Weight = String -> Int

process :: Weight -> Int
process x = 0

Here assignWeight is of correct type. You can pass it around: look how I passed it to non-monadic process function. You cannot have assignWeight defined at top level without violating purity, as other commenters pointed out, but to have it locally and pass around is a commonly used approach.

Here is a more modular approach:

getAssignWeight :: IO Weight
getAssignWeight = do
        content <- parseFile
        let assignWeight x = fromJust $ lookup x content
        return assignWeight

main = do
        assignWeight <- getAssignWeight
        print $ process assignWeight

Upvotes: 0

daniel gratzer
daniel gratzer

Reputation: 53901

You have two choices,

assignWeight :: String -> IO Int
assignWeight = do
  file <- readFile whatever

  -- parse file and extract a function assigning strings to their weights
  return $ parseFile file

Or,

assignWeight :: String -> Weight
assignWeight file = parseFileAndExtractWeights file

and then read the file in main and use currying. So there's not toplevel function, but we still get our function later by partially applying assignWeight to the contents of the file

main = do
  weights <- assignWeight `fmap` readFile whatever
  -- use weights

But you can't perform IO in a computation of type Weight, so you'll either have to perform it elsewhere and pass the type to it or just change the type. No other way about it.

Upvotes: 3

Related Questions