diff --git a/TODO.md b/TODO.md index 6ff7ee3..618be6d 100644 --- a/TODO.md +++ b/TODO.md @@ -18,11 +18,10 @@ Values marked with `*` are deprioritised and may be delayed until after v1. - Unqualified imports - Opaque types -- Importing types in module interfaces +- Multiple `case` subjects - Case expression exhaustiveness checking* - String prefix patterns with a binding to the prefix* - Alternative patterns* -- Multiple `case` subjects - Clause guards* - Mutually recursive functions & types* diff --git a/birdie_snapshots/import_alias_to_avoid_conflict.accepted b/birdie_snapshots/import_alias_to_avoid_conflict.accepted index 239d74b..fd256e9 100644 --- a/birdie_snapshots/import_alias_to_avoid_conflict.accepted +++ b/birdie_snapshots/import_alias_to_avoid_conflict.accepted @@ -5,6 +5,5 @@ file: ./test/trick_test.gleam test_name: import_alias_to_avoid_conflict_test --- import wobble - import wibble/wobble as wibble diff --git a/birdie_snapshots/import_generic_type_in_interface_definition.accepted b/birdie_snapshots/import_generic_type_in_interface_definition.accepted new file mode 100644 index 0000000..8e38304 --- /dev/null +++ b/birdie_snapshots/import_generic_type_in_interface_definition.accepted @@ -0,0 +1,11 @@ +--- +version: 1.5.4 +title: import_generic_type_in_interface_definition +file: ./test/trick_test.gleam +test_name: import_generic_type_in_interface_definition_test +--- +import gleam/dynamic/decode as decoding +import gleam/dynamic + +pub const make_dynamic: fn(a) -> dynamic.Dynamic(b, c) = decoding.make_dynamic + diff --git a/birdie_snapshots/import_type_in_interface_definition.accepted b/birdie_snapshots/import_type_in_interface_definition.accepted new file mode 100644 index 0000000..92dd4a6 --- /dev/null +++ b/birdie_snapshots/import_type_in_interface_definition.accepted @@ -0,0 +1,11 @@ +--- +version: 1.5.4 +title: import_type_in_interface_definition +file: ./test/trick_test.gleam +test_name: import_type_in_interface_definition_test +--- +import gleam/dynamic/decode as decoding +import gleam/dynamic + +pub const make_dynamic: fn(a) -> dynamic.Dynamic = decoding.make_dynamic + diff --git a/src/trick.gleam b/src/trick.gleam index 6b18e4c..648f51e 100644 --- a/src/trick.gleam +++ b/src/trick.gleam @@ -4808,7 +4808,8 @@ pub opaque type ModuleName { ModuleName(name: String, interface: ModuleInterface) } -/// Import a particular module so it can be used. +/// Import a particular module so it can be used. For importing modules in module +/// interface definitions, see [`define_import`](#define_import). /// /// ### Examples /// @@ -4889,7 +4890,7 @@ pub fn import_( Ok(#( state, - Definition( + Import( doc.concat([ doc.from_string("import "), doc.from_string(module.name), @@ -7007,3 +7008,58 @@ fn do_check_private( |> result.try(fn(_) { do_check_private(state, value_name, return) }) } } + +/// Defines an import for a module interface, allowing types from the imported +/// module to be used in the module's definition. For importing types in a full +/// code-generated module, see [`import_`](#import_). +/// +/// ### Examples +/// +/// ```gleam +/// let assert Ok(option_module) = trick.define_module("gleam/option", ...) +/// +/// trick.define_module("gleam/dict", { +/// use option <- trick.define_import(option_module) +/// +/// use dict <- trick.define_custom_type("Dict") +/// use _ <- trick.define_type_parameter("key") +/// use _ <- trick.define_type_parameter("value") +/// use <- trick.define_constructors([]) +/// +/// let key = trick.generic("key") +/// let value = trick.generic("value") +/// let option_type = trick.imported_generic_type(option, "Option") +/// +/// trick.define_values([trick.FunctionInterface( +/// "upsert", +/// [ +/// trick.Field(None, trick.with_generics(dict, [key, value])), +/// trick.Field(None, key), +/// trick.Field(None, trick.Function( +/// [trick.with_generics(option_type, [value])], +/// value, +/// )), +/// ], +/// trick.with_generics(dict, [key, value]), +/// )]) +/// }) +/// ``` +/// +pub fn define_import( + module: ModuleInterface, + continue: fn(ModuleName) -> DefinedModule, +) -> DefinedModule { + use state <- DefinedModule + let imported_name = + module.name + |> string.split("/") + |> list.last + |> result.unwrap(module.name) + + use state <- check_import(state, imported_name) + + let name = ModuleName(name: imported_name, interface: module) + let state = + State(..state, type_info: dict.merge(module.type_info, state.type_info)) + continue(name).compile(state) +} diff --git a/test/trick_test.gleam b/test/trick_test.gleam index adc4069..09a97f0 100644 --- a/test/trick_test.gleam +++ b/test/trick_test.gleam @@ -4129,3 +4129,80 @@ pub fn import_alias_to_avoid_conflict_test() { |> unwrap |> birdie.snap("import_alias_to_avoid_conflict") } + +pub fn import_type_in_interface_definition_test() { + let assert Ok(dynamic) = + trick.define_module("gleam/dynamic", { + use _ <- trick.define_custom_type("Dynamic") + use <- trick.define_constructors([]) + trick.define_values([]) + }) + + let assert Ok(decode) = + trick.define_module("gleam/dynamic/decode", { + use dynamic <- trick.define_import(dynamic) + trick.define_values([ + trick.FunctionInterface( + name: "make_dynamic", + parameters: [trick.Field(None, trick.generic("a"))], + return_type: trick.imported_type(dynamic, "Dynamic"), + ), + ]) + }) + + { + use decode <- trick.import_(decode, Some("decoding")) + use _ <- trick.import_(dynamic, None) + use _ <- trick.constant( + "make_dynamic", + trick.Public, + trick.imported_value(decode, "make_dynamic"), + ) + trick.end_module() + } + |> trick.to_string + |> unwrap + |> birdie.snap("import_type_in_interface_definition") +} + +pub fn import_generic_type_in_interface_definition_test() { + let assert Ok(dynamic) = + trick.define_module("gleam/dynamic", { + use _ <- trick.define_custom_type("Dynamic") + use _ <- trick.define_type_parameter("one") + use _ <- trick.define_type_parameter("other") + use <- trick.define_constructors([]) + trick.define_values([]) + }) + + let assert Ok(decode) = + trick.define_module("gleam/dynamic/decode", { + use dynamic <- trick.define_import(dynamic) + trick.define_values([ + trick.FunctionInterface( + name: "make_dynamic", + parameters: [trick.Field(None, trick.generic("a"))], + return_type: dynamic + |> trick.imported_generic_type("Dynamic") + |> trick.with_generics([ + trick.generic("one"), + trick.generic("other"), + ]), + ), + ]) + }) + + { + use decode <- trick.import_(decode, Some("decoding")) + use _ <- trick.import_(dynamic, None) + use _ <- trick.constant( + "make_dynamic", + trick.Public, + trick.imported_value(decode, "make_dynamic"), + ) + trick.end_module() + } + |> trick.to_string + |> unwrap + |> birdie.snap("import_generic_type_in_interface_definition") +}