Last active
April 2, 2024 20:17
-
-
Save dennisreimann/fe2cc20ea7cb6ea60483c1d758b2d891 to your computer and use it in GitHub Desktop.
A reaction to the article "A small dive into, and rejection of, Elm"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{- | |
This file is a reaction to the article "A small dive into, and rejection of, Elm": | |
https://hackernoon.com/a-small-dive-into-and-rejection-of-elm-8217fd5da235#.9w3ml4r6b | |
I think constructive criticism is very welcome in the Elm community. Pointing out | |
possibilities for documentation improvements, mentioning missing features or ways | |
in which the language could evolve etc. are imho a vital part of a good community. | |
However, the article above is neither well informed nor constructive. IMHO ranting | |
about something one has not quite understood yet is helping no one, neither the author | |
nor us as a community. The only and worst thing that might happen is that people | |
interested in the language might get turned off, because someone else didn't get it | |
at first glance and therefore rejected it. That's why I took the time to show a better | |
and easier solution to the described problem. | |
Again: I am not saying this is perfect and I welcome everyone to reply to this and | |
give constructive feedback. I am new to Elm myself and coming up with this was a | |
nice way to dig a little deeper :) | |
-} | |
module Main exposing (..) | |
import Html exposing (Html, div, input, form, option, select, text) | |
import Html.Events exposing (onInput) | |
import Html.Attributes exposing (class, id, selected, value) | |
import Html.App | |
type alias Model = | |
{ timeRange : TimeRange } | |
type Msg | |
= ChangeTimeRange String | |
type TimeRange | |
= AllTime | |
| OneWeek | |
| OneDay | |
{- | |
One of the main concerns in the article is that union types cannot be iterated. | |
In a simple case like this where the union type consists of simple values this | |
might seem like the language is lacking a nice feature. However, a union type | |
can be an amalgamation of different types, which is oftentimes the reason for | |
using union types. If you want to learn more about this, see my detailed article: | |
https://dennisreimann.de/articles/elm-data-structures-union-type.html | |
I'm far to new too the language and algebraic data types to know whether or not | |
iterating a complex union type would be possible or a good idea, but in this | |
case we can solve this much simpler: By wrapping the time ranges in a list with | |
tuples containing the range and its title, we can get rid of the otherwise mentioned | |
function timeRangeDisplayName. Of course this is kind of redundant, but you would | |
have this redundancy either way if you want to map the ranges to a display name. | |
-} | |
type alias TimeRangeOption = | |
( TimeRange, String ) | |
timeRangeDefaultOption : TimeRangeOption | |
timeRangeDefaultOption = | |
( AllTime, "All time" ) | |
timeRangeOptions : List TimeRangeOption | |
timeRangeOptions = | |
[ timeRangeDefaultOption | |
, ( OneWeek, "One week" ) | |
, ( OneDay, "24h" ) | |
] | |
-- Helper functions for mapping the union type value to a string and back. | |
-- This isn't the prettiest part indeed. | |
timeRangeToId : TimeRange -> String | |
timeRangeToId timeRange = | |
(toString timeRange) | |
timeRangeForId : String -> TimeRange | |
timeRangeForId rangeId = | |
List.filter (\n -> (toString (fst n)) == rangeId) timeRangeOptions | |
|> List.head | |
|> Maybe.withDefault timeRangeDefaultOption | |
|> fst | |
-- MODEL | |
initModel : Model | |
initModel = | |
{ timeRange = OneWeek | |
} | |
-- UPDATE | |
update : Msg -> Model -> Model | |
update msg model = | |
case msg of | |
ChangeTimeRange rangeId -> | |
let | |
updatedModel = | |
{ model | timeRange = (timeRangeForId rangeId) } | |
in | |
updatedModel | |
-- VIEW | |
-- separate the rendering of a single option to a function so we can | |
-- map our timeRangeOptions and make this a little cleaner | |
timeRangeOption : TimeRange -> ( TimeRange, String ) -> Html.Html msg | |
timeRangeOption selectedRange timeRange = | |
let | |
optionValue = | |
(fst timeRange |> timeRangeToId) | |
optionTitle = | |
(snd timeRange) | |
isSelected = | |
selectedRange == (fst timeRange) | |
in | |
option | |
[ value optionValue, selected isSelected ] | |
[ text optionTitle ] | |
-- use onInput to handle changes of the select field. It's fine and we do not need | |
-- to fiddle with a custom onSelect | |
view : Model -> Html Msg | |
view model = | |
let | |
options = | |
(List.map (timeRangeOption model.timeRange) timeRangeOptions) | |
in | |
div [ class "wrapper" ] | |
[ select [ id "time_range", onInput ChangeTimeRange ] options | |
, text ("Selected time range id: " ++ (toString model.timeRange)) | |
] | |
-- MAIN | |
main : Program Never | |
main = | |
Html.App.beginnerProgram | |
{ model = initModel | |
, view = view | |
, update = update | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@dennisreimann, I went through the article in "hackernoon.com" and found it quite irrelevant and your above example is really great way to prove its irrelevance.
I was thinking of another solution where we can exclude type TimeRange (and with that, we can also exclude functions such as timeRangeToId and timeRangeForId ) and directly deal with Option Msg rangeId in update function . I have created a working code for the same https://gist.github.com/arvindershinh/9a7596466b8891e6d7650c7c434e0eca .
I will highly appreciate if you and others can provide your feedback on this approach.
Specially i would like to know if it is a good design or not.