yes. There are also appendable and comparable and I think that’s all? All of these are special variable prefixes that also carry an explicit constrain. But by design are impossible to combine in elm. You can’t have appendable number. And all number are comparable otherwise you couldn’t use Set Int. Number is special because you can construct value that is number. You can’t construct value that is comparable. What you can construct is some concrete type of comparable but not a value of type comparable.
One thing to realize about these constrained type variables is that they don’t exist in type definition itself. Only in function implementation. This is legal:
type alias NoConstraintDict a b = Dict a b
but you won’t be able to put any a in dict because functions to insert is where constraint applies. In reality these works like:
type Dict a b
insert : Comparable a => a -> b -> Dict a b -> Dict a b
the constrain is property of the function insert, not the dict type.
So here is an interesting program for you. It has Model number and it even does increment the number without knowing what type it is. What it can’t do is to display number. Because to convert it to String there is no function number -> String only Float -> String and Int -> String.
module Main exposing (main)
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
import Dict exposing (Dict)
type alias NoConstraintDict a b = Dict a b
type alias Model a =
{ count : a }
initialModel : Model number
initialModel =
{ count = 0 }
type Msg
= Increment
| Decrement
update : Msg -> Model number -> Model number
update msg model =
case msg of
Increment ->
{ model | count = model.count + 1 }
Decrement ->
{ model | count = model.count - 1 }
view : Model number -> Html Msg
view model =
div []
[ button [ onClick Increment ] [ text "+1" ]
, button [ onClick Decrement ] [ text "-1" ]
]
main : Program () (Model number) Msg
main =
Browser.sandbox
{ init = initialModel
, view = view
, update = update
}
Hopefully that gives you full idea about why I and some others keep bringing this discussion back from type variables to these constrained type variables. Especially the number because that one has literal which is constrained type variable you can construct (like 0, 0 + 1).
EDIT: Just realized we can stay in development build and use Debug.toString. So here you have Counter example in elm which is actually working with number, incrementing it and decrementing it while not knowing if it’s Int or Float.
module Main exposing (main)
import Browser
import Dict exposing (Dict)
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
type alias Model a =
{ count : a }
initialModel : Model number
initialModel =
{ count = 0 }
type Msg
= Increment
| Decrement
update : Msg -> Model number -> Model number
update msg model =
case msg of
Increment ->
{ model | count = model.count + 1 }
Decrement ->
{ model | count = model.count - 1 }
view : Model number -> Html Msg
view model =
div []
[ button [ onClick Increment ] [ text "+1" ]
, div [] [ text <| Debug.toString model.count ]
, button [ onClick Decrement ] [ text "-1" ]
]
main : Program () (Model number) Msg
main =
Browser.sandbox
{ init = initialModel
, view = view
, update = update
}
I think this is closest we get to “weird untyped program”. ellie