diff --git a/crates/lang/src/format.rs b/crates/lang/src/format.rs index eaf2fa2d..71e76d10 100644 --- a/crates/lang/src/format.rs +++ b/crates/lang/src/format.rs @@ -856,9 +856,17 @@ impl<'comments> Formatter<'comments> { match args { [arg] if is_breakable_expr(&arg.value) => self .expr(fun) - .append(if needs_curly { "{" } else { "(" }) + .append(if needs_curly { + break_(" {", " { ") + } else { + break_("(", "(") + }) .append(self.call_arg(arg, needs_curly)) - .append(if needs_curly { "}" } else { ")" }) + .append(if needs_curly { + break_("}", " }") + } else { + break_(")", ")") + }) .group(), _ => self diff --git a/crates/lang/src/tipo/environment.rs b/crates/lang/src/tipo/environment.rs index f9212e79..ca9b6055 100644 --- a/crates/lang/src/tipo/environment.rs +++ b/crates/lang/src/tipo/environment.rs @@ -1200,6 +1200,19 @@ impl<'a> Environment<'a> { Ok(()) } + (Type::Tuple { elems: elems1, .. }, Type::Tuple { elems: elems2, .. }) + if elems1.len() == elems2.len() => + { + for (a, b) in elems1.iter().zip(elems2) { + unify_enclosed_type( + t1.clone(), + t2.clone(), + self.unify(a.clone(), b.clone(), location), + )?; + } + Ok(()) + } + ( Type::Fn { args: args1, diff --git a/examples/aiken_std/lib/aiken/context.ak b/examples/aiken_std/lib/aiken/context.ak index 54d7b12a..e9a2a4d9 100644 --- a/examples/aiken_std/lib/aiken/context.ak +++ b/examples/aiken_std/lib/aiken/context.ak @@ -1,3 +1,5 @@ +use aiken/map.{Map} + pub type ScriptContext(purpose) { transaction: Transaction, purpose: purpose, @@ -39,11 +41,11 @@ pub type Transaction { fee: Value, mint: Value, certificates: List(Certificate), - withdrawals: List(Pair(StakeCredential, Int)), + withdrawals: Map(StakeCredential, Int), validity_range: Interval(Int), extra_signatories: List(PublicKeyHash), - redeemers: List(Pair(ScriptPurpose, Redeemer)), - datums: List(Pair(Hash(Data), Data)), + redeemers: Map(ScriptPurpose, Redeemer), + datums: Map(Hash(Data), Data), id: TransactionId, } @@ -107,11 +109,8 @@ pub type DatumOption { pub type AssetName = ByteArray -pub type Pair(a, b) = - Nil - pub type Value = - List(Pair(PolicyId, List(Pair(AssetName, Int)))) + Map(PolicyId, Map(AssetName, Int)) pub type Certificate { CredentialRegistration { delegator: StakeCredential } diff --git a/examples/aiken_std/lib/aiken/map.ak b/examples/aiken_std/lib/aiken/map.ak new file mode 100644 index 00000000..3aa41101 --- /dev/null +++ b/examples/aiken_std/lib/aiken/map.ak @@ -0,0 +1,82 @@ +pub opaque type Map(key, value) { + inner: List(#(key, value)), +} + +/// Create a new map +pub fn new() { + Map { inner: [] } +} + +/// Get the inner list holding the map data +pub fn to_list(m: Map(key, value)) -> List(#(key, value)) { + m.inner +} + +/// Get a value in the map by a key +/// +/// ```aiken +/// use aiken/map +/// +/// let info = map.new() |> map.insert(key: "name", value: "Aiken") +/// +/// asset Some(x) = map.get(in: info, by: "key") +/// ``` +pub fn get(in m: Map(key, value), by k: key) -> Option(value) { + do_get(m.inner, k) +} + +fn do_get(elems: List(#(key, value)), k: key) -> Option(value) { + when elems is { + [] -> None + [#(first, second), ..xs] -> + if first == k { + Some(second) + } else { + do_get(xs, k) + } + } +} + +/// Insert a value in the map by a key +/// +/// ```aiken +/// use aiken/map +/// +/// map.new() |> map.insert(key: "name", value: "Aiken") +/// ``` +pub fn insert( + in m: Map(key, value), + key k: key, + value v: value, +) -> Map(key, value) { + if contains(m, k) { + m + } else { + Map { inner: [#(k, v), ..m.inner] } + } +} + +/// Check if a key exists in the map +/// +/// ```aiken +/// use aiken/map +/// +/// let info = map.new() |> map.insert(key: "name", value: "Aiken") +/// +/// asset Some(x) = map.contains(in: info, key: "key") +/// ``` +pub fn contains(in m: Map(key, value), key k: key) -> Bool { + do_contains(m.inner, k) +} + +fn do_contains(elems: List(#(key, value)), k: key) -> Bool { + when elems is { + [] -> False + [#(first, _), ..xs] -> + if first == k { + True + } else { + do_contains(xs, k) + } + } +}