Hi,
I try to implement some lazy loading in my application and came up with the following (simplified) data structure:
type Item
= LazyItem
{ id: Int
, title: String
}
| LoadedItem
{ id: Int
, title: String
, detailData: String -- expensive to load
}
Now of cause there are some functions that’ll can only work on a LoadedItem (as they need the detailData
, but there are a lot of functions that could work on either variant. My listViewItem function for example could look something like this:
viewListItem: Item -> Html msg
viewListItem x =
case x of
LazyItem item ->
tr [] [
td [] [text item.id],
td [] [text item.title]
]
LoadedItem item ->
tr [] [
td [] [text item.id],
td [] [text item.title]
]
As you can see, the case for either variant is exactly the same. How would I rewrite this function so it can handle both cases without repeating myself? I already came up with
viewListItem: Item -> Html msg
viewListItem x =
let
row : { a | id : Int, title : String } -> Html msg
row item = tr [] [
td [] [text <| String.fromInt item.id],
td [] [text item.title]
]
in
case x of
LazyItem item ->
row item
LoadedItem item ->
row item
which is a little bit better, but still a lot of glue code to write. And if I ever want to add a third variant to the Item
-type (which also has an id
and title
), I’d have to edit all functions that work with that type and add another case.
Is there some way to get rid of the case x of
and get a function that can work on all Items that have the required fields?
I’d expect something like this to work, but I couldn’t figure out the syntax:
viewListItem: Item { a | id : Int, title : String } -> Html msg
viewListItem Item item = tr [] [
td [] [text <| String.fromInt item.id],
td [] [text item.title]
]