-
-
Save dennisreimann/fe2cc20ea7cb6ea60483c1d758b2d891 to your computer and use it in GitHub Desktop.
{- | |
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 Thanks for pointing this out, I corrected it :)
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).
@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.
Line 94
rangeId
is string, sotoString
dont need anymore,i got inconsistency
timeRange
because of that.