How to define function with record argument in erlang - module

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.

Related

How do I get distinct items from a list?

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

Attribute Error when getting unique column values

I am new to Python and Pandas. I am trying to write a function to get a unique list of my column values. My function looks like below, where "placename" is the attribute that I want to get unique values. 'placename' should be passed as a string argument,corresponding to the header of the csv file.
def get_city_list(state, type, placename):
city_dir = os.path.join(baseDir, type + ".csv")
city_df = pd.read_csv(city_dir, quotechar = '"', skipinitialspace = True, sep = ",")
state_df = city_df.loc[city_df["state"] == state]
city_list = state_df.placename.unique()
return city_list
However, when I call this function, it throws a attribute error saying 'DataFrame' object has no attribute "placename". It seems that "placename" should not be a string, and when I substitute it as
city_list = state_df.cityname.unique(), it works, where cityname (without " ") is the actual header of the column in the original csv file. Since I want to make my function versatile,I want to find a way to deal with this case so that I dont have to manually change the content of "placename" every time.
Your help is greatly appreciated!
Thanks
The dot operator . is reserved to access attributes of an object. pandas is nice enough to make column names accessible via an attribute. But you can't do something like df."myplace"
Change your code to:
state_df[placename].unique()
This way, you are passing placename on to the getitem method.

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)

Using a filter in part of a string

I have a filter that formats a number based on what stat type it is (percent, integer, currency, etc). I want to use this filter on a value as part of a string. See below:
<js-widget
v-on:click="this.selectedReportId = key"
:selected="this.selectedReportId == key"
:icon="report.icon"
:stat="report.current | stat report.statType"
:title="report.title"
:tooltip="report.tooltip"
:percent="report.percent"
:description="'Previous value: '+(report.past | stat report.statType)">
</js-widget>
As you can see in the :stat parameter, I can use the filter if it is the only thing in the expression. But when it tries to evaluate the :description parameter, I get Error when evaluating expression. Is it possible to use a filter in this way?
It's been very long since the question was asked, but if someone searches, I happen to know the solution.
First there is a way to call a filter from inside the vue component. Actually all filters are stored in the
this.$options.filters
collection. So if you want to call a filter from inside the component you should do
...
this.$options.filters.stat(report.past, report.statType)
...
And if you want to do the same from the markup, you should also call filter not like filter (| syntax), but like the component method
<js-widget
v-on:click="this.selectedReportId = key"
:selected="this.selectedReportId == key"
:icon="report.icon"
:stat="report.current | stat report.statType"
:title="report.title"
:tooltip="report.tooltip"
:percent="report.percent"
:description="'Previous value: '+ $options.filters.stat(report.past, report.statType)">
</js-widget>
Or, better, with template string
:description = "`Previous value: ${$options.filters.stat(report.past, report.statType)}`"
The filter is something available outside of the expression you are evaluating – so that's not going to work syntactially
I would use a computed property instead. For more information, see http://vuejs.org/guide/computed.html.