Skip to content

Instantly share code, notes, and snippets.

@dennisreimann
Last active April 2, 2024 20:17
Show Gist options
  • Save dennisreimann/fe2cc20ea7cb6ea60483c1d758b2d891 to your computer and use it in GitHub Desktop.
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 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
}
@emaniacs
Copy link

Line 94 rangeId is string, so toString dont need anymore,
i got inconsistency timeRange because of that.

@dennisreimann
Copy link
Author

@emaniacs Thanks for pointing this out, I corrected it :)

@baritonehands
Copy link

I did something similar: https://gist.github.com/baritonehands/d749f2374f90298c106fda8686204a1a

Main differences: I used a Dict to lookup the values rather than filter a list. Also, I convert back to the Union type (or any type other than String).

@arvindershinh
Copy link

arvindershinh commented Feb 6, 2018

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment