What does "! []" Elm code syntax in Todomvc mean - elm

Coming from react, I am learning to understand Elm.
In the Todomvc example code, there is the following code snippet:
-- How we update our Model on a given Msg?
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NoOp ->
model ! [] <-- What is this?
What I (think I) understand, is that the update function takes in a msg of type Msg and a model of type Model, and returns a tuple of containing a Model and a Cmd Msg.
But how should I read the return statement?
model ! []
What does this statement mean? return a "model [something] empty list"?
Did I miss something in the docs where this is explained? (Googling "elm !" did not get me far :)

Update for Elm 0.19
Elm 0.19 has removed the exclamation point operator. You must now construct the tuple manually, as in (model, Cmd.none).
Original Answer for Elm 0.18
The exclamation point in model ! [] is just a short-hand function for (model, Cmd.batch []), which is the type returned from typical update statements. It is defined here

Note that this syntax is going away in the next version of Elm (0.19) so don't get into the habit of using it ;-)
You can use today, and with 0.19:
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NoOp ->
(model, Cmd.none)

Related

How can I use Cmd.map within a update-function using multiple arguments?

In Elm 0.19.1, I have the following Msg (among others):
type Msg
= Yay Multiselect.Msg
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Yay sub ->
let
( subModel, subCmd, _ ) =
Multiselect.update sub model
in
( { model | multiselectC = subModel }, Cmd.map Yay subCmd )
I'm using an external library here (Multiselect: https://github.com/inkuzmin/elm-multiselect). For this Msg, I'm correctly following the examples of the library. Goal here is handling events from this library to update the Model in this module. This correctly works.
Here comes the problem. I want to add multiple arguments to this Msg. For example, let's add the Multiselect.Model to it, creating the following type:
type Msg
= Yay Multiselect.Msg Multiselect.Model
This gives an error in the Cmd.map (makes sense since it is missing de Model). I've tried updating the Cmd.map-function in the following function:
Yay sub msModel ->
let
( subModel, subCmd, _ ) =
Multiselect.update sub msModel
in
( { model | multiselectC = subModel }, Cmd.map (\cmd -> Yay cmd subModel) subCmd )
This will remove the compile errors and will work for most of the events. However, some some events (for example, removing items), the library will behave unexpectedly and different from the code before.
I've also added some logging, creating the following command line:
It looks like it is correctly updating the Model when removing an selected item. However, with the next triggered event, it looks like the function is using the old model, removing the update. This basically means that nothing happens.
How can I correctly write the Cmd.map with multiple arguments which will behave the same as the first update-function with only 1 argument?
The way you pass multiple arguments to your Msgs is correct.
But you ran into a special case, because you passed state from the view.
Most times, it is better to only pass information what is supposed to change and not both the supposed change and an old state as part of your message.
My take at an explanation:
This behavior is due to the Elm runtime and optimizations for rendering:
The DOM is only rendered between 30 and 60 times a second, and the event that is fired from the view is using an old state.
So the state of your model was updated (because it is executed as fast as possible), but then later overwritten with the old state that is still being displayed by the browser.
When looking at this image from the official documentation, you can see that not every update triggers an immediate re-rendering of the DOM, but the work is done out-of-sync:
I actually fixed this issue in my case. It turns out that this method of mapping the Cmd is correct and works. However, in my case using the external library, it turns out that my view will fire multiple messages to my update-function using the same Multiselect.Model. My update-function updates this model, but this updated model will not be used for the messages fired from the view.
What I've done to fix this is removing the Multiselect.Model argument from the Msg. I will now use other arguments (that will not be updated in between the different messages) to search for the Multiselect.Model within the model of my module. This will take much code, but it actually works.

Assign a value in elm

This is a really noob question, i'm sorry but my search on the internet can't find the answer. I have the following code:
-- MODEL
type alias Model = Int
model : Model
model =
0
-- UPDATE
type Msg = Increment | Decrement | Reset
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
Reset ->
model = 0
I am trying to implement the reset that sets the model value to 0. But i'm getting a compilation error:
The = operator is reserved for defining variables. Maybe you want == instead? Or
maybe you are defining a variable, but there is whitespace before it?
Please help!
You only need to write the model's new value there. In this case, that'll be just 0:
Reset ->
0

XY prob: How do I issue a bunch of msgs simultaneously?

