On Tue, Mar 15, 2011 at 03:30:50PM +0100, Torben Hoffmann wrote:
>
> Dictionary1 = ec_gb_trees:new().
> Dictionary2 = ec_dict:new().
>
> NewDictionary1 = Dictionary1:put(SomeKey, SomeValue).
> NewDictionary2 = Dictionary2:put(SomeKey, SomeValue).
> You have lost me a bit here... how would the Dictionary1 hold both a
> module name and a value?
Its a parameterized type. ec_gb_trees:new would return the
parameterized type. I guess I can't expect you to see whats in my head
lol, I forget about that sometimes.
So because of limitations in parameterized types. There are two
modules for each type. A non prameterized module used for construction
and a parameterized module that actually provides the
impementation. So ec_gb_trees would look like (off the top of my head,
uncompiled and un tested).
,----
| -module(ec_gb_trees).
|
| -export([new/0]).
|
| -spec new() -> ec_dictionary:dictionary().
| new() ->
| ec_gb_trees_impl:new(gb_trees:new()).
`----
then ec_gb_trees_impl would look something like
,----
| -module(ec_gb_trees_impl, [Data]).
|
| -behaviour(ec_dictionary).
|
| -export([get/1, put/2, ...]).
|
|
| .....
`----
> In this case it wouldn't matter what the concrete implementation of
> dictionary is, as long as it implemented the ec_dictionary interface.
>
> I have some utilities that check that a particular module implements
> an interface, but in general you just have to trust that that the
> caller is passing you the right value. Of course, thats pretty normal
> in erlang.
>
> Trust it at runtime but test the hell out of it and run dialyzer like
> crazy.
Exactly.
>
> Well, isn't that a matter of how many layers of indirection you want?
What do you mean?
> My approach adds the thinnest indirection layer by having everything in
> the same module.
> I like that sort of thing since it makes debugging extremely easy.
> Another bonus of coding it so directly as I did is that if you run xref on
> the module it will actually figure out what is going on! I have done some
> code where the module was a parameter and that puts a real stick in the
> wheel of xref.
I agree on all points. However, the inability for folks to extend it
in thier systems is just a killer for a generalized library. Using
this approach I haven't found a way around that.
> Another thing is dialyzer - I am not quite sure that it handles
> parameterized modules very well, so I would experiment with that before
> playing more with those things...
I am not sure either. I think the tools in general support it much
better then they used to. However, it is certainly worth testing to
see where dialyzer might fall down..
>
> The testing should not be all that affected by the chosen implementation
> so I am up for a bit of fun!
sweet!
>
> There is one major problem, I think, critical fault in this. That is you
> must know all the possible implementations at the time you are coding
> the are coding the interface. I think this is a killer for a set of
> types. I believe that client developers need the ability to implement
> the interface after the creation of the interface for it to be
> useful.
>
> My first implementation used a similar approach. Though it auto called
> the function in the module defined by base. So your size looked
> something like
>
> size(#my_map{base = Moule , content=C}) -> Module:size(C).
>
> It retained the ability to be extended at run time, but still required
> that a wrapper be defined to implement the interface. I was coding a
> wrapper for assoc lists as a test when I realized I had just
> reinvented parametrized modules.
>
> Hmmmm, I am not quite sure that I see the issue here - could you show me a
> bit of your code?
>
> My approach has - as you point out - the drawback that you have to extend
> it every time you need a new base module.
> This you have to weigh up against benefit of giving xref, dialyzer et al
> an easier job.
>
> Using your behaviour based solution one could let new return something
> like #my_map{} above and then let a generic wrapper module handle the
> operations on the data:
>
> Dict1 = ec_dict:new().
> Dict2= gen_dict:add(1,Dict1).
>
> or you could - and probably should - change the instantiation to:
>
> Dict1 = gen_dict:new(dict). %% pick implementation at this point.
> Dict2= gen_dict:add(1,Dict1).
Thats true, and doable. In this model I like that approach a lot. and
we would retain the tools compatabliity at the expense of a bit of
indirection.
> This might be a more Erlangish way of doing it since it uses a behaviour
> as restriction for the mapping modules and it wraps the indirection with a
> simple data structure. (It does make xref unhappy I guess, but dialyzer
> should be a happy trooper at this point.)
I don't think it would affect xref for the user. For the type
developer it would, but we would want to ensure good tests there
anyway. In this model the user matters more.
> This might be exactly the same as using parameterized modules, so it would
> require an experiment to judge what is the cleaner way of doing it.
>
> I would say that the solution that keeps most of the analysis tools happy
> is the one to go with. I have not used the tools with parameterized
> modules so I have no clue if it makes it a mess or not.
Hmm, then it could be that my original approach is best. I have some
decent examples of that approach already. Let me send them to the list
and get some feedback.
> Cheers,
> Torben
>
> > Cheers,
> > Torben
> >
> > On Tue, Mar 15, 2011 at 02:49, Eric Merritt
> <[2][3][email protected]>
> > wrote:
> >
> > >
> > > Isn't this just another implementation of a dictionary?
> > > Aren't there enough of those in stdlib?
> >
> > I guess I wasn't clear. I have no interest at all in implementing a
> > dict. We have half a dozen different key value style stores in various
> > libraries distributed with erlang. My goal is to provide a common
> > interface to those implementations. I have a ton of places I just care
> > to take a 'map' and don't care about the actual implementation. I want
> > to support that use case instead of specializing on gb_trees, or dict
> > or what have you.
> >
> > > How is this better?
> >
> > Its not better, simply a common interface to these types.
> > > At least use the same functions names as in the dict API for similar
> > > functions.
> > >
> > > Is the set() type in the specs the same as stdlib:set() or are You
> > > defining Your own.
> >
> > sets:set() in stdlib is a specific implementation of a set. I am not
> > planning on defining sets.
> > > I am not that happy with the name map. Since it seems to be the
> > > same thing as what is normally called dictionaries in Erlang I
> > > think it would be better to use that. Or relation since that is what
> > it
> > > is in a mathematical sense.
> >
> > That seems reasonable to me, I think dictionary is probably best
> > though its a bit more typing. Relation is more correct but I think its
> > probably less intuitive.
> > > Also map is, at least for me, to to closely coupled to the higher
> > order
> > > function. I suspect that before the week is over You are going to
> add
> > > the function map/2 and that will just look silly map:map(Fun,Map).
> >
> > Yes I agree in this case. That particular name collision bothered me
> > as well. This why its good to ask for comments ;)
> > > See more inline below.
> > >
> > > /Anders
> > >
> > >>
> > >> -opaque map(_KeyT, _ValueT) :: term().
> > >>
> > >> %% @doc empties the map.
> > >> -spec clear() -> map().
> > >>
> > >
> > > Shouldn't this be
> > > -spec new() -> map() instead?
> > > Or is this really a mutable datatype?
> > > In which case I suppose it should be
> > > -spec clear(map()) -> map()
> > > Or is it just me not understanding parameterized modules?
> >
> > Semantically its exactly the same. Its probably worth just having new,
> > and discarding clear sense it is less meaningful in a language without
> > mutation.
> > >> %% @doc returns a boolean indicating if this map has the specified
> > key
> > >> -spec has(term()) -> boolean().
> > >
> > > Should be has_key
> >
> > Probably to be clear what it actually does.
> > >
> > >>
> > >> %% @doc check to see if the specified value exists in the map
> > >> -spec has_value(Term()) -> boolean().
> > >>
> > >> %% @doc return the key value pairs as a set of {key, value} pairs
> > >> -spec as_set() -> set().
> > >>
> > >> %% @doc get the item from the may
> > >> -spec get(term()) -> term().
> > >>
> > > define types
> > > key() = term()
> > > value() = term()
> > > -spec get(key()) -> value().
> >
> > Ok, good idea.
> >
> > >> %% @doc return all keys in the map as a set
> > >> -spec keys(term()) -> set().
> > >
> > > What is the parameter here?
> >
> > lol, nothing. A fat finger on my part.
> > >>
> > >> %% @doc Add a value to the map
> > >> -spec put(term(), term()) -> map().
> > >
> > > -spec add(term(), term()) -> map().
> > > To match remove
> > >
> > >>
> > >> %% @doc remove a value from the map
> > >> -spec remove(term()) -> map().
> > >>
> > >> %% @doc get the current number of key value pairs in the map
> > >> -spec size() -> number().
> > >
> > > integer() instead of number()
> >
> > ok. also good recommendation. I will, post a new spec tomorrow along
> > with the interface and implementation.
> > >>
> > >> %% @doc get all the values in the map as a sequence
> > >> -spec values() -> sequence().
> > >>
> > >> As you can tell from the specs I am going to define sets and
> > sequences
> > >> very quickly as well.
> > >>
> > >> --
> > >> Eric Merritt
> > >> Erlang & OTP in Action (Manning) [3][4]http://manning.com/logan
> > >> [4][5]http://twitter.com/ericbmerritt
> > >> [5][6]http://erlware.org
> > >>
> > >> --
> > >> You received this message because you are subscribed to the Google
> > Groups "erlware-dev" group.
> > >> To post to this group, send email to
> [6][7][email protected].
> > >> To unsubscribe from this group, send email to
> > [7][8][email protected].
> > >> For more options, visit this group at
> > [8][9]http://groups.google.com/group/erlware-dev?hl=en.
> > >>
> > >>
> > >
> > > --
> > > You received this message because you are subscribed to the Google
> > Groups "erlware-dev" group.
> > > To post to this group, send email to
> [9][10][email protected].
> > > To unsubscribe from this group, send email to
> > [10][11][email protected].
> > > For more options, visit this group at
> > [11][12]http://groups.google.com/group/erlware-dev?hl=en.
> > >
> > >
> >
> > --
> > You received this message because you are subscribed to the Google
> > Groups "erlware-dev" group.
> > To post to this group, send email to
> [12][13][email protected].
> > To unsubscribe from this group, send email to
> > [13][14][email protected].
> > For more options, visit this group at
> > [14][15]http://groups.google.com/group/erlware-dev?hl=en.
> >
> > --
> > [15][16]http://www.linkedin.com/in/torbenhoffmann
> >
> > References
> >
> > Visible links
> > 1. [17]https://github.com/manopapad/proper
> > 2. mailto:[18][email protected]
> > 3. [19]http://manning.com/logan
> > 4. [20]http://twitter.com/ericbmerritt
> > 5. [21]http://erlware.org/
> > 6. mailto:[22][email protected]
> > 7. mailto:[23]erlware-dev%[email protected]
> > 8. [24]http://groups.google.com/group/erlware-dev?hl=en
> > 9. mailto:[25][email protected]
> > 10. mailto:[26]erlware-dev%[email protected]
> > 11. [27]http://groups.google.com/group/erlware-dev?hl=en
> > 12. mailto:[28][email protected]
> > 13. mailto:[29]erlware-dev%[email protected]
> > 14. [30]http://groups.google.com/group/erlware-dev?hl=en
> > 15. [31]http://www.linkedin.com/in/torbenhoffmann
> --
> Eric Merritt
> Erlang & OTP in Action (Manning) [32]http://manning.com/logan
> [33]http://twitter.com/ericbmerritt
> [34]http://erlware.org
>
> --
> [35]http://www.linkedin.com/in/torbenhoffmann
>
> References
>
> Visible links
> 1. mailto:[email protected]
> 2. https://github.com/manopapad/proper
> 3. mailto:[email protected]
> 4. http://manning.com/logan
> 5. http://twitter.com/ericbmerritt
> 6. http://erlware.org/
> 7. mailto:[email protected]
> 8. mailto:erlware-dev%[email protected]
> 9. http://groups.google.com/group/erlware-dev?hl=en
> 10. mailto:[email protected]
> 11. mailto:erlware-dev%[email protected]
> 12. http://groups.google.com/group/erlware-dev?hl=en
> 13. mailto:[email protected]
> 14. mailto:erlware-dev%[email protected]
> 15. http://groups.google.com/group/erlware-dev?hl=en
> 16. http://www.linkedin.com/in/torbenhoffmann
> 17. https://github.com/manopapad/proper
> 18. mailto:[email protected]
> 19. http://manning.com/logan
> 20. http://twitter.com/ericbmerritt
> 21. http://erlware.org/
> 22. mailto:[email protected]
> 23. mailto:erlware-dev%[email protected]
> 24. http://groups.google.com/group/erlware-dev?hl=en
> 25. mailto:[email protected]
> 26. mailto:erlware-dev%[email protected]
> 27. http://groups.google.com/group/erlware-dev?hl=en
> 28. mailto:[email protected]
> 29. mailto:erlware-dev%[email protected]
> 30. http://groups.google.com/group/erlware-dev?hl=en
> 31. http://www.linkedin.com/in/torbenhoffmann
> 32. http://manning.com/logan
> 33. http://twitter.com/ericbmerritt
> 34. http://erlware.org/
> 35. http://www.linkedin.com/in/torbenhoffmann
--
Eric Merritt
Erlang & OTP in Action (Manning) http://manning.com/logan
http://twitter.com/ericbmerritt
http://erlware.org
--
You received this message because you are subscribed to the Google Groups
"erlware-dev" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/erlware-dev?hl=en.