1

I'm in the process of laying out all of the data types for an API wrapper I'm writing. I want to create each type separately, and then combine them under a group so that I can refer to them ambiguously.

E.g.

data Bookmark = Bookmark {
  url :: T.Text,
  title :: T.Text
} deriving (Show)

data Note = Note {
  author :: T.Text,
  text :: T.Text
} deriving (Show)

data APIObject = Bookmark | Note

This way, I could define something like the following:

retrieveFromAPI :: APIObject a => String -> a

Where String is a URL.

However, when I try to compile this, I get two errors:

[1 of 1] Compiling Main             ( API.hs, interpreted )

API.hs:28:16:
    Multiple declarations of `Bookmark'
    Declared at: API.hs:22:17
                 API.hs:28:16

API.hs:28:27:
    Multiple declarations of `Note'
    Declared at: API.hs:27:13
                 API.hs:28:27
Failed, modules loaded: none.

What should I be doing differently?

Thanks!

1 Answer 1

7

The problem is with APIObject, you have its constructors as Bookmark and Note, but those are also constructors for the Bookmark and Note data types! Instead you could do something like

data APIObject = BM Bookmark | NT Note

So now you have a constructor BM :: Bookmark -> APIObject and a constructor NT :: Note -> APIObject.


Alternatively, if you aren't planning on adding more types to APIObject, you could just do

type APIObject = Either Bookmark Note

Since Either is defined as

data Either a b = Left a | Right b

so it's isomorphic (the same as) what you are trying to do.

Sign up to request clarification or add additional context in comments.

4 Comments

Hmm, trying to work out the final part of step one. How might those constructors be put together? Would I need to wrap any Bookmark / Note instance with the appropriate BM / NT type in order to use it generically?
@Dan yes, you'd have to wrap them up in the additional constructors. Haskell doesn't let you make sum types for free.
Gotcha. Why can't I do something like this: data APIObject = Bookmark { url :: T.Text, title :: T.Text } | Note { author :: T.Text, text :: T.Text }?
@Dan You actually can, but it's recommended not to. I personally think it shouldn't be allowed at all unless each constructor has exactly the same fields. The reason is that you could have author (Bookmark "www.example.com" "Example") allowed by the type system, but it would cause a runtime error. Haskell is meant to make programming safer, not necessarily easier (although it often is), so you should choose a safer route over a "nicer" one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.