I'm writing an Elm app where a major part of the state is in a Dict with Records as values. I have an update function for the main state model, and an update function for the individual records in the Dict. Is there a way to use Dict.update with the record state update function?
The issue I'm having is that the state update function for the records returns what update functions usually return: a tuple with the updated object and any commands to trigger (e.g. (newRecord, Cmd.none)). But the Dict.update function needs to take in a record and return a record (e.g. just newRecord), not a tuple with a record and a command object.
Is there a way around this? Right now I've got it working using a combination of Dict.get and Dict.insert, but this seems clumsy.
If update function for record always return Cmd.none, you can simplify it and return only the updated model.
Child modules do not have to follow ( Model, Cmd Msg ) convention if your application architecture does not require that.
If you do need to pass Commands from the lower level, it is also possible to re-structure your child module's update function to simplify those updates.
Example based on example/http
Here is an example of how you can split the update so that you could re-use the same logic in the top-level update without additional trickery.
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
(updateModel msg model, updateCmd msg model)
updateCmd: Msg -> Model -> Cmd Msg
updateCmd msg model =
case msg of
MorePlease ->
getRandomGif model.topic
_ ->
Cmd.none
updateModel: Msg -> Model -> Model
updateModel msg model =
case msg of
NewGif (Ok newUrl) ->
Model model.topic newUrl
_ ->
model
If you need the updated model in updateCmd, then just pass it instead of current model or even pass both if you desire.
As a bonus, you can entirely omit unused branches of the case expression.
Using Dict.update
It will also be possible to use updateModel in Dict.update without retrieving the record and writing it back.
Dict.update childKey (Maybe.map (updateModel childMsg)) model
Not sure if this is what you are looking for, but if you have a nested Dict structure in your model, like this:
type alias Model =
{ parentsAndChildren : Dict String (Dict String Int) }
Then it is not necessary to make the child update output a Cmd. Your update could look something like this:
update : Msg -> Model -> Model
update msg model =
case msg of
NewChild parentName childName age ->
let
newModel =
{ model
| parentsAndChildren =
model.parentsAndChildren
|> Dict.update
parentName
(Maybe.map insertChild)
}
in
(newModel, Cmd.none)
-- helper function to update a child Dict
insertChild: String -> Int -> Dict (String Int) -> Dict (String Int)
insertChild name age childDict =
Dict.insert name age childDict
The only update function that NEEDS to output a Cmd is the update function in your top component.
So your child update function doesn't have to output a Cmd.
Not that I know of. I have this helper function in my one of my projects
updateDict : comparable -> msg -> Dict comparable b -> (msg -> b -> ( b, Cmd msg )) -> (comparable -> msg -> c) -> ( Dict comparable b, Cmd c )
updateDict uid act dict fn wrapper =
case Dict.get uid dict |> Maybe.map (fn act) of
Just ( m, e ) ->
( Dict.insert uid m dict
, Cmd.map (wrapper uid) e
)
Nothing ->
( dict, Cmd.none )
Related
Is it possible in F# to apply a value as though it is a function? For example:
let record =
{|
Func = fun x -> x * x
Message = "hello"
|}
let message = record.Message // unsweetened field access
let unsweet = record.Func 3 // unsweetened function application
let sweet = record 3 // sweetened function application
Right now, the last line doesn't compile, of course:
error FS0003: This value is not a function and cannot be applied.
Is there some sort of syntactical sweetener that would allow me to "route" function application as I see fit, while still retaining its normal unsweetened behavior as well? I'm thinking of something magic like this:
// magically apply a record as though it was a function
let () record arg =
record.Func arg
(Note: I used a record in this example, but I'd be happy with a class as well.)
The closest thing I can think of would be a custom operator using statically resolved type parameters to resolve a specific property of type FSharpFunc, then invoke the function with the supplied input parameter. Something like this:
let record =
{|
Func = fun x -> x * x
Message = "hello"
|}
let inline (>.>) (r: ^a) v =
let f = (^a : (member Func: FSharpFunc< ^b, ^c>) r)
f v
record >.> 3 // val it : int = 9
I want to decode json string like below.
"[[\"aaa\",1,2,3,4],[\"bbb\",1,2,3]]"
and decode to Elm tuple list.
[("aaa",[1,2,3,4]),("bbb",[1,2,3])] : List (String, List Int)
How to decode it?
jsdecode=index 0 string
|> andThen xxxxxxx??
This isn't straightforward to do, but before I jump straight in how to do it, let me collect a series of thoughts about the data we are trying to decode:
We are decoding a list of lists
Each list should be composed by a starting string and a series of values
But actually there might be an empty list, no initial string but some values or an initial string and no values
So in my mind the difficulty of building the right decoder reflects the complexity of handling all these edge cases. But let's start defining the data we would like to have:
type alias Record =
( String, List Int )
type alias Model =
List Record
jsonString : String
jsonString =
"[[\"aaa\",1,2,3,4],[\"bbb\",1,2,3]]"
decoder : Decoder Model
decoder =
Decode.list recordDecoder
Now we need to define a type that represents that the list could contain either strings or ints
type EntryFlags
= EntryId String
| EntryValue Int
type RecordFlags
= List EntryFlags
And now for our decoder
recordDecoder : Decoder Record
recordDecoder =
Decode.list
(Decode.oneOf
[ Decode.map EntryId Decode.string
, Decode.map EntryValue Decode.int
]
)
|> Decode.andThen buildRecord
So buildRecord takes this list of EntryId String or EntryValue Int and builds the record we are looking for.
buildRecord : List EntryFlags -> Decoder Record
buildRecord list =
case list of
[] ->
Decode.fail "No values were passed"
[ x ] ->
Decode.fail "Only key passed, but no values"
x :: xs ->
case buildRecordFromFlags x xs of
Nothing ->
Decode.fail "Could not build record"
Just value ->
Decode.succeed value
As you can see, we are dealing with a lot of edge cases in our decoder. Now for the last bit let's check out buildRecordFromFlags:
buildRecordFromFlags : EntryFlags -> List EntryFlags -> Maybe Record
buildRecordFromFlags idEntry valueEntries =
let
maybeId =
case idEntry of
EntryId value ->
Just value
_ ->
Nothing
maybeEntries =
List.map
(\valueEntry ->
case valueEntry of
EntryValue value ->
Just value
_ ->
Nothing
)
valueEntries
|> Maybe.Extra.combine
in
case ( maybeId, maybeEntries ) of
( Just id, Just entries ) ->
Just ( id, entries )
_ ->
Nothing
In this last bit, we are using a function from maybe-extra to verify that all the values following the initial EntryId are indeed all of the EntryValue type.
You can check out a working example here: https://ellie-app.com/3SwvFPjmKYFa1
There are two subproblems here: 1. decoding the list, and 2. transforming it to the shape you need. You could do it as #SimonH suggests by decoding to a list of JSON values, post processing it and then (or during the post-processing) decode the inner values. I would instead prefer to decode it fully into a custom type first, and then do the post processing entirely in the realm of Elm types.
So, step 1, decoding:
type JsonListValue
= String String
| Int Int
decodeListValue : Decode.Decoder JsonListValue
decodeListValue =
Decode.oneOf
[ Decode.string |> Decode.map String
, Decode.int |> Decode.map Int
]
decoder : Decode.Decoder (List (List JsonListValue))
decoder =
Decode.list (Decode.list decodeListValue)
This is a basic pattern you can use to decode any heterogenous array. Just use oneOf to try a list of decoders in order, and map each decoded value to a common type, typically a custom type with a simple constructor for each type of value.
Then onto step 2, the transformation:
extractInts : List JsonListValue -> List Int
extractInts list =
list
|> List.foldr
(\item acc ->
case item of
Int n ->
n :: acc
_ ->
acc
)
[]
postProcess : List JsonListValue -> Result String ( String, List Int )
postProcess list =
case list of
(String first) :: rest ->
Ok ( first, extractInts rest )
_ ->
Err "first item is not a string"
postProcess will match the first item to a String, run extractInts on the rest, which should all be Ints, then put them together into the tuple you want. If the first item is not a String it will return an error.
extractInts folds over each item and adds it to the list if it is an Int and ignores it otherwise. Note that it does not return an error if an item is not an Int, it just doesn't include it.
Both of these functions could have been written to either fail if the values don't conform to the expectations, like postProcess, or to handle it "gracefully", like extractInts. I chose to do one of each just to illustrate how you might do both.
Then, step 3, is to put it together:
Decode.decodeString decoder json
|> Result.mapError Decode.errorToString
|> Result.andThen
(List.map postProcess >> Result.Extra.combine)
Here Result.mapError is used to get error from decoding to conform to error type we get from postProcess. Result.Extra.combine is a function from elm-community/result-extra which turns a List of Results into a Result of List, which comes in very handy here.
data Task = Task
{ id :: String
, description :: String
, dependsOn :: [String]
, dependentTasks :: [String]
} deriving (Eq, Show, Generic, ToJSON, FromJSON)
type Storage = Map String Task
s :: Storage
s = empty
addTask :: Task -> Storage -> Storage
addTask (Task id desc dep dept) = insert id (Task id desc dep dept)
removeTask :: String -> Storage -> Storage
removeTask tid = delete tid
changes = [addTask (Task "1" "Description" [] []), removeTask "1"]
main = putStrLn . show $ foldl (\s c -> c s) s changes
Suppose I have the following code. I want to store changes list in a json file. But I don't know how to do that with Aeson, aside probably from writing a custom parser and there must be a better way to do that obviously. Like maybe using language extension to derive (Generic, ToJSON, FromJSON) for addTask and removeTask etc...
EDIT. For all people that say "You can't serialize function".
Read the comments to an answer to this question.
Instance Show for function
That said, it's not possible to define Show to actually give you more
? detail about the function. – Louis Wasserman May 12 '12 at 14:51
Sure it is. It can show the type (given via Typeable); or it can show some of the inputs and outputs (as is done in QuickCheck).
EDIT2. Okay, I got that I can't have function name in serialization. But can this be done via template Haskell? I see that aeson supports serialization via template Haskell, but as newcomer to Haskell can't figure out how to do that.
Reading between the lines a bit, a recurring question here is, "Why can't I serialize a function (easily)?" The answer -- which several people have mentioned, but not explained clearly -- is that Haskell is dedicated to referential transparency. Referential transparency says that you can replace a definition with its defined value (and vice versa) without changing the meaning of the program.
So now, let's suppose we had a hypothetical serializeFunction, which in the presence of this code:
foo x y = x + y + 3
Would have this behavior:
> serializeFunction (foo 5)
"foo 5"
I guess you wouldn't object too strenuously if I also claimed that in the presence of
bar x y = x + y + 3
we would "want" this behavior:
> serializeFunction (bar 5)
"bar 5"
And now we have a problem, because by referential transparency
serializeFunction (foo 5)
= { definition of foo }
serializeFunction (\y -> 5 + y + 3)
= { definition of bar }
serializeFunction (bar 5)
but "foo 5" does not equal "bar 5".
The obvious followup question is: why do we demand referential transparency? There are at least two good reasons: first, it allows equational reasoning like above, hence eases the burden of refactoring; and second, it reduces the amount of runtime information that's needed, hence improving performance.
Of course, if you can come up with a representation of functions that respects referential transparency, that poses no problems. Here are some ideas in that direction:
printing the type of the function
instance (Typeable a, Typeable b) => Show (a -> b) where
show = show . typeOf
-- can only write a Read instance for trivial functions
printing the input-output behavior of the function (which can also be read back in)
creating a data type that combines a function with its name, and then printing that name
data Named a = Named String a
instance Show (Named a) where
show (Named n _) = n
-- perhaps you could write an instance Read (Map String a -> Named a)
(and see also cloud haskell for a more complete working of this idea)
constructing an algebraic data type that can represent all the expressions you care about but contains only basic types that already have a Show instance and serializing that (e.g. as described in the other answer)
But printing a bare function's name is in conflict with referential transparency.
Make a data type for your functions and an evaluation function:
data TaskFunction = AddTask Task | RemoveTask String
deriving (Eq, Show, Generic, ToJSON, FromJSON)
eval :: TaskFunction -> Storage -> Storage
eval (AddTask t) = addTask t
eval (RemoveTask t) = removeTask t
changes = [AddTask (Task "1" "Description" [] []), RemoveTask "1"]
main = putStrLn . show $ foldl (\s c -> c s) s (eval <$> changes)
I am new to Deedle, and in documentation I cant find how to solve my problem.
I bind an SQL Table to a Deedle Frame using the following code:
namespace teste
open FSharp.Data.Sql
open Deedle
open System.Linq
module DatabaseService =
[<Literal>]
let connectionString = "Data Source=*********;Initial Catalog=*******;Persist Security Info=True;User ID=sa;Password=****";
type bd = SqlDataProvider<
ConnectionString = connectionString,
DatabaseVendor = Common.DatabaseProviderTypes.MSSQLSERVER >
type Database() =
static member contextDbo() =
bd.GetDataContext().Dbo
static member acAgregations() =
Database.contextDbo().AcAgregations |> Frame.ofRecords
static member acBusyHourDefinition() =
Database.contextDbo().AcBusyHourDefinition
|> Frame.ofRecords "alternative_reference_table_scan", "formula"]
static member acBusyHourDefinitionFilterByTimeAgregationTipe(value:int) =
Database.acBusyHourDefinition()
|> Frame.getRows
These things are working properly becuse I can't understand the Data Frame Schema, for my surprise, this is not a representation of the table.
My question is:
how can I access my database elements by Rows instead of Columns (columns is the Deedle Default)? I Thied what is showed in documentation, but unfortunatelly, the columns names are not recognized, as is in the CSV example in Deedle Website.
With Frame.ofRecords you can extract the table into a dataframe and then operate on its rows or columns. In this case I have a very simple table. This is for SQL Server but I assume MySQL will work the same. If you provide more details in your question the solution can narrowed down.
This is the table, indexed by ID, which is Int64:
You can work with the rows or the columns:
#if INTERACTIVE
#load #"..\..\FSLAB\packages\FsLab\FsLab.fsx"
#r "System.Data.Linq.dll"
#r "FSharp.Data.TypeProviders.dll"
#endif
//open FSharp.Data
//open System.Data.Linq
open Microsoft.FSharp.Data.TypeProviders
open Deedle
[<Literal>]
let connectionString1 = #"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\userName\Documents\tes.sdf.mdf"
type dbSchema = SqlDataConnection<connectionString1>
let dbx = dbSchema.GetDataContext()
let table1 = dbx.Table_1
query { for row in table1 do
select row} |> Seq.takeWhile (fun x -> x.ID < 10L) |> Seq.toList
// check if we can connect to the DB.
let df = table1 |> Frame.ofRecords // pull the table into a df
let df = df.IndexRows<System.Int64>("ID") // if you need an index
df.GetRows(2L) // Get the second row, but this can be any kind of index/key
df.["Number"].GetSlice(Some 2L, Some 5L) // get the 2nd to 5th row from the Number column
Will get you the following output:
val it : Series<System.Int64,float> =
2 -> 2
>
val it : Series<System.Int64,float> =
2 -> 2
3 -> 3
4 -> 4
5 -> 5
Depending on what you're trying to do Selecting Specific Rows in Deedle might also work.
Edit
From your comment you appear to be working with some large table. Depending on how much memory you have and how large the table you still might be able to load it. If not these are some of things you can do in increasing complexity:
Use a query { } expression like above to narrow the dataset on the database server and convert just part of the result into a dataframe. You can do quite complex transformations so you might not even need the dataframe in the end. This is basically Linq2Sql.
Use lazy loading in Deedle. This works with series so you can get a few series and reassemble a dataframe.
Use Big Deedle which is designed for this sort of thing.
I am new to Erlang and noticed that there is no native function to create json string from lists (Or is there?). I use this method to create json string in Erlang but do not know if it will not malfunction.
Here is an example of my method:
-module(index).
-export([test/0]).
test() ->
Ma = "Hello World", Mb = "Hello Erlang",
A = "{\"Messages\" : [\"" ++ Ma ++ "\", \""++Mb++"\"], \"Usernames\" : [\"Username1\", \"Username2\"]}",
A.
The result is:
388> test().
"{\"Messages\" : [\"Hello World\", \"Hello Erlang\"], \"Usernames\" : [\"Username1\", \"Username2\"]}"
389>
I think this is the expected result but is there any chance that this method may malfunction when included special characters, such as: <, >, & / \ " ??
What precautions should I take to make this method stronger?
If Ma or Mb contains double quotes or whatever control characters, the parsing from string to JSON will fail. This parsing may never occur in Erlang, as Erlang does not have string to JSON conversion built-in.
It's a good idea to use binaries (<<"I am a binary string">>), as lists consume a lot more resources.
We're using jiffy, which is implemented as a NIF and hence is reasonably fast and it allows for document construction like so:
jiffy:decode(<<"{\"foo\": \"bar\"}">>).
{[{<<"foo">>,<<"bar">>}]}
Doc = {[{foo, [<<"bing">>, 2.3, true]}]}.
{[{foo,[<<"bing">>,2.3,true]}]}
jiffy:encode(Doc).
<<"{\"foo\":[\"bing\",2.3,true]}">>
I had this very same problem, searched high and low and in the end came up with my own method. This is purely just pointing people in the right directions to finding a solution for themselves. Note: I tried jiffy but as I'm using rebar3 it's not currently compatible.
Im using MS sql server so i use the Erlang odbc module: http://erlang.org/doc/man/odbc.html
The odbc:sql_query/2 gives me back {selected, Columns, Results}
From here i can take the Columns which is a list of strings & the Results, a list of rows represented each as a tuple, then create a few functions to output valid Erlang code to be able to serialize correctly to Json based on a number of factors. Here's the full code:
make the initial query:
Sql = "SELECT * FROM alloys;",
Ref = connect(),
case odbc:sql_query(Ref, Sql) of
{selected, Columns, Results} ->
set_json_from_sql(Columns, Results, []);
{error, Reason} ->
{error, Reason}
end.
Then the input function is set_json_from_sql/3 that calls the below functions:
format_by_type(Item) ->
if
is_list(Item) -> list_to_binary(io_lib:format("~s", [Item]));
is_integer(Item) -> Item;
is_boolean(Item) -> io_lib:format("~a", [Item]);
is_atom(Item) -> Item
end.
json_by_type([H], [Hc], Data) ->
NewH = format_by_type(H),
set_json_flatten(Data, Hc, NewH);
json_by_type([H|T], [Hc|Tc], Data) ->
NewH = format_by_type(H),
NewData = set_json_flatten(Data, Hc, NewH),
json_by_type(T, Tc, NewData).
set_json_flatten(Data, Column, Row) ->
ResTuple = {list_to_binary(Column), Row},
lists:flatten(Data, [ResTuple]).
set_json_from_sql([], [], Data) -> jsone:encode([{<<"data">>, lists:reverse(Data)}]);
set_json_from_sql(Columns, [H], Data) ->
NewData = set_json_merge(H, Columns, Data),
set_json_from_sql([], [], NewData);
set_json_from_sql(Columns, [H|T], Data) ->
NewData = set_json_merge(H, Columns, Data),
set_json_from_sql(Columns, T, NewData).
set_json_merge(Row, Columns, Data) ->
TupleRow = json_by_type(tuple_to_list(Row), Columns, []),
lists:append([TupleRow], Data).
So set_json_from_sql/3 gives you your Json output after matching set_json_from_sql([], [], Data).
The key points here are that you need to call list_to_binary/1 for strings & atoms. Use jsone to encode Erlang objects to Json: https://github.com/sile/jsone
And, notice format_by_type/1 is used to match against Erlang object types, yes not ideal but works as long as you are aware of your DB's types or you can increase the extra guards to accommodate this.
This works for me
test()->
Ma = "Hello World", Mb = "Hello Erlang",
A = "{\"Messages\" : {{\"Ma\":\"" ++ Ma ++ "\"}, {\"Mb\":\""++Mb++"\"}}, {\"Usernames\" : {\"Username1\":\"usrname1\"}, {\"Username2\":\"usrname2\"}}",
io:format("~s~n",[A]).
Output
10> io:format("~s~n",[A]).
{"Messages" : {{"Ma":Hello World"}, {"Mb":Hello Erlang"}}, {"Usernames" : {"Username1":"usrname1"}, {"Username2":"usrname2"}}
ok
or use one of many libraries on github to convert erlang terms to json. My Tuple to JSON module is simple but effective.
Do it like a pro
-define(JSON_WRAPPER(Proplist), {Proplist}).
-spec from_list(json_proplist()) -> object().
from_list([]) -> new();
from_list(L) when is_list(L) -> ?JSON_WRAPPER(L).
-spec to_binary(atom() | string() | binary() | integer() | float() | pid() | iolist()) -> binary().
to_binary(X) when is_float(X) -> to_binary(mochinum:digits(X));
to_binary(X) when is_integer(X) -> list_to_binary(integer_to_list(X));
to_binary(X) when is_atom(X) -> list_to_binary(atom_to_list(X));
to_binary(X) when is_list(X) -> iolist_to_binary(X);
to_binary(X) when is_pid(X) -> to_binary(pid_to_list(X));
to_binary(X) when is_binary(X) -> X.
-spec recursive_from_proplist(any()) -> object().
recursive_from_proplist([]) -> new();
recursive_from_proplist(List) when is_list(List) ->
case lists:all(fun is_integer/1, List) of
'true' -> List;
'false' ->
from_list([{to_binary(K) ,recursive_from_proplist(V)}
|| {K,V} <- List
])
end;
recursive_from_proplist(Other) -> Other.