pete-murphy/elm-loadableA data type for tracking loadable (and reloadable) data, like data fetched from a backend server.
elm install pete-murphy/elm-loadableThis package is centered around the Loadable type, which is a minimal extension of RemoteData from krisajenkins/remotedata that adds a "reloading" state to the error and success cases. See this discussion on alternative solutions to this problem for background.
Differences from RemoteData
RemoteData e a is a sum type defined as
type RemoteData e a
= NotAsked
| Loading
| Failure e
| Success aAs the "sum type" name implies, we can think of this type as having $$2 + e + a$$ inhabitants (see The algebra (and calculus!) of algebraic data types). Under this lens, Loadable e a is a product of sums represented internally as
type Value e a
= Empty
| Failure e
| Success awith a loading : Boolean added to all cases. Expressed algebraically, Value is $$1 + e + a$$ (very similar to RemoteData) and Loadable is the product of that sum and Bool's 2 inhabitants: $$2 \times (1 + e + a)$$ or $$2 + 2e + 2a$$ (compare to RemoteData's $$2 + e + a$$).
You can losslessly convert from RemoteData to Loadable
remoteDataToLoadable : RemoteData e a -> Loadable e a
remoteDataToLoadable remoteData =
case remoteData of
RemoteData.NotAsked ->
Loadable.notAsked
RemoteData.Loading ->
Loadable.loading
RemoteData.Success a ->
Loadable.succeed a
RemoteData.Failure e ->
Loadable.fail eGoing the other direction just means discarding the loading state in the success & error cases.
loadableToRemoteData : Loadable e a -> RemoteData e a
loadableToRemoteData loadable =
case ( Loadable.value loadable, Loadable.isLoading loadable ) of
( Loadable.Empty, False ) ->
RemoteData.NotAsked
( Loadable.Empty, True ) ->
RemoteData.Loading
( Loadable.Success a, _ ) ->
RemoteData.Success a
( Loadable.Failure e, _ ) ->
RemoteData.Failure etype alias Model =
{ todos : Loadable Http.Error (List Todo) }
init : ( Model, Cmd Msg )
init =
({ todos = Loadable.notAsked }, fetchTodos )
type Msg
= UserClickedFetchTodos
| BackendRespondedWithTodos (Result Http.Error (List Todo))
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
UserClickedFetchTodos ->
( { model | todos = Loadable.toLoading model.todos }, fetchTodos )
BackendRespondedWithTodos result ->
( { model | todos = Loadable.fromResult result }, Cmd.none )
view : Model -> Html Msg
view model =
Html.div []
[ Html.h1 [] [ Html.text "Todos" ]
, Html.button [ Events.onClick UserClickedFetchTodos ] [ Html.text "Fetch Todos" ]
, Html.div [ Attributes.classList [ ( "loading", Loadable.isLoading model.todos ) ] ]
[ case Loadable.value model.todos of
Loadable.Empty ->
Html.text "Loading..."
Loadable.Success todos ->
Html.ul [] (List.map viewTodo todos)
Loadable.Failure _ ->
Html.text "Something went wrong"
]
]