-
Notifications
You must be signed in to change notification settings - Fork 121
Standard library: Association lists #231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1252fbf
7d70057
19b1610
1503376
f349e36
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be tempted to abstract
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
| }; | ||
|
|
||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
|
||
|
|
@@ -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; | ||
|
|
@@ -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; | ||
|
|
@@ -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; | ||
|
|
@@ -165,7 +165,7 @@ actor ProduceExchange { | |
| start: Date, | ||
| end: Date, | ||
| cost: Price, | ||
| tt: TruckTypeId | ||
| tt: TruckKindId | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, thanks! |
||
| ) : async ?RouteId { | ||
| // xxx | ||
| null | ||
|
|
||
There was a problem hiding this comment.
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.There was a problem hiding this comment.
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.