diff --git a/stdlib/docTable.as b/stdlib/docTable.as index a8234c01f98..f70e9182658 100644 --- a/stdlib/docTable.as +++ b/stdlib/docTable.as @@ -210,6 +210,23 @@ class DocTable( } }; + /** + `upsertInfo` + --------- + + Updates the document with the given id, or inserts a new document if nothing exists. Returns the old document if it was replaced. + + */ + upsertInfo(id:Id, info:Info) : ?Doc { + let doc = docOfInfo(info); + switch doc { + case null { null }; + case (?doc) { + updateDoc(id, doc) + } + } + }; + /** `rem` --------- diff --git a/stdlib/examples/produce-exchange/serverActor.as b/stdlib/examples/produce-exchange/serverActor.as index d36688ffe9d..eeafde7f29e 100644 --- a/stdlib/examples/produce-exchange/serverActor.as +++ b/stdlib/examples/produce-exchange/serverActor.as @@ -54,6 +54,7 @@ actor server = { */ registrarAddUser( + userId: UserId, short_name_: Text, description_: Text, region_: RegionId, @@ -63,51 +64,56 @@ actor server = { isTransporter: Bool ) : async ?UserId { - let prId = if isProducer { getModel().producerTable.addInfoGetId( - func(id_:ProducerId):ProducerInfo { + let producer_ = if isProducer { + getModel().producerTable.upsertInfo(userId, shared { - id=id_:ProducerId; + id=userId:ProducerId; short_name=short_name_; description=description_; region=region_; inventory=[]; reserved=[]; } - }) } else null; + ) + } else null; - let trId = if isTransporter { getModel().transporterTable.addInfoGetId( - func(id_:TransporterId):TransporterInfo { + let transporter_ = if isTransporter { + getModel().transporterTable.upsertInfo(userId, shared { - id=id_:TransporterId; + id=userId:TransporterId; short_name=short_name_; description=description_; routes=[]; reserved=[]; } - }) } else null; + ) + } else null; - let rrId = if isRetailer { getModel().retailerTable.addInfoGetId( - func(id_:RetailerId):RetailerInfo { + let retailer_ = if isRetailer { + getModel().retailerTable.upsertInfo(userId, shared { - id=id_; + id=userId; short_name=short_name_; description=description_; region=region_:RegionId } - }) } else null; + ) + } else null; - getModel().userTable.addInfoGetId( - func (id_: UserId) : UserInfo = - shared { - id = id_; - short_name = short_name_; - description = description_; - region = region_; - producerId = prId; - transporterId = trId; - retailerId = rrId; - isDeveloper = isDeveloper_; - }) + let user_ = getModel().userTable.upsertInfo(userId, + shared { + id = userId; + short_name = short_name_; + description = description_; + region = region_; + producerId = ?userId; + transporterId = ?userId; + retailerId = ?userId; + isDeveloper = isDeveloper_; + } + ); + + ?userId }; /** @@ -338,21 +344,22 @@ actor server = { */ registrarAddProducer( + producerId: ProducerId, short_name_: Text, description_: Text, region_: RegionId, ) : async ?ProducerId { - getModel().producerTable.addInfoGetId( - func(id_:ProducerId):ProducerInfo { - shared { - id=id_:ProducerId; - short_name=short_name_:Text; - description=description_:Text; - region=region_:RegionId; - inventory=[]; - reserved=[]; - } - }) + let producer_ = getModel().producerTable.upsertInfo(producerId, + shared { + id=producerId:ProducerId; + short_name=short_name_; + description=description_; + region=region_; + inventory=[]; + reserved=[]; + }); + + ?producerId }; /** @@ -405,19 +412,20 @@ actor server = { */ registrarAddRetailer( + retailerId: RetailerId, short_name_: Text, description_: Text, region_: RegionId, ) : async ?RetailerId { - getModel().retailerTable.addInfoGetId( - func(id_:RetailerId):RetailerInfo { - shared { - id=id_:RetailerId; - short_name=short_name_:Text; - description=description_:Text; - region=region_:RegionId - } - }) + let retailer_ = getModel().retailerTable.upsertInfo(retailerId, + shared { + id=retailerId:RetailerId; + short_name=short_name_:Text; + description=description_:Text; + region=region_:RegionId + }); + + ?retailerId }; /** @@ -466,20 +474,20 @@ actor server = { */ registrarAddTransporter( + transporterId: TransporterId, short_name_: Text, description_: Text, ) : async ?TransporterId { - getModel().transporterTable.addInfoGetId( - func(id_:TransporterId):TransporterInfo { - shared { - id=id_:TransporterId; - short_name=short_name_:Text; - description=description_:Text; - routes=[]; - reserved=[]; - } - }) - + let transporter_ = getModel().transporterTable.upsertInfo(transporterId, + shared { + id=transporterId:TransporterId; + short_name=short_name_:Text; + description=description_:Text; + routes=[]; + reserved=[]; + }); + + ?transporterId }; /** diff --git a/stdlib/examples/produce-exchange/serverModel.as b/stdlib/examples/produce-exchange/serverModel.as index 2e86bda2381..45d1c6b7237 100644 --- a/stdlib/examples/produce-exchange/serverModel.as +++ b/stdlib/examples/produce-exchange/serverModel.as @@ -47,16 +47,24 @@ class Model() { private idIsEq(x:Nat,y:Nat):Bool { x == y }; + private userIdIsEq(x:UserId,y:UserId):Bool { x == y }; + private idPairIsEq(x:(Nat,Nat),y:(Nat,Nat)):Bool { x.0 == y.0 and x.1 == y.1 }; private idHash(x:Nat):Hash { Hash.hashOfInt(x) }; + private userIdHash(x:UserId):Hash { Hash.hashOfText(x) }; + private idPairHash(x:(Nat,Nat)):Hash { Hash.hashOfIntAcc(Hash.hashOfInt(x.0), x.1) }; private keyOf(x:Nat):Key { new { key = x ; hash = idHash(x) } }; + private keyOfUserId(x:UserId):Key { + new { key = x ; hash = userIdHash(x) } + }; + private keyOfIdPair(x:Nat, y:Nat):Key<(Nat,Nat)> { new { key = (x,y) ; hash = idPairHash(x,y) } }; @@ -118,10 +126,10 @@ secondary maps. var userTable : UserTable = DocTable( - 0, - func(x:UserId):UserId{x+1}, + "", + func(x:UserId):UserId{x}, func(x:UserId,y:UserId):Bool{x==y}, - idHash, + userIdHash, func(doc:UserDoc):UserInfo = shared { id=doc.id; short_name=doc.short_name; @@ -229,10 +237,10 @@ secondary maps. var producerTable : ProducerTable = DocTable( - 0, - func(x:ProducerId):ProducerId{x+1}, + "", + func(x:ProducerId):ProducerId{x}, func(x:ProducerId,y:ProducerId):Bool{x==y}, - idHash, + userIdHash, func(doc:ProducerDoc):ProducerInfo = shared { id=doc.id; short_name=doc.short_name; @@ -313,10 +321,10 @@ secondary maps. var transporterTable : TransporterTable = DocTable ( - 0, - func(x:TransporterId):TransporterId{x+1}, + "", + func(x:TransporterId):TransporterId{x}, func(x:TransporterId,y:TransporterId):Bool{x==y}, - idHash, + userIdHash, func(doc:TransporterDoc):TransporterInfo = shared { id=doc.id; short_name=doc.short_name; @@ -341,10 +349,10 @@ secondary maps. var retailerTable : RetailerTable = DocTable( - 0, - func(x:RetailerId):RetailerId{x+1}, + "", + func(x:RetailerId):RetailerId{x}, func(x:RetailerId,y:RetailerId):Bool{x==y}, - idHash, + userIdHash, func(doc:RetailerDoc):RetailerInfo = shared { id=doc.id; short_name=doc.short_name; @@ -706,7 +714,7 @@ than the MVP goals, however. Map.insert2D( inventoryByRegion, keyOf(producer_.region.id), idIsEq, - keyOf(producer_.id), idIsEq, + keyOfUserId(producer_.id), userIdIsEq, updatedInventory, ); @@ -817,7 +825,7 @@ than the MVP goals, however. let (t, d) = Trie.remove3D( inventoryByRegion, keyOf(producer.region.id), idIsEq, - keyOf(producer.id), idIsEq, + keyOfUserId(producer.id), userIdIsEq, keyOf(id), idIsEq ); assertSome(d); @@ -951,7 +959,7 @@ than the MVP goals, however. `transporterAllRouteInfo` --------------------------- */ - transporterAllRouteInfo(id:RouteId) : ?[RouteInfo] { + transporterAllRouteInfo(id:TransporterId) : ?[RouteInfo] { let doc = switch (transporterTable.getDoc(id)) { case null { return null }; case (?doc) { doc }; @@ -1087,7 +1095,7 @@ than the MVP goals, however. retailerQueryCount += 1; debug "\nRetailer "; - debugInt id; + debug id; debug " sends `retailerQueryAll`\n"; debug "------------------------------------\n"; @@ -1136,7 +1144,7 @@ than the MVP goals, however. routes, /** - (To perform this Cartesian product, use a 1D inventory map:) */ Trie.mergeDisjoint2D( - inventory, idIsEq, idIsEq), + inventory, userIdIsEq, idIsEq), func (route_id:RouteId, route :RouteDoc, diff --git a/stdlib/examples/produce-exchange/serverTypes.as b/stdlib/examples/produce-exchange/serverTypes.as index 19a07c15dff..9f46dff0a9e 100644 --- a/stdlib/examples/produce-exchange/serverTypes.as +++ b/stdlib/examples/produce-exchange/serverTypes.as @@ -49,17 +49,18 @@ Externally, these Ids give a unique identifier that is unique to its type, but n Internally, each type of Id serves as a "row key" for a table (or two). */ -type UserId = Nat; +type Id = Text; +type UserId = Id; type RegionId = Nat; type TruckTypeId = Nat; type ProduceId = Nat; -type ProducerId = Nat; +type ProducerId = Id; type InventoryId = Nat; type ReservedInventoryId = Nat; -type RetailerId = Nat; -type TransporterId = Nat; +type RetailerId = Id; +type TransporterId = Id; type RouteId = Nat; type ReservedRouteId = Nat; diff --git a/stdlib/examples/produce-exchange/test/simpleSetupAndQuery.as b/stdlib/examples/produce-exchange/test/simpleSetupAndQuery.as index c0e6ecc4e36..429e9001340 100644 --- a/stdlib/examples/produce-exchange/test/simpleSetupAndQuery.as +++ b/stdlib/examples/produce-exchange/test/simpleSetupAndQuery.as @@ -64,29 +64,29 @@ actor class Test() = this { printEntityCount("Produce", (await s.getCounts()).produce_count); // populate with producers - let pra = await s.registrarAddProducer("pra", "", unwrap(rega) ); - let prb = await s.registrarAddProducer("prb", "", unwrap(rega) ); - let prc = await s.registrarAddProducer("prc", "", unwrap(regb) ); - let prd = await s.registrarAddProducer("prd", "", unwrap(rega) ); - let pre = await s.registrarAddProducer("pre", "", unwrap(regb) ); + let pra = await s.registrarAddProducer("pra", "Producer A", "", unwrap(rega) ); + let prb = await s.registrarAddProducer("prb", "Producer B", "", unwrap(rega) ); + let prc = await s.registrarAddProducer("prc", "Producer C", "", unwrap(regb) ); + let prd = await s.registrarAddProducer("prd", "Producer D", "", unwrap(rega) ); + let pre = await s.registrarAddProducer("pre", "Producer E", "", unwrap(regb) ); printEntityCount("Producer", (await s.getCounts()).producer_count); // populate with transporters - let tra = await s.registrarAddTransporter("tra", "" ); - let trb = await s.registrarAddTransporter("trb", "" ); - let trc = await s.registrarAddTransporter("trc", "" ); - let trd = await s.registrarAddTransporter("trd", "" ); - let tre = await s.registrarAddTransporter("tre", "" ); + let tra = await s.registrarAddTransporter("tra", "Transporter A", "" ); + let trb = await s.registrarAddTransporter("trb", "Transporter B", "" ); + let trc = await s.registrarAddTransporter("trc", "Transporter C", "" ); + let trd = await s.registrarAddTransporter("trd", "Transporter D", "" ); + let tre = await s.registrarAddTransporter("tre", "Transporter E", "" ); printEntityCount("Transporter", (await s.getCounts()).transporter_count); // populate with retailers - let rra = await s.registrarAddRetailer("rra", "", unwrap(regc) ); - let rrb = await s.registrarAddRetailer("rrb", "", unwrap(regd) ); - let rrc = await s.registrarAddRetailer("rrc", "", unwrap(rege) ); - let rrd = await s.registrarAddRetailer("rrd", "", unwrap(regc) ); - let rre = await s.registrarAddRetailer("rre", "", unwrap(rege) ); + let rra = await s.registrarAddRetailer("rra", "Retailer A", "", unwrap(regc) ); + let rrb = await s.registrarAddRetailer("rrb", "Retailer B", "", unwrap(regd) ); + let rrc = await s.registrarAddRetailer("rrc", "Retailer C", "", unwrap(rege) ); + let rrd = await s.registrarAddRetailer("rrd", "Retailer D", "", unwrap(regc) ); + let rre = await s.registrarAddRetailer("rre", "Retailer E", "", unwrap(rege) ); printEntityCount("Retailer", (await s.getCounts()).retailer_count); diff --git a/stdlib/hash.as b/stdlib/hash.as index 4814bb8a3f3..ad22bab7cc7 100644 --- a/stdlib/hash.as +++ b/stdlib/hash.as @@ -70,6 +70,14 @@ let BitVec = new { ha == hb }; + func hashOfText(t:Text) : BitVec { + var x = 0 : Word32; + for (c in t.chars()) { + x := x ^ charToWord32(c); + }; + return x + }; + func bitsPrintRev(bits:BitVec) { for (j in range(0, length() - 1)) { if (getHashBit(bits, j)) { @@ -89,7 +97,7 @@ let BitVec = new { } } }; - + func toList(v:BitVec) : BitList { func rec(pos:Nat) : BitList { if (pos >= length()) { null } @@ -171,12 +179,12 @@ let BitList = new { }; -/** +/** Canonical representations --------------------------- Choose a canonical representation of hash values for the rest of - the standard library to use: + the standard library to use: */ type Hash = BitVec;