I'm fairly new to Haskell, and to get better I'm trying to make a simple web server. I wanted to make how I represent pages extendable, so my idea was to make webpages be a list of Renderable data (like how you can make a list of objects that implement a certain interface in Java) where Renderable is
class Renderable a where
render :: a -> IO String
Unfortunately I learned that lists MUST be a concrete type, so I can only make a list of one type of Renderable data. Also it seems impossible to create data that is constrained by a typeclass, so I can't make something like RenderList data. My temporary solution has been like this:
myPage =
[render $ someData
,render $ someMoreData
,render $ someOtherData
...
]
but this feels awkward, makes the use of a typeclass have no benefit, and feels like there should be a better way. So I'm wondering what ways could I restructure what I have to be cleaner, more in line with standard Haskell practices, and still be easily extendable?
Thanks.
Lists in Haskell must be type-homogeneous.
In Haskell, lists are a homogenous data structure. It stores several elements of the same type. That means that we can have a list of integers or a list of characters but we can't have a list that has a few integers and then a few characters. And now, a list!
What's a typeclass in Haskell? A typeclass defines a set of methods that is shared across multiple types. For a type to belong to a typeclass, it needs to implement the methods of that typeclass. These implementations are ad-hoc: methods can have different implementations for different types.
You're trying to implement an object-oriented style of design. In Java, for example, you'd have a List<Renderable>
and you'd be all set. This design style is a little bit less natural in Haskell; you need to create a wrapper type for a bounded existential as demonstrated on the Haskell wiki page for existential types. For example:
class Renderable_ a where
render :: a -> IO String
data Renderable = forall a. Renderable_ a => Renderable a
instance Renderable_ Renderable where
render (Renderable a) = render a
You can then have a list of Renderable
, which you can render however you like. Like I said, though, that is sort of an OO style which is less natural in Haskell. You can probably avoid this by rethinking your data structures. You say that you "wanted to make how you represent pages extendable"; consider other ways of doing that instead.
Unrelated: I'm guessing render
doesn't need to produce an IO String
action. Try to keep IO
out of the core of your design, if you can.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With