1

I have a Haskell file called Types.hs that contains this definition, among others:

module Types
( KnapsackItem
) where

data KnapsackItem = KnapsackItem {
weight :: Int,
cost :: Int
} deriving (Show, Eq)

Then there's my Main.hs (also shortened here for simplicity):

import Types

evalTotal :: [KnapsackItem] -> [KnapsackItem] -> (Int, Int, String)
evalTotal items sub = (sum $ map weight sub, sum $ map cost sub, outputString)
    where outputString = "[" ++ unwords [if item `elem` sub then "1" else "0" | item 
    <- items] ++ "]"

main :: IO ()
main = do
    let items = [KnapsackItem {...}, KnapsackItem{...}, ...] -- details omitted in this example
    print $ map (evalTotal items) (subsequences items)

I get the following errors:

src/Main.hs:67:34: Not in scope: `weight'
src/Main.hs:67:56:
    Not in scope: `cost'
    Perhaps you meant one of these:
      `const' (imported from Prelude), `cos' (imported from Prelude),
      `cosh' (imported from Prelude)

As you can see, it won't let me access the attributes of each KnapsackItem. However, it works if I move the data definition into Main.hs and avoid the import. What am I doing wrong, please?

duplode
  • 33,731
  • 7
  • 79
  • 150
stitch123
  • 197
  • 9

2 Answers2

5

Change the export list at the beginning of Types.hs to:

module Types
    ( KnapsackItem (..)
    ) where

That will make the module export all constructors and fields of KnapsackItem. Without the (..), only the name of the type is exported.

For some extra commentary on (..) and related matters, see also What do bracketed double dots mean in Haskell?

duplode
  • 33,731
  • 7
  • 79
  • 150
2

Simply naming a type in an export list only exports the type. This is what you need if you intend the type to be abstract to downstream modules, keeping the details hidden and inaccessible (generally only if you also export other functions for using the type).

If you want to also export data constructors and/or fields, you need to explicitly say so. In your case, that would look like this:

module Types
( KnapsackItem ( KnapsackItem, weight, cost )
) where

Note carefully that KnapsackItem at the type level and KnapsackItem at the value level are two different items (even though it's very conventional to give them the same name for types with a single data constructor), so whether each one is exported is controlled separately.

Because it's very common to export either all of a type's constructors and fields or none of them, there is a shortcut syntax available to export everything. Instead of listing each constructor and field separately after the type name, you can just use (..) to export them all:

module Types
( KnapsackItem (..)
) where

The same syntax also applies in import lists; import Types ( KnapsackItem ) only imports the type, requiring a sub-list (which can be (..)) to import fields and data constructors. But you have used the form where you import everything from the module, so that wasn't an issue here (but might be if you're using explicit import lists in your real code).

Ben
  • 68,572
  • 20
  • 126
  • 174