There was examples of updating a map in a map in the SF talk, what have they become? I see nothing about this in the EEP.
K does not equal any existing key in the map, a new association
will be created from key K to value V. If key K is equal to an existing
key in map M its associated value will be replaced by the new value V and
the new key K will replace the old key if the key does not match. In both
cases the evaluated map expression will return a new map.On 05/10/2013 05:03 AM, Richard A. O'Keefe wrote:I think that's the bigger issue with frames. Are they worth spending the time implementing considering they are essentially a records replacement? Records work good enough for most purposes, with the exception of upgrades, which few people do anyway.
Frames are optimised (pared to the bone, in fact) for use in
record-like ways. They are somewhere between pathetic and
hopeless as general purpose dictionaries.
Erlang newbie here. This comment therefore is not about the merits of the proposals.
I detect a certain tendency to dismiss suggestions if the suggestions are not germane to that particular user's current (or past) needs. Maybe the suggestions didn't originate from a particular community of Erlang users. I hope things don't get dismissed because of NIH syndrome.
There are very few (a handful) who take the time and trouble ROK seems to take to explore and explain the nuances of choices. I have followed his passionate support of Frames and read his proposal and see the value of something that replaces records. Since I am not a professional user of Erlang I will leave it to others to debate the technical merits. I just hope that folks ask the question whether it has to be an 'either/or' decision between Maps and Frames - can it be both.
Now I will go back to watching these highly educational exchanges on the technical merits of the proposals.
-A newbie
I agree we should avoid cluttering the language up, but if we view/use Maps as replacement for Dicts and Frames as replacement for Records, then the situation wouldn't be any more cluttered than it already is.
Tom
- are to be a dynamic alternative to records which is a Good Thing.
- are to be a faster replacement to dicts, which is always useful.
The problems are different enough so that I think that in trying to be a solution to both will mean that they will do neither in the best way. So either pick one and solve it properly, or come with two solutions.
From my own experience I would say save the new syntax for the record alternative, this is where I would get the most out of it. Having atoms as keys would pose me no problems. Use a new data type here.
For the dict alternative having a special syntax would give me very little so I wouldn't bother about it, using functions calls handles my usage very well. It also makes it easy to test alternate implementations to find the one with the most appropriate properties, just change the name of the module. For a built in type I would prefer speed to ordering. Actually you probably wouldn't need a built in type, just suitable BIFs to manage existing tuples/lists.
I think I agree with ROK here.
Anyway, that's my take on it,
Robert
You break my heart.
It's the one thing that is important to me. I don't care about the
implementation speed at all.
--
Loïc Hoguin
Erlang Cowboy
Nine Nines
http://ninenines.eu
Robert
add_item(Player=#{ inventory => #{ ItemID => # { quantity = Q }}},
ItemID, ItemInfos) ->
Player#{ inventory => #{ ItemID => #{ quantity => Q + 1 }}};
add_item(Player, ItemID, ItemInfos) ->
Player#{ inventory => #{ ItemID => ItemInfos }}.
Pretty clear what this code is doing, the equivalent with functions
leaves me scratching my head. This kind of syntax also allows you to get
it right the first time a lot more often than with functions.
I need to do things like this for more than 500 different actions.
There's not enough time in the world to do it with functions. That
particular project is on hold until I can avoid wasting my time btw.
Even before taking a really deep dive into studying the EEP one thing I can immediately say: get rid of this having both equal and match and USE ONLY MATCH. Keys are the same when they match. Period. This paragraph:
If keyKdoes not equal any existing key in the map, a new association will be created from keyKto valueV. If keyKis equal to an existing key in mapMits associated value will be replaced by the new valueVand the new keyKwill replace the old key if the key does not match. In both cases the evaluated map expression will return a new map.
is weird. Yes I know what it means but it is not intuitive. When keys are replaced or not replaced when they are equal is not can seem very strange to those not deep into erlang semantics.
---As regards efficiency, utility, beauty and so on these are subjective.If you want the last ounce of efficiency records and dicts are notgoing to go away when maps arrive. So if maps have the wrongperformance characteristics then use the exiting mechanisms.In the latest addition of my book I've been documenting the changes to maps- this chapter has changed three times and has tended to be conservativeso I haven't (yet) mentioned that keys can be any term (and not just atoms).I'm rather looking forward to being able to represent things like XMLand JSON and property lists in a maps and to have an one-size-fits-allreplacement for dicts and records. I've never really worried about thelast ounce of efficiency - if I want real efficiency I'd changelanguage and go to C or program an FPGA.
El May 14, 2013, a las 7:49 AM, Jesper Louis Andersen <jesper.lou...@erlang-solutions.com> escribió:
>
> On May 14, 2013, at 1:43 PM, "Jon Schneider" <j...@axismilton.ltd.uk> wrote:
>
>>> That's simply not true. Clock rate doesn't increase, but performance
>>> continues getting better. Core i7 CPUs (and Xeon equivalents) are a huge
>>> improvement over the previous generation, even for single-threaded
>>> operations.
>>
>> Some would use the word slight rather than huge.
>>
>> Available processing power is no excuse to throw it down the toilet.
>
+1
> One problem with more processing power is that we have a memory bandwidth bottleneck as well and we can't move data from the memory banks onto the CPU at ease. Optimizing for tighter data representations helps these days. Perhaps more than gaining a clock cycle here and there.
>
+1
Tom
Michael, please don't the idea that I am coming down on you specifically here. I am not. You just had the misfortune to mention this near the top where I could easily see it. And it is something I have been pondering about for a long time. It is one of the classic complaints about Erlang (after ;, if, variables, ...), why isn't there a built-in map type.
One of the ideas behind dicts/orddicts was to provide a standardised API which would allow you to choose and easily change which implementation you need/want. Unfortunately we never wrote this down anywhere and only two different versions exist (though I have a ttdict based on 2-3 trees if anyone needs a sorted tree, very fast too). While this forced you to think it would give you lots of options. One of the bad things of having a built-in type is that you don't have think, but you won't always get the most suitable. And seriously thinking about your data structures is always a Good Thing.
Sorry for the rant here.
What *I* would like is:
- A dynamic record-like alternative which I call rr (Robert's records) to avoid getting into a discussion about what frames are/aren't.
- A really fast dict implementation. I don't care how it's done but speed is the main requirement. If it represented as a built-in type or a tree of tuples/lists I don't care, just as long as it is fast.
Loïc you and I can meet at dawn like gentlemen to discuss special syntax or not. Swords or pistols? :-)
Robert
Fred Hebert > #{_ := V} = #{a => 3}, > and variants. Value searching will not be allowed. Richard A. O'Keefe > Frames > Maps are basically an extension of Frames which allows for arbitrary key terms.
Which could use your implementation proposal for small maps defined in source code, and switches to a tree (or something else) for larger dynamic sized Maps. |
On 05/14/2013 06:07 AM, Robert Virding wrote:
> One thing that has always puzzled me in these discussions is this fascination with *NATIVE* implementations. Why *native*? What is it with *native* that makes people want it? Is it the speed? Or is it the special syntax? Or what? Why care HOW things are done just as long as it fulfils my requirements. When I want a fast dictionary then FAST is the requirement, same with ORDERED or BAGS or ... . How it is done I don't care. If a special syntax is helpful then great, it is the syntax I am after. People complain that you need a special data type or else you can get them mixed up. I don't buy that. I work with lists, tuples, proplists, orddicts, dicts, gb_trees, records, etc together in one application and one problem I don't have is getting them mixed up. I see it as a solution to a problem which is at worst only very small.
I think my concern about whether the implementation is native (Erlang-only source code) or not relates to the performance characteristics. It seems as if we need C integration to get faster than dict on a wide variety of key types. I agree that it is best to have many different types of dictionary implementations, and ideally we would have a bunch of dictionaries implemented in C also :-) However, I understand that more testing and considerations are necessary to make the C integration decent performance-wise and fault-tolerance-wise. Basically, what seems required, is performance similar to the process dictionary, or better, as a separate type, called Maps.
>
> Michael, please don't the idea that I am coming down on you specifically here. I am not. You just had the misfortune to mention this near the top where I could easily see it. And it is something I have been pondering about for a long time. It is one of the classic complaints about Erlang (after ;, if, variables, ...), why isn't there a built-in map type.
>
> One of the ideas behind dicts/orddicts was to provide a standardised API which would allow you to choose and easily change which implementation you need/want. Unfortunately we never wrote this down anywhere and only two different versions exist (though I have a ttdict based on 2-3 trees if anyone needs a sorted tree, very fast too). While this forced you to think it would give you lots of options. One of the bad things of having a built-in type is that you don't have think, but you won't always get the most suitable. And seriously thinking about your data structures is always a Good Thing.
I agree, and I wish OTP/Erlang was able to add more data structures like your rbtree to compliment the gbtree, and possibly resolving any differences between the aatree and the gbtree. So, I think having more data structures is more beneficial than having less, since it gives developers more options. Though I understand these decisions are probably conservative and slow moving. Please release the ttdict! Have been wanting to see that one for awhile.
>
> Sorry for the rant here.
>
> What *I* would like is:
>
> - A dynamic record-like alternative which I call rr (Robert's records) to avoid getting into a discussion about what frames are/aren't.
>
> - A really fast dict implementation. I don't care how it's done but speed is the main requirement. If it represented as a built-in type or a tree of tuples/lists I don't care, just as long as it is fast.
I agree a record-like alternative would be nice. I just see it as less important than the fast dictionary implementation. So, if given the choice between the two, I am happy we can get the fast dictionary implementation.
The record-like alternative is supposedly most important for external term representation, since it is a separate type. Normally, usage of records as external terms are a type of message format used in communication with ports or other things (cnodes, or possibly port drivers). So, perhaps what we want is something a bit different from the concept of a "record" but rather a "message" type. A message could just be a generic container that defines a name, a header, and a body, where the header is normally empty (the header is always key->value) and the name is always an atom (or possibly atom or binary/string, to avoid excessive atom consumption). The body could be a binary that is accessed efficiently based on key->values where each key maps to an index. I will admit this idea is coming from a different angle, so it isn't well-aligned with the previous discussions, but it seems like trying to replace records as they are typically used is futile (unless we just make a type
specific to tuples with an atom to keep the same performance, and satisfy both concerns). Either way, it seems important to focus on what the record-like alternative is suppose to solve and the problems it is trying to solve. In the past, the discussion of frames seems to have gotten bogged down in syntax discussions, and convenience, but whatever record-like alternative which is pursued seems to require clearly stated benefits. So far, the main one I have seen is just that there is a way to represent records as external terms, and that reason isn't entirely compelling when you first see it. Simplifying debugging and development is probably an obvious goal, but to really do that, requires the same performance to replace records usage, so then you get back to replicating a tuple atom combo as a separate type, instead of focusing on a new type with new characteristics.
Pistols.
I don't disagree with most of what you say. The special syntax isn't
really needed until you get a project where you need to keep really many
things in the state (hundreds if not thousands of terms per connection)
and update them many times, in my case many times per second, while
still striving for low latency.
When you have that many values, your first enemy is verbosity, not only
to be able to write the code fast, but also to find the bugs in it
quicker. Execution speed only comes after. Of course by verbosity I
don't mean saving a character or two, I mean that what the code says is
simpler. For example, why would you want to say "I want to extract this
value V1 then extract this value V2 then modify a value in V2 then put
V2 back in V1 then put V1 in the original structure" instead of "I want
to modify this deep value there"? We shouldn't have to worry about all
these intermediate operations. They're a lot longer to write, and a
bigger source of error.
My personal use case is probably not that universal, but I believe it
also applies to anyone who has to access or modify JSON or equivalent
(not XML). How do you access deep values in JSON in Erlang? How do you
update a value in JSON? How do you update 20 of them? Painfully. You can
write functions to do it if you don't need to do it many times in your
program, but the more you need to do it the more you'll want a special
syntax to actually get things done instead of handling the details of
the modification.
--
Loïc Hoguin
Erlang Cowboy
Nine Nines
http://ninenines.eu
I was looking at an example:
1> M = #{ 1 => a },
#{ 1 => a }
2> M#{ 1.0 => b }.
#{ 1.0 => b }.
3> M#{ 1 := b }.
#{ 1 => b }
4> M#{ 1.0 := b }.
** exception error: bad argument
And given the definition where we have 'match' and 'equal' being two
different things, am I right in understanding that '=>' can both create
a mapping and update it based on 'equal' whereas ':=' can only update
them using 'match' ?
I don't think there is any mention of the expected behaviour when
matching on #{_ := V} = #{a => 3}, unless I missed it (there
were cases in map comprehensions, but these are obviously not the same).
Does this need to be specified? Similarly, #{_ := V} = #{a => 1, b => 1}
and #{K := 1} = #{a => 1, b => 1} do not seem specified to me.
On 05/13/2013 08:27 PM, Björn-Egil Dahlberg wrote:I think what Kenneth had in his talk was good. But
- Deep updates of Maps are not covered by this EEP. We would welcome
suggestions on this.
Simple update:
M#{ K => V }
Nested update:
M#{ K }#{ DK => DV }
I personally would prefer a more powerful:
M#{ K #{ DK => DV }}
Because it allows you to write things like:
M#{ K #{ DK => DV }, K2 => V2 }
And update multiple levels at once.
I know K can be a map, but deep update only works on values, the same way normal update only works on values, so no confusion is possible, we're only updating values, not K itself. If you feel an operator is needed it can be introduced between K and # in the two examples above.
Programmatically this would translate as extracting the map found at key K, and ensuring it's a map, updating this map with DK => DV, then placing this map back into the key K in map M along with setting V2 in key K2.
It doesn't sound hard to implement in the compiler, it's just unrolling things for access and rolling back again for the actual update. Exactly what we do manually today.
It should also be easy to compile to an optimized deep update with this syntax as you got everything in a single expression.
Note: If you are not interested in it despite how simple it is please at least ensure it can be done with a parse transform.
Thoughts?
_______________________________________________ eeps mailing list ee...@erlang.org http://erlang.org/mailman/listinfo/eeps
On 05/14/2013 08:21 PM, Björn-Egil Dahlberg wrote:
2013/5/14 Loďc Hoguin <es...@ninenines.eu <mailto:es...@ninenines.eu>>
It works fine if done in two steps (with the first step most likely done in the function clause itself).
doit(M=#{ a := A=#{ b := B }}) ->
M#{ a := A#{ b := B + 4 }}.
Writing this I realize it's quite possible that you can already do it with the current EEP. Am I right?
And it also fits:
doit(M=#{ a := A=#{ b := B }, aa := yes_do_it}) ->
M#{ a := A#{ b := B + 4 }, aa := done}.
You can already do this with records. Can we do this with maps too? This + any type as key + dynamic is all I need.
It would be cool if that could also be optimized at compile time, though that's more of an edge case so understandable if that comes later.
--
Loďc Hoguin
Could be worth adding to the EEP so as to not forget to test it. Should
I send a PR to your repository?
> It would be cool if that could also be optimized at compile time,
> though that's more of an edge case so understandable if that comes
> later.
>
>
> What was it you said to me? "Make it work, make it pretty, make it
> fast." =) Still on step oneish.
Yep. It was just a suggestion that heavily hinted at "optimize for that
case after everything else is optimized".
--
Loïc Hoguin
Erlang Cowboy
Nine Nines
http://ninenines.eu
On 05/14/2013 11:04 PM, Björn-Egil Dahlberg wrote:Could be worth adding to the EEP so as to not forget to test it. Should I send a PR to your repository?
It works fine if done in two steps (with the first step most likely
done in the function clause itself).
doit(M=#{ a := A=#{ b := B }}) ->
M#{ a := A#{ b := B + 4 }}.
Writing this I realize it's quite possible that you can already do
it with the current EEP. Am I right?
Yes.
And it also fits:
doit(M=#{ a := A=#{ b := B }, aa := yes_do_it}) ->
M#{ a := A#{ b := B + 4 }, aa := done}.
You can already do this with records. Can we do this with maps too?
This + any type as key + dynamic is all I need.
Yes. I had similar examples in the EEP but didn't nest the Maps.
Yep. It was just a suggestion that heavily hinted at "optimize for that case after everything else is optimized".
It would be cool if that could also be optimized at compile time,
though that's more of an edge case so understandable if that comes
later.
What was it you said to me? "Make it work, make it pretty, make it
fast." =) Still on step oneish.
From: "Dan Gudmundsson" <dg...@erlang.org>Robert Virding> Use only matchingWe would like to but we believe we can not do that because we can't output a defined order,i.e. you can not sort 1.0 and 1.We would need to define a new term order in erlang and we need to introduce somethinglike <:<, >:> =:< and >:=.
>
> On May 14, 2013, at 7:37 PM, Chris King <colan...@gmail.com> wrote:
>
>> Static structure does not preclude subtyping. OCaml's functional
>> objects capture this notion perfectly -- an object has a set of
>> methods which are known at compile time (hence static). However any
>> given function need only be aware of the presence of the subset of
>> methods in which it is interested. Note that this does not preclude a
>> function from creating a copy of said object with only those methods
>> modified!
>
> This is often called "structural subtyping" in contrast to "nominal
> subtyping". The difference is that the structural variant is implicit
> whereas the nominal variant is explicit. You have to explicitly define
> the relationsship in the nominal case, which is common in Java, C++, …
>
> Google Go is another language with structural subtyping in its interface
> model. There is also some structurality in the Standard ML module system
> w.r.t. signature checks.
Yes, I know.
> I agree we need something which is better than records at some point,
> with static guarantee of the contents. But currently, the thing we are
> missing is a fast map implementation which is functional, so we can
> avoid ETS tables for purposes they aren't designed for.
I understand that. But why must (a) this new fast map implementation have
an associated syntax, and (b) this syntax displace the syntax which *is*
needed to implement pattern-matching of frames?
If anything you can get an *even faster* map implementation if you ignore
the use case of extensible records.
That worried me when reading the draft too. I expect that the answer is to
define :
> '#[]'({my_type, V}, K) -> extract_mytype(K,V);
> '#[]'(Other, K) -> erlang_default:'#[]'(Other, K).
But this can become ugly and unwieldy quickly. Perhaps we need a standard
wraper function :
> '#[]'(K,V,[]) -> erlang:error(badarg).
> '#[]'(K,V,[M|T]) -> try M:'#[]'(K,V) catch error:badarg -> '#[]'(K,V,T).
That we can use in each module as :
> '#[]'(K,V) -> '#[]'(K,V,[mytype,json,maps,lists...]).
But you need to order your modules very carefully, puting the most selective
ones first.
Appart from that, while this eep looks interesting, it doesn't seem to address
pattern matching ? Or did I read incorrectly and you actually can
> case Json of
> Json#['class']#['foo'] -> handle_foo(Json);
> Json#['class']#['bar'] -> handle_bar(Json);
> end.
?
--
Vincent de Phily
_______________________________________________ eeps mailing list ee...@erlang.org http://erlang.org/mailman/listinfo/eeps