How to make a view with a list of components

102 views
Skip to first unread message

Antoine Snyers

unread,
Sep 24, 2016, 12:46:45 PM9/24/16
to Elm Discuss
Hello Elm Discuss. 
I'm a java/javascript developer. I'm excited about learning elm. I finally switched from reading and watching to writing something myself and now I am looking for feedback and guidance on what I've done here below:

My types in Log.elm (I don't think the definitions matter for my problem, tell me if you need them too)
type alias Log =
   
{ author : String
   
, content : String
   
}
   
type
LogMsg = ...


viewLog
: Log -> Html LogMsg


My model in Task.elm
type alias Task =
   
{ title : String
   
, description : String
   
, logs : List Log
   
}


type
TaskMsg
   
= Change String


Now I thought I was going to be able to write my view like this
viewTask task =
        div
[]
       
[ h1 [] [ text task.title ]
       
, Markdown.toHtml [] task.description
       
, List.map viewLog task.logs
       
]


However this has 2 problems:
- the third element in the second parameter of div is a list, so I have a List inside a List
- The inner list is not a List Html TaskMsg but a List Html LogMsg (which was extra confusing when they where both simply called Msg at first)

After some searching and puzzling I was finally able to get the result I wanted with this code :
type TaskMsg
   
= Change String
   
| TaskLog LogMsg


viewTask
: Task -> Html TaskMsg
viewTask task
=
    div
[]
       
(List.concat
           
[ [ h1 [] [ text task.title ]
             
, Markdown.toHtml [] task.description
             
]
           
, (List.map transformLogList
               
(List.map viewLog task.logs)
             
)
           
]
       
)




transformLogList
: Html LogMsg -> Html TaskMsg
transformLogList log
=
   
App.map transformLogMsg log




transformLogMsg
: LogMsg -> TaskMsg
transformLogMsg logMsg
=
   
TaskLog logMsg


Even though I'm a total beginner to elm, this seems more complicated than it should be.
The problems that I'm thinking of:
- Now I need to write an update method in Task.elm that deals with LogMsg. But I wanted to contain everything that touches LogMsg in Log.elm
- Especially the double usage of List.map combined with App.map will be something I'll have trouble reading again when I come back to it in the future

So what would I need to restructure or rewrite to be able to share this with a fellow elm first-timer.

I have searched the guide, the docs, a number of blog posts and other elm repositories on github and somehow I still miss the answer...
But I don't think I understand yet what it would mean to apply that suggestion to my code.
(Also I'm not familiar with the <| operator yet)

Pierre Beitz

unread,
Sep 24, 2016, 1:25:07 PM9/24/16
to Elm Discuss
first some refactoring:
 
transformLogMsg : LogMsg -> TaskMsg
transformLogMsg logMsg
=
   
TaskLog logMsg

transformLogList
: Html LogMsg -> Html TaskMsg
transformLogList log
=
   
App.map transformLogMsg log

can due to currying also be written as 

transformLogMsg : LogMsg -> TaskMsg
transformLogMsg
=
   
TaskLog


transformLogList
: Html LogMsg -> Html TaskMsg

transformLogList
=
   
App.map transformLogMsg

when you apply this rule once again and inline stuff you may end up with 

viewTask task =
    div
[]
       
[ h1 [] [ text task.title ]
       
, Markdown.toHtml [] task.
description
       
, App.map TaskLog (div [] (List.map viewLog task.logs))
       
]

which does not read all that bad. with the cons-operator (::) or List.concat you might again get rid of the extra div. 

the <| operator is just the inverse of |>. the following expressions are identical:

piping (some "stuff")

piping
<| some "stuff"

some
"stuff" |> piping

For the confusion regarding the update-part i'd recommend reading https://www.elm-tutorial.org/en/02-elm-arch/07-composing-2.html, which should be clarifying about how to isolate child-modules. Properly integrated your Task-Module should not need to know about LogMsg.

Eric G

unread,
Sep 24, 2016, 1:26:05 PM9/24/16
to Elm Discuss
Hi Antoine,

I think you can use function composition like this to make it easier:

List.map (viewLog >> App.map TaskLog) task.logs

instead of the double List.map with helper function. This maps each `viewLog log` to the TaskLog-wrapped msg type, in one step.

Eric

Antoine Snyers

unread,
Sep 24, 2016, 5:09:29 PM9/24/16
to Elm Discuss
Wow, what a difference! Thanks a lot, Pierre and Eric, this has been an eye opener for me.

Arthur Welf

unread,
Sep 24, 2016, 10:40:59 PM9/24/16
to elm-d...@googlegroups.com
There is a law for Functors ("mappable" structures): 

List.map (f) >> List.map (g) == List.map (f >> g)

So, you can compose functions you map over data in list instead of composing several maps on list. 

24 сент. 2016 г., в 23:09, Antoine Snyers <antoine...@gmail.com> написал(а):

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages