Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions stdlib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ NO_COLOR="\x1b[0m"
MODULES=\
List \
ListTest \
AssocList \
Trie \
Set \
SetDb \
Expand Down Expand Up @@ -54,31 +55,37 @@ $(OUTDIR)/ListTest.out: $(OUTDIR) list.as listTest.as
$(ASC) -r $(filter-out $(OUTDIR), $^) > $@
@echo $(DONE)

$(OUTDIR)/Trie.out: $(OUTDIR) list.as trie.as
$(OUTDIR)/AssocList.out: $(OUTDIR) list.as assocList.as
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$(OUTDIR) should be added as order-only prerequisite. For other targets too.

Suggested change
$(OUTDIR)/AssocList.out: $(OUTDIR) list.as assocList.as
$(OUTDIR)/AssocList.out: list.as assocList.as | $(OUTDIR)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, good point.

I fix this once @nomeata and I settle on the other changes to the Makefile; he has another PR open for the strings defined above.

@echo $(MODULE_NAME) $(basename $(notdir $@))
@echo $(BEGIN)
$(ASC) -r $(filter-out $(OUTDIR), $^) > $@
@echo $(DONE)

$(OUTDIR)/Set.out: $(OUTDIR) list.as trie.as set.as
$(OUTDIR)/Trie.out: $(OUTDIR) list.as assocList.as trie.as
@echo $(MODULE_NAME) $(basename $(notdir $@))
@echo $(BEGIN)
$(ASC) -r $(filter-out $(OUTDIR), $^) > $@
@echo $(DONE)

$(OUTDIR)/SetDb.out: $(OUTDIR) list.as trie.as set.as setDb.as
$(OUTDIR)/Set.out: $(OUTDIR) list.as assocList.as trie.as set.as
@echo $(MODULE_NAME) $(basename $(notdir $@))
@echo $(BEGIN)
$(ASC) -r $(filter-out $(OUTDIR), $^) > $@
@echo $(DONE)

$(OUTDIR)/SetDbTest.out: $(OUTDIR) list.as trie.as set.as setDb.as setDbTest.as
$(OUTDIR)/SetDb.out: $(OUTDIR) list.as assocList.as trie.as set.as setDb.as
@echo $(MODULE_NAME) $(basename $(notdir $@))
@echo $(BEGIN)
$(ASC) -r $(filter-out $(OUTDIR), $^) > $@
@echo $(DONE)

$(OUTDIR)/ProduceExchange.out: $(OUTDIR) list.as trie.as examples/produceExchange.as
$(OUTDIR)/SetDbTest.out: $(OUTDIR) list.as assocList.as trie.as set.as setDb.as setDbTest.as
@echo $(MODULE_NAME) $(basename $(notdir $@))
@echo $(BEGIN)
$(ASC) -r $(filter-out $(OUTDIR), $^) > $@
@echo $(DONE)

$(OUTDIR)/ProduceExchange.out: $(OUTDIR) list.as assocList.as trie.as examples/produceExchange.as
@echo $(MODULE_NAME) $(basename $(notdir $@))
@echo $(BEGIN)
$(ASC) -r $(filter-out $(OUTDIR), $^) > $@
Expand Down
168 changes: 168 additions & 0 deletions stdlib/assocList.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* Association Lists, a la functional programming, in ActorScript.
*/


// polymorphic association linked lists between keys and values
type AssocList<K,V> = List<(K,V)>;

let AssocList = new {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be tempted to abstract AssocList on k_eq... (which is equally unsafe but maybe more convenient). But it's fine as stands. I guess this is a case where we'd like something like generative functors or just existentials.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that generative functors (or existentials) would be really nice here. It's not too painful to pass around these equality functions, but if there were a bunch more operations in the mix, it might get unwieldy after a while, or require some explicit structures, or both.


// find the value associated with a given key, or null if absent.
func find<K,V>(al : AssocList<K,V>,
k:K,
k_eq:(K,K)->Bool)
: ?V
{
func rec(al:AssocList<K,V>) : ?V {
switch (al) {
case (null) null;
case (?((hd_k, hd_v), tl)) {
if (k_eq(k, hd_k)) {
?hd_v
} else {
rec(tl)
}
};
}};
rec(al)
};

// replace the value associated with a given key, or add it, if missing.
// returns old value, or null, if no prior value existed.
func replace<K,V>(al : AssocList<K,V>,
k:K,
k_eq:(K,K)->Bool,
ov: ?V)
: (AssocList<K,V>, ?V)
{
func rec(al:AssocList<K,V>) : (AssocList<K,V>, ?V) {
switch (al) {
case (null) {
switch ov {
case (null) (null, null);
case (?v) (?((k, v), null), null);
}
};
case (?((hd_k, hd_v), tl)) {
if (k_eq(k, hd_k)) {
// if value is null, remove the key; otherwise, replace key's old value
// return old value
switch ov {
case (null) (tl, ?hd_v);
case (?v) (?((hd_k, v), tl), ?hd_v);
}
} else {
let (tl2, old_v) = rec(tl);
(?((hd_k, hd_v), tl2), old_v)
}
};
}};
rec(al)
};

// The key-value pairs of the final list consist of those pairs of
// the left list whose keys are not present in the right list; the
// values of the right list are irrelevant.
func diff<K,V,W>(al1: AssocList<K,V>,
al2: AssocList<K,W>,
keq: (K,K)->Bool)
: AssocList<K,V>
{
func rec(al1:AssocList<K,V>) : AssocList<K,V> = {
switch al1 {
case (null) null;
case (?((k, v1), tl)) {
switch (find<K,W>(al2, k, keq)) {
case (null) { rec(tl)};
case (?v2) { ?((k, v1), rec(tl)) };
}
};
}
};
rec(al1)
};

// This operation generalizes the notion of "set union" to finite maps.
// Produces a "disjunctive image" of the two lists, where the values of
// matching keys are combined with the given binary operator.
//
// For unmatched key-value pairs, the operator is still applied to
// create the value in the image. To accomodate these various
// situations, the operator accepts optional values, but is never
// applied to (null, null).
//
func disj<K,V,W,X>(al1:AssocList<K,V>,
al2:AssocList<K,W>,
keq:(K,K)->Bool,
vbin:(?V,?W)->X)
: AssocList<K,X>
{
func rec1(al1:AssocList<K,V>) : AssocList<K,X> = {
switch al1 {
case (null) {
func rec2(al2:AssocList<K,W>) : AssocList<K,X> = {
switch al2 {
case (null) null;
case (?((k, v2), tl)) {
switch (find<K,V>(al1, k, keq)) {
case (null) { ?((k, vbin(null, ?v2)), rec2(tl)) };
case (?v1) { ?((k, vbin(?v1, ?v2)), rec2(tl)) };
}
};
}
};
rec2(al2)
};
case (?((k, v1), tl)) {
switch (find<K,W>(al2, k, keq)) {
case (null) { ?((k, vbin(?v1, null)), rec1(tl)) };
case (?v2) { /* handled above */ rec1(tl) };
}
};
}
};
rec1(al1)
};

// This operation generalizes the notion of "set intersection" to
// finite maps. Produces a "conjuctive image" of the two lists, where
// the values of matching keys are combined with the given binary
// operator, and unmatched key-value pairs are not present in the output.
//
func conj<K,V,W,X>(al1 : AssocList<K,V>,
al2:AssocList<K,W>,
keq:(K,K)->Bool,
vbin:(V,W)->X)
: AssocList<K,X>
{
func rec(al1:AssocList<K,V>) : AssocList<K,X> = {
switch al1 {
case (null) { null };
case (?((k, v1), tl)) {
switch (find<K,W>(al2, k, keq)) {
case (null) { rec(tl) };
case (?v2) { ?((k, vbin(v1, v2)), rec(tl)) };
}
};
}
};
rec(al1)
};


func fold<K,V,X>(al:AssocList<K,V>,
nil:X,
cons:(K,V,X)->X)
: X
{
func rec(al:List<(K,V)>) : X = {
switch al {
case null nil;
case (?((k,v),t)) { cons(k, v, rec(t)) };
}
};
rec(al)
};

};
10 changes: 5 additions & 5 deletions stdlib/examples/produceExchange.as
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ type Price = Nat;
type Unit = Nat; // xxx replace with a variant type
type Grade = Nat; // xxx replace with a variant type

type TruckType = Nat; // ??? replace with a variant type
type TruckKind = Nat; // ??? replace with a variant type

type TruckCapacity = Weight;

Expand All @@ -81,7 +81,7 @@ type RegionId = Nat; // xxx variant type?
type ProduceId = Nat;
type ProducerId = Nat;
type RetailerId = Nat;
type TruckTypeId = Nat;
type TruckKindId = Nat;
type InventoryId = Nat;
type TransporterId = Nat;
type RouteId = Nat;
Expand All @@ -98,7 +98,7 @@ type OrderInfo = shared {
quant: Quantity;
ppu: PricePerUnit;
transporter: TransporterId;
truck_type: TruckTypeId;
truck_kind: TruckKindId;
weight: Weight;
region_begin:RegionId;
region_end: RegionId;
Expand All @@ -115,7 +115,7 @@ type QueryAllResult = shared {
quant: Quantity;
ppu: PricePerUnit;
transporter: TransporterId;
truck_type: TruckTypeId;
truck_kind: TruckKindId;
weight: Weight;
region_begin:RegionId;
region_end: RegionId;
Expand Down Expand Up @@ -165,7 +165,7 @@ actor ProduceExchange {
start: Date,
end: Date,
cost: Price,
tt: TruckTypeId
tt: TruckKindId
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tk?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, thanks!

) : async ?RouteId {
// xxx
null
Expand Down
Loading