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
Related
It's not clear to me how to retrieve distinct items from a list.
I have the following code:
topicsFromLinks : List Link -> List Topic
topicsFromLinks links =
links
|> List.map (\l -> l.topics)
|> List.concat
|> Set.fromList
|> Set.toList
Error:
The definition of topicsFromLinks does not match its type
annotation. - The type annotation for topicsFromLinks says it always
returns:
List Topic
But the returned value (shown above) is a:
List comparable
I expect the following lines to just work in regards to structural equality:
|> Set.fromList
|> Set.toList
Why am I receiving a List of Comparables?
How do I resolve this compilation error?
Appendix:
type alias Topic =
{ name : String, isFeatured : Bool }
type alias Link =
{
...
, topics : List Topic
...
}
According to the documentation for Set:
The values can be any comparable type. This includes Int, Float, Time, Char, String, and tuples or lists of comparable types.
You are attempting to put a Topic value in where only a comparable type will work.
Thankfully there is the elm-community/list-extra package which exposes a uniqueBy function that lets you specify your own function to turn something into a comparable.
If you want to get the distinct list of topics, matching both on the name and isFeatured fields, then you can use toString to serialize the value into something comparable:
import List.Extra exposing (uniqueBy)
topicsFromLinks : List Link -> List Topic
topicsFromLinks links =
links
|> List.map .topics
|> List.concat
|> uniqueBy toString
In 2021, rather than using an external library and writing a clumsy toString method (since Debug.toString isn't available for production use anymore), consider using a fold:
unique : List a -> List a
unique list =
List.foldl
(\a uniques ->
if List.member a uniques then
uniques
else
uniques ++ [ a ]
)
[]
list
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)
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)
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.
I'm currently learning to code Erlang. I have a web application on top of Chicago Boss.
I have a model called Todo, and I would like to offer CRUD operations on it as a REST API.
In my PUT method I have this code:
index('PUT', [Id]) ->
Todo = boss_db:find(Id),
Body = element(2, mochijson:decode(Req:request_body())),
%% Set the new values
NewTodo = Todo:attributes([
{subject, proplists:get_value("subject", Body)},
{done, proplists:get_value("done", Body)}
])
,
{json, [{todo, element(2, NewTodo:save())}]}.
How can I optimize this code fragment? Or is this already the best possible?
Is there some "smarter" way to change the keys of a proplist to atom keys? Like this:
[{"subject", "Foo"}] -> [{subject, "Foo"}].
I also find it kind of tedious to assign a Todo variable and then have a NewTodo. Sadly I can't find some good example Erlang Chicago Boss apps on github that I can check out.
You always can do something like this:
t([{"subject", V}|T]) -> [{subject, V}|t(T)];
t([{"done" , V}|T]) -> [{done, V}|t(T)];
t([_ |T]) -> t(T) ; % optional garbage ignoring clause
t([]) -> [].
But I doubt, it will be significant speed improvement in your case.
May be you will be able squeeze last bit from this:
-compile({inline, [t/1]}).
t(L) -> t(L, []).
t([{"subject", V}|T], A) -> t(T, [{subject, V}|A]);
t([{"done" , V}|T], A) -> t(T, [{done, V}|A]);
t([_ |T], A) -> t(T, A); % optional garbage ignoring clause
t([], A) -> A.
Which is worth only for benchmark code competitions ;-) (Note there is not lists:reverse/1 call in last clause. It would be ruining improvement form previous version.)
P.S.: If you think I'm micro-optimization freak, you are right, so I would replace lists:reverse/1 call with lists:reverse/2 to use BIF directly and save some more time ;-)
Unfortunately I cannot comment on Hynek's answer, but as Erlang newbie, my first guess would have been to go for something along the lines of:
lists:map(fun({A, B}) -> {list_to_atom(A), B} end, [X || {Y, Z}=X <- List, is_list(Y)]).
You cannot really avoid the NewTodo assignment
How about
index('PUT', [Id]) ->
Body = element(2, mochijson:decode(Req:request_body())),
OldTodo = boss_db:find(Id),
NewTodo = OldTodo:attributes([ {list_to_atom(A),B} || {A,B}<-Body ]),
{json, [{todo, element(2, NewTodo:save())}]}.