What I am trying to achieve is type inheritance. What I mean by that is that I want to be able to have functions returning "sub-types" and then a function returning the "super-type". Let me give you an example
let's say I have a main View component which returns an Html Msg
view: Model -> Html Msg
view model =
div [class "goal-box"] [
function_a model,
function_b model
]
Now I would like function_a
and function_b
to each be able to return a subtype of Msg
function_a: Model -> Html AMsg
function_b: Model -> Html BMsg
The reason why I want this is because I want to make sure function_a is limited in what kind of Msg it can produce, and same goes for function_b, but eventually I need a unified view that uses both.
So what came natural was to define Msg as
type Msg
= AMsg
| BMsg
type AMsg
= AnotherMsg Int
| AgainMsg Int
type BMsg
= ThisMsg String
| ThatMsg Int
This does not seem to work, as the compiler tells me that it was expecting a return value of type Html Msg
and not Html AMsg
.
I hope this is clear. I feel like types are the concept I am struggling with the most coming from JS, but hopefully I am headed in the right direction.
DISCLAIMER
I have asked a similar question earlier in the day but I realized I made a mistake and then confused myself a couple of times as I was editing it. So I had to delete it. Apologies in advance to the people that took the time to read it and answer.
There are two main issues here.
Firstly AMsg
and BMsg
in your Msg
do not refer to those types, they are just constructors for your Msg
type.
You need to change this to:
type Msg
= AMsg AMsg
| BMsg BMsg
Here first AMsg
and BMsg
on each line are constructors for Msg
type, and second ones refer to your other types. After this you can create values like AMsg (AnotherMsg 34)
.
Secondly you need to use function Html.map
in your view
to change message types so that when e.g. function_a
sends message AnotherMsg 34
(of type AMsg
), that will be transformed into AMsg (AnotherMsg 34)
(of type Msg
) and so in your view
all messages are of same type.
Full example code below, with ellie example here: https://ellie-app.com/3TG62zDLvwFa1
module Main exposing (main)
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
type alias Model =
{}
init : Model
init =
{}
type Msg
= AMsg AMsg
| BMsg BMsg
type AMsg
= AnotherMsg Int
| AgainMsg Int
type BMsg
= ThisMsg String
| ThatMsg Int
view : Model -> Html Msg
view model =
div [] [
Html.map AMsg (function_a model),
Html.map BMsg (function_b model)
]
function_a : Model -> Html AMsg
function_a model =
div [] [ text "A" ]
function_b : Model -> Html BMsg
function_b model =
div [] [ text "B" ]
update : Msg -> Model -> Model
update msg model =
model
main : Program () Model Msg
main =
Browser.sandbox
{ init = init
, view = view
, update = update
}