Good morning. I'm having a ton of fun with Elm, but I'm back to ask another newbie question, and I'm probably mostly just being confused by my own design mistakes. But here goes.
I have an overly simplistic Model type, just to get started:
type alias Model =
{ errorMessage: Maybe String -- This is what Rails would call a "flash": we just show it.
, video: Maybe Video.Model -- Information about a video and subtitles.
, player: Maybe VideoPlayer.Model -- Player state: URL, playing/paused, time.
}
The idea is that the errorMessage represents an application wide error, displayed at the top of the screen if it's present. The Video.Model represents data from my web server: The video's URL, the associated subtitles, and other persistent state. The VideoPlayer.Model represents a generic HTML 5 video player: It has state like { url = "...", playing = true, currentTime = 10.54 }, but it doesn't know anything about my application-specific data.
As a quick-and-dirty first draft, I wanted to write a throwaway view function which worked like this:
view address model =
case model of
-- If we have an error, show that.
{ errorMessage = Just err } ->
text err
-- If we have a player, show that.
{ videoPlayer = Just player } ->
VideoPlayer.view (Signal.forwardTo address VideoPlayerAction) player
-- Otherwise, just show an error.
_ ->
text "Loading..."
Here, the intent is to say, "If we have an error, just show it. If not, see if we have a player, and show that. Otherwise, show the loading message." However, this is where I get stuck: Elm's record patterns only allow me to match entire fields, and they don't allow me to destructure those fields.
Other functional languages often provide destructuring matches on records. For example, in Rust, I might write:
// A weird struct provided to me by an argument parsing
// library based on 'docopt'.
struct Args {
cmd_export: bool,
cmd_csv: bool,
cmd_review: bool,
cmd_tracks: bool,
arg_subs: String,
// ...
}
// We know that, logically, one of cmd_csv, cmd_review or
// cmd_tracks must be true, but we need to find out which.
fn export_type(args: &Args) -> &str {
match *args {
Args{cmd_csv: true, ..} => "csv",
Args{cmd_review: true, ..} => "review",
Args{cmd_tracks: true, ..} => "tracks",
// This is an assertion failure which crashes the application:
_ => panic!("Cannot determine export type: {:?}", args),
}
}
Above, I can match on nested record patterns: I can say, "If the record contains the field cmd_csv with a the value true, then do such-and-such."
What's the best way to match nested structure in a record in Elm? I know that with an ADT, I could write "Loaded (Just x) -> ..." and "Loaded Nothing -> ..." in different branches of the same case. But there doesn't seem to be any sort of similar flexibility for records. It's not the sort of thing I'd actually use very often, but on the rare occasions where I do want it, it's really useful.
Now, I can obviously restructure my application to work around this (and I was planning to do anyway, by adding a Flash.Model, Flash.view, etc, as soon as my prototype was working). And of course, I'm prepared to believe that there might be really principled reasons for Elm's design here.
Anyway, I have no idea whether my silly questions are useful or constructive. :-) I think Elm is a really cool language, and I'd love to figure out how to write a well-structured Elm application. Does it help anybody for me to post my confusions and stumbling blocks like this? If all I'm doing is adding noise, I'm happy to bite my tongue. And a big thank you to everybody who's helping create such a cool project.