Almost definitely an XY problem but I can't think of a concise way to phrase what I'm trying to do.
I have a textarea. Inside this text area, the user enters a comma-separated list of ID numbers. When they click "Fetch", I split their input on commas, to get multiple string values, and each of those string values gets passed to a function that makes a HTTP request to my API getting info on the item.
That's where I stumble. Right now I have these parts:
getInfo : String -> Cmd Msg, takes an ID string and ultimately fires Http.send
type Msg = Fetch String, where Fetch idStr -> (model, getInfo idStr)
I sort of want to take my textarea's input and say String.split "," |> List.map (\id -> getInfo id). Except I don't know what to do with the List Msg that would give me, I want to fire each of those msgs off, but Elm doesn't work that way?
While reading I found Cmd.batch, but there's not really any info on it in the docs so I'm not sure if that's what I want or how to use it.
Yes, Cmd.batch can batch multiple cmds into one cmd.
For example (via new message: FetchAll):
FetchAll idsStr ->
let
cmds = String.split "," idsStr |> List.map (\id -> getInfo id)
in (model, Cmd.batch cmds)
also, (model, Cmd.batch cmds) can be written as model ! cmds

Elm: Pass current time as an effect to Tick Action, after model update

In update I would like to call my Tick Action every time Input is called.
The scenario is that a user enters a value in a text field, on update the model is updated via Input and then Tick is called and more stuff is performed on the model.
In 0.16 I could do something like this :
Input query ->
({ model | query = query }, Effects.tick Tick)
Tick clockTime ->
-- do something with clockTime
I'm not sure how to do this in 0.17.
I'm not sure if this would be a subscription and if it were, how you could go about configuring it to call Input then Action.
Any help is appreciated.
The functionality for retrieving the current time as an effect has been moved into a Task, in the Time module under Time.now.
http://package.elm-lang.org/packages/elm-lang/core/4.0.1/Time#now
You can reproduce your functionality by making the following changes:
1) Make sure there is a NoOp message available in your messages. Time.now returns a Task which we know will never fail, but we still need a failure message to hand to Task.perform
type Msg
= Input String
| Tick Time
| NoOp
2) Replace Effects.tick Tick with Time.now and Task.perform
Input query ->
( { model | query = query }
, Time.now |> Task.perform (\_ -> NoOp) Tick
)
If you don't like the NoOp message, there are other ways around it, such as using Debug.crash or performFailproof from Task.Extra (found here: http://package.elm-lang.org/packages/NoRedInk/elm-task-extra/2.0.0/Task-Extra#performFailproof)

How to define function with record argument in erlang

I want to define the structure of my function input.
This module defines my function (part of it is pseudo code):
-module(my_service).
-export([my_function/2]).
-type my_enum() :: 1 | 2 | 5.
-record(my_record, {id = null :: string(), name = null :: string(), myType = null :: my_enum()}).
-spec my_function(MyValue#my_record) -> Result#my_record.
my_function(ClientRequest, MyValue) when is_record(Entry, my_record) ->
Response = client:request(get, "http://www.example.com/api?value=" + MyValue#my_record.name),
case Response of
{ok, Status, Head, Body} ->
Result = parse_response(Body, my_record);
Error ->
Error
end.
This is how I want to call it:
-module(test1).
-import(io, [format/2]).
main(_) ->
application:start(inets),
MyTestValue = #my_record{name => "test value", myType => 2},
X = my_service:my_function(MyTestValue),
io:format("Response: ~p", [X]).
So, any idea how I can force the type of the function input to be of type my_record?
It's often handy to destructure a record directly in the function argument list, which also forces that argument to have the desired record type. For example, we can slightly modify your my_function/2 like this:
my_function(ClientRequest, #my_record{name=Name}=MyValue) ->
Response = client:request(get, "http://www.example.com/api?value=" ++ Name),
case Response of
{ok, Status, Head, Body} ->
Result = parse_response(Body, MyValue);
Error ->
Error
end.
Note how we're pattern-matching the second parameter MyValue: we're not only asserting that it's a #my_record{} instance, but we're also extracting its name field into the Name variable at the same time. This is handy because we use Name on the first line of the function body. Note that we also keep the parameter name MyValue because we pass it to parse_response/2 within the function body. If we didn't need it, we could instead write the function head like this:
my_function(ClientRequest, #my_record{name=Name}) ->
This would still have the desired effect of forcing the second argument to be of type #my_record{}, and would also still extract Name. If desired, you could extract other fields in a similar manner:
my_function(ClientRequest, #my_record{name=Name, id=Id}) ->
Both Name and Id are then available for use within the function body.
One other thing: don't use the atom null as a default for your record fields. The default for Erlang record fields is the atom undefined, so you should just use that. This is preferable to defining your own concept of null in the language.