Hi,
Sorry for the basic question, but I can’t find it in the doc … if I have a data type that i need to be in a Set , is there (like for a template) a way to indicate what attributes compose a Key?
data MyElem = MyElem with
foo: Text
bar: Text
something: Int
somethingelse: Party
deriving (Eq, Show)
How can I have Set MyElem
Sets (DA.Next.Set
) are implemented as Maps. Set MyElem
is just Map MyElem ()
. DAML currently only has native maps with text keys (type TextMap
. So the Map
and Set
implementations in DA.Next.Set
and DA.Next.Map
require one to write serialization and deserialization code to Text
.
You can use show
as serialization, but then have to write parsing code, which is non-trivial.
This is clearly awkward so we have “Generic Maps” and thus also Generic Sets on our roadmap. You can already test Generic Maps as follows:
Set your project to build DAML-LF 1.dev (which will eventually become 1.9) by adding this to your daml.yaml
:
build-options:
- --target=1.dev
Then import DA.Map
in your project, add Ord
to the list of typeclasses derived by MyElem
and treat Map MyElem ()
as your set. The DA.Set
wrapper library doesn’t exist yet, but it would be an easy adaption from daml/Set.daml at a080b47e347277e099167fc59affde9dac313342 · digital-asset/daml · GitHub.
2 Likes
I feel the need to point out that any dar produced using --target=1.dev
is very much not supported for any kind of production use. Even just uploading it to a persistent ledger is likely to break things. This is really just meant for dev; please only upload such dars to throwaway ledgers.
While there is a point at which we will create a DAML LF 1.9
, it would certainly not be safe to assume 1.9
will be exactly what 1.dev
is today, nor a strict superset of it.
1 Like
So, are you saying that the ordering determines the keys used for hashing, in answer to OP? I expect I’ve misunderstood this, but if not, isn’t this a pretty hefty constraint? Is it not enough to have some sort of a Hashable
typeclass?
Would this mean that in OP’s example, you’d need to write an instance Ord
that then picks which particular field to order on - i.e. using deriving Ord
in this particular case wouldn’t work?
Btw. is Ord
a total order?
1 Like
I’m not in the loop on the specific work for the new DA.Map
, but I think I can offer some elements of an answer nonetheless.
First off, yes, the Ord
typeclass is intended to represent a total order. deriving Ord
, when it works, does that.
Regarding the requirement for Ord
, while one could argue it is not, strictly speaking, a requirement to store something in an associative data structure, and a Hashable
typeclass could do the trick if the underlying implementation were indeed a hash table, it’s also possible to implement maps (and sets) using binary trees based on ordering the given elements, as is the case for Data.Set in Haskell. Based on @bernhard’s stated requirement to add Ord
, I suspect the current prototype for DA.Map
is based on similar techniques.
2 Likes
@Jean_Safar
Something like this is what you’re looking for
import DA.Text
-- We need a separator character that is not in either of our text fields
-- nor a digit
-- nor a Party (https://github.com/digital-asset/daml/blob/ba74146c10d149687d7f42972a9ced6203c3f807/daml-lf/spec/daml-lf-1.rst#literals)
sep : Text
sep = ";"
instance M.MapKey MyElem where
keyToText c = c.foo
<> sep <> c.bar
<> sep <> M.keyToText c.something
<> sep <> M.keyToText c.somethingelse
keyFromText t = case splitOn sep t of
[ foo, bar, somethingT, somethingelseT] ->
MyElem with
foo
bar
something = M.keyFromText somethingT
somethingelse = M.keyFromText somethingelseT
_ -> error $ "Malformed key" <> t
2 Likes
Note that this ";
can never be used in a string ever" is likely to bite you in the future. If adding an Ord
instance does not work for you, and you really need to support translation to and from a text format, I’d recommend implementing a simple string-based replacement à la htmlencode: on the way in, replace ;
with &semicolon
and &
with &
, and do the opposite on the way out. Then you know there is no ;
in the encoded strings and you can safely use that as a delimiter.
2 Likes
Let me add some clarification around the upcoming generic map type:
Together with the generic Map type, DAML-LF has gained a builtin generic comparison function. This works for roughly anything that is serializable (not quite accurate but a decent approximation). For incomparable values, e.g., functions, this throws a runtime error.
For DAML, we want to prevent invalid values statically. Currently, this is accomplished by requiring an Ord
instance. However, the Ord
instance will not actually be used by the map. Instead it will use DAML-LF’s builtin notion of equality. Since nothing stops you from defining an Ord
instance for a record containing a function, you can technically still produce something that crashes.
Having two notions of ordering is rather confusing so there is an open question here if it would be better to forbid user-defined Ord
instances to make sure that Ord
instances always match up with the builtin notion of equality which is also much faster.