Is there a better way to structure my List types?

136 views
Skip to first unread message

Ryan W

unread,
Aug 1, 2016, 3:46:53 AM8/1/16
to Elm Discuss
I want to be able to store several different types of lists in a single data structure, and and each type of list should be treated differently by the view function.

What I've tried:
type ThingList
   
= FooList (List Foo)
   
| BarList (List Bar)
   
| BazList (List Baz)


This works okay, but if I want to do something like get the length of a ThingList without caring about whether it is a FooList, BarList or BazList it seems I have to have to write something like this:

length : ThingList
length thingList
=
   
case thingList of
       
FooList list ->
           
List.length list

       
BarList list ->
           
List.length list

       
BazList list ->
           
List.length list

Since in my actual program I plan to have several more sub-types in the actual counterpart to ThingList this becomes somewhat cumbersome and it definitely isn't DRY. I guess this is a form of the expression problem? I tried to generalize it a bit so I wouldn't have to update as many of these functions when I add a new sub-type like this:
reduce : (List a -> b) -> EntityList -> b
reduce f entityList
=
   
case entityList of
       
FooList list ->
            f list

       
BarList list ->
            f list

       
BazList list ->
            f list

but that gave me an error like this:

Function `f` is expecting the argument to be: List Foo But it is: List Bar

In my particular case, at least for now, I only wanted the length of a ThingList literal so I can work around it, but I'm curious if there is way to structure the types that avoids this problem.

Wouter In t Velt

unread,
Aug 1, 2016, 4:25:18 AM8/1/16
to Elm Discuss
How about this?

type Thing =
 
Foo
 
| Bar
 
| Baz


type
ThingList = List Thing



That way, you can simply do:

List.length ThingList

ryan...@gmail.com

unread,
Aug 1, 2016, 5:50:04 AM8/1/16
to elm-d...@googlegroups.com
The problem with that, for my use case at least, is that I want to be able to know that a list contains only Foos or only Bars because there are functions that only make sense for Bars and not Foos. And it would be kind of strange to have to put in a case for the sub-type I never expect to call that function with. And if I ever do then I've lost the type safety so the compiler won't catch my mistake. 
--
You received this message because you are subscribed to a topic in the Google Groups "Elm Discuss" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elm-discuss/8FNvfSguIq8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elm-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

debois

unread,
Aug 1, 2016, 6:08:20 AM8/1/16
to Elm Discuss
What actually goes in the lists? (As opposed to Foo, Bar etc.)

ryan...@gmail.com

unread,
Aug 1, 2016, 7:20:53 AM8/1/16
to elm-d...@googlegroups.com
Records, where each type has a different set of fields.

> On Aug 1, 2016, at 4:08 AM, debois <soren....@gmail.com> wrote:
>
> What actually goes in the lists? (As opposed to Foo, Bar etc.)
>

Søren Debois

unread,
Aug 1, 2016, 7:27:27 AM8/1/16
to elm-d...@googlegroups.com
I meant, what are you trying to model? What's the /actual/ application you're doing?

-- 
Søren Debois
Sent with Airmail

Ryan W

unread,
Aug 1, 2016, 7:49:34 AM8/1/16
to elm-d...@googlegroups.com
I'm using this package to display several different types of tables, with different sets of columns. The actual number of tables and the data within them is dynamic, but the types of tables are all definable ahead of time. Only one type of table needs to be visible at one time, but users should be able to switch back and forth between different tables. So, in my model I have a ThingList called currentThingList which is rendered in the view, and a Dict that holds all the ThingLists so the currentThingList can be set according to  the users' wishes.

I don't see how the semantics of the data is relevant.

Nick H

unread,
Aug 1, 2016, 9:51:20 AM8/1/16
to elm-d...@googlegroups.com
The semantics are relevant because we want to avoid an XY scenario, which tend to come up a lot on this mailing list. Rest assured, Soren is only asking because he wants you to get the best help possible.  :-)

It sounds to me like the approach you describe in the OP is the best way to model the data.

Sure, your "reduce" function doesn't compile. But I don't think it would be that useful anyway. How many 1-arity functions are there that can operate on a list of unspecified type and return a value of a specified type? The only one I can think of is List.length.

You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.

Nick H

unread,
Aug 1, 2016, 9:59:51 AM8/1/16
to elm-d...@googlegroups.com
I don't mean to gloss over the fact that the length function you had to write is a little verbose. But I don't think there are a lot of other operations that would be applicable to every type of list in ThingList. For instance, folding and mapping wouldn't work, because the fold/map function would only work for one of the component types.

Ryan W

unread,
Aug 1, 2016, 10:25:05 AM8/1/16
to elm-d...@googlegroups.com
I was thinking I might want List.take and List.drop for pagination purposes but in seems like the easiest way in this particular case is to do all that generic list manipulation ahead of time when I have the plain lists, before I add the types. But if dynamic pagination was a hard requirement then it seems like I'd be stuck filling out functions for every type.

OvermindDL1

unread,
Aug 1, 2016, 10:29:12 AM8/1/16
to Elm Discuss
As another way to think about this, and I am unsure how Elm'ish this is (but it is very functional), you could invert your lists into being functions, then pass in commands to operate on the internal lists.  This is how some functional list implementations are developed especially when missing HKT's like Elm is (when is Elm getting HKT's anyway?).

Nick H

unread,
Aug 1, 2016, 11:28:28 AM8/1/16
to elm-d...@googlegroups.com
Every data type is going to need its own view function, right? (I am assuming your thingList corresponds to the "List data" argument to Table.view.) It seems like that would be the logical place to put the pagination logic.

Your pagination could be encapsulated by a function like "paginate : Config -> List a -> List a" that is perfectly generic and calls List.take or whatever. Each of your view functions could then call this function.

Yes, the call to paginate would be duplicated in each of your view functions, but it is a better sort of duplication than, say, defining take/drop on your over-arching ThingList type and having tons of case statements. It will be easier to maintain, and easier on the eyes!

In my (recent, painful) experience, Elm works much better when you focus on "vertical encapsulation" -- your main union type has a case statement in your outer "update" function, your outer "view" function, and nowhere else. What you are trying to do with your list operations, I would describe as "horizontal encapsulation" -- trying to define as many operations as possible on your main union type.

If you focus on vertical encapsulation, Elm will make your life easy. If you focus on horizontal encapsulation, Elm will be your enemy every step of the way.

Ryan W

unread,
Aug 1, 2016, 11:49:36 AM8/1/16
to elm-d...@googlegroups.com
Doing pagination in the view after the case statement like that does sound a lot better than what I was thinking of.

Your advice about vertical encapsulation is interesting. I'll keep it in mind.

--
You received this message because you are subscribed to a topic in the Google Groups "Elm Discuss" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elm-discuss/8FNvfSguIq8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elm-discuss...@googlegroups.com.

Nick H

unread,
Aug 1, 2016, 11:59:40 AM8/1/16
to elm-d...@googlegroups.com
I may have oversold it there at the end :-P, but I have been doing some major refactoring lately and am very pleased with the results so far. Please keep sharing your experiences!
Reply all
Reply to author
Forward
0 new messages