Language Idea: Limit number of paramters in custom types to three

I have a type like this in my application:

type Model =
    Model InnerModel (Widget Model Msg)

This is a recursive type definition, it won’t work with records:

type alias Model =
    { model : InnerModel
    , widget: Widget Model Msg
    }

yields an error message along the lines of This type alias is recursive, forming an infinite type! Blah Blah...

Now the interesting part is that the Widget state msg type only needs the state type parameter because it enforces some type constraints upon its configuration, no data is ever stored inside of the Widget state msg type that relates to the passed in Model type.

One could work around the recursive type issue by passing the InnerModel type instead of Model to Widget state msg meta (Widget InnerModel Msg) but this would force me to have a nested record type for the Model just in order to be able to do this and split up the model into two records arbitrarily, without actually needing the separation. This feels wrong and makes updates a lot harder because updating nested records is quite verbose.

I know, you want to limit type variables to three, so this seems like a non-issue for the proposal. But the point is that if for some reason I would need to introduce type variables for the Model type, I wouldn’t have an option on par with the current custom type in terms of ergonomics.

So if we would really remove the possibility to create custom types with more than three type parameters, I would argue that we would either need a better story of how to update nested records or a possibility to create recursive type aliases to retain language ergonomics.

1 Like

I’ve also used the "case " pattern to evaluate multiple values at once. With the removal of large tuples I figure you can just make a class like this:

module Toop
    exposing
        ( T1
        , T2
        , T3
        , T4
        )


type T1 a
    = T1 a


type T2 a b
    = T2 a b


type T3 a b c
    = T3 a b c


type T4 a b c d
    = T4 a b c d

But I guess this sort of thing is exactly what we’re trying to remove by limiting the number of parameters to custom types. The above is just a workaround to the tuple size limitation. It does seem silly to get rid of large tupes if you can just turn around and reimplement them so easily.

Beside the tuple/case thing, I have been using custom types with several parameters for what I think are good reasons - I have more than one opaque type in my Model, and so the Model needs to take each type as a parameter. To prevent this would be a real problem for abstraction, as I see it. Maybe you’d need to make a sort of chinese box of types with each layer containing one more new type? Or just give up on the abstraction thing and go with copy-paste.

Here’s an example, where I have item indexes and tag indexes that are opaque types. With this approach the specific index implementation is hidden, which allows it to be swapped out easily.

type alias Model itemid item idxs tidxs =
    { itemSearchPanelState : SearchPanel.Model
    , itemSearchState : SearchState itemid item idxs tidxs
    , selectedItemId : Maybe itemid
    , itemstuff : ItemStuff itemid item
    , itemindexer : ItemIndexer itemid item idxs
    , tagindexer : ItemIndexer TagId Tag tidxs
    }
1 Like

Would this not work?

type Model =
    Model
       { model : InnerModel
       , widget: Widget Model Msg 
       , meta: Meta
       }

Of course it would, but here we’re back to the nested record update hell. The whole point was to work around nested records.

I’m not arguing that you can’t find a solution with records, my only point is that custom types are nicer in terms of ergonomics specifically for the shown example.

I think there is some confusion here: the proposal is to limit the number of parameters to data constructors, not type constructors. In the example below, Thing is a type constructor whereas Foo and Bar are data constructors. Hence only Bar is violating the proposed rule (since it has four parameters).

type Thing a b c d e f
    = Foo a b
    | Bar c d e f
1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.