From a43130fb5848fd54732c4ebf80b9b18e0b6cf6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Fernandes?= Date: Thu, 16 May 2024 11:05:28 -0300 Subject: [PATCH] [gleam] complete erlang-extraction --- gleam/erlang-extraction/.gitignore | 4 + gleam/erlang-extraction/HELP.md | 32 ++++++ gleam/erlang-extraction/HINTS.md | 21 ++++ gleam/erlang-extraction/README.md | 104 ++++++++++++++++++ gleam/erlang-extraction/gleam.toml | 12 ++ gleam/erlang-extraction/manifest.toml | 27 +++++ .../src/erlang_extraction.gleam | 18 +++ .../test/erlang_extraction_test.gleam | 54 +++++++++ 8 files changed, 272 insertions(+) create mode 100644 gleam/erlang-extraction/.gitignore create mode 100644 gleam/erlang-extraction/HELP.md create mode 100644 gleam/erlang-extraction/HINTS.md create mode 100644 gleam/erlang-extraction/README.md create mode 100644 gleam/erlang-extraction/gleam.toml create mode 100644 gleam/erlang-extraction/manifest.toml create mode 100644 gleam/erlang-extraction/src/erlang_extraction.gleam create mode 100644 gleam/erlang-extraction/test/erlang_extraction_test.gleam diff --git a/gleam/erlang-extraction/.gitignore b/gleam/erlang-extraction/.gitignore new file mode 100644 index 0000000..170cca9 --- /dev/null +++ b/gleam/erlang-extraction/.gitignore @@ -0,0 +1,4 @@ +*.beam +*.ez +build +erl_crash.dump diff --git a/gleam/erlang-extraction/HELP.md b/gleam/erlang-extraction/HELP.md new file mode 100644 index 0000000..68fe081 --- /dev/null +++ b/gleam/erlang-extraction/HELP.md @@ -0,0 +1,32 @@ +# Help + +## Running the tests + +To run the tests, run the command `gleam test` from within the exercise directory. + +## Submitting your solution + +You can submit your solution using the `exercism submit src/erlang_extraction.gleam` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Gleam track's documentation](https://exercism.org/docs/tracks/gleam) +- The [Gleam track's programming category on the forum](https://forum.exercism.org/c/programming/gleam) +- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +To get help if you're having trouble, you can use one of the following resources: + +- [gleam.run](https://gleam.run/documentation/) is the gleam official documentation. +- [Discord](https://discord.gg/Fm8Pwmy) is the discord channel. +- [StackOverflow](https://stackoverflow.com/questions/tagged/gleam) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions. \ No newline at end of file diff --git a/gleam/erlang-extraction/HINTS.md b/gleam/erlang-extraction/HINTS.md new file mode 100644 index 0000000..5db6787 --- /dev/null +++ b/gleam/erlang-extraction/HINTS.md @@ -0,0 +1,21 @@ +# Hints + +## 1. Define the `GbTree` external type + +- External types can be defined using the `pub type TypeName(a, b, c)` syntax. + +## 2. Define the `new_gb_tree` function + +- External functions can be defined using the `@external(erlang, "module", "function")` syntax. +- The function should use the `gb_trees:empty/0`. + +## 3. Define the `insert` function + +- The external function can be a private function that is called by the `insert` function in order to change the order of the arguments. +- The function should use the `gb_trees:insert/3`. + +## 4. Define the `delete` function + +- The external function can be a private function that is called by the `delete` function in order to change the order of the arguments. +- The function should use the `gb_trees:delete_any/2`. +- The `gb_trees:delete/2` function will crash if the key is not found, so it should not be used. \ No newline at end of file diff --git a/gleam/erlang-extraction/README.md b/gleam/erlang-extraction/README.md new file mode 100644 index 0000000..0433716 --- /dev/null +++ b/gleam/erlang-extraction/README.md @@ -0,0 +1,104 @@ +# Erlang Extraction + +Welcome to Erlang Extraction on Exercism's Gleam Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +## External Functions + +Gleam can run on the Erlang virtual machine (BEAM), or on JavaScript runtimes. There are many other languages that use these runtimes, and it is often useful to be able to call code written in these languages from Gleam. + +Gleam's _external functions_ feature permits functions in other languages to be imported into Gleam and called with no runtime overhead. + +If your Gleam project runs on the Erlang virtual machine and you wish to call the `reverse` function from the Erlang `lists` module you can do it by adding the `@external` attribute to a Gleam function head like this: + +```gleam +@external(erlang, "lists", "reverse") +pub fn reverse_list(x: List(a)) -> List(a) +``` + +This can then be called as a normal Gleam function: + +```gleam +let reversed = reverse_list([1, 2, 3]) +// -> [3, 2, 1] +``` + +If you attempt to compile this code for JavaScript runtimes it will fail with an error message as there is no implementation for JavaScript. Another implementation can be specified for JavaScript runtimes like this: + +```gleam +@external(erlang, "lists", "reverse") +@external(javascript, "./my_module.mjs", "reverse") +pub fn reverse_list(x: List(a)) -> List(a) +``` + +It is also possible to write a Gleam implementation that will be used when there is no external implementation for the current compile target: + +```gleam +@external(erlang, "lists", "reverse") +pub fn reverse_list(x: List(a)) -> List(a) { + tail_recursive_reverse(x, []) +} + +fn tail_recursive_reverse(list, reversed) { + case list { + [] -> reversed + [x, ..xs] -> tail_recursive_reverse(xs, [x, ..reversed]) + } +} +``` + +## External Types + +External types can be used to refer to data types defined in other languages, such as Erlang or JavaScript. + +To define an external function declare a type but do not provide any constructors. This can then be used in the same way as any other type. + +```gleam +pub type OrderedDictionary(element) +``` + +## Instructions + +Tadhg has found the perfect Erlang library to help with his project. Being an older Erlang library it is using the `gb_trees` module rather than the newer `maps` module for storing data. + +Help out Tadgh by creating external types and functions for working with `gb_trees` in Gleam. + +## 1. Define the `GbTree` external type + +The `GbTree` type should have two type parameters, a key type and a value type. It should have no constructors, making it an external type. + +## 2. Define the `new_gb_tree` function + +The `new_gb_tree` function should take no arguments and return an empty `GbTree`. + +It should use the `gb_trees:empty/0` function from the Erlang standard library. + +## 3. Define the `insert` function + +The `insert` function should take a `GbTree` and a key and value to insert into the tree. It should return a new `GbTree` with the key and value inserted. + +The function should take three arguments: +1. The `GbTree` to insert into. +2. The key to insert. +3. The value to insert. + +It should use the `gb_trees:insert/3` function from the Erlang standard library. + +## 4. Define the `delete` function + +The `delete` function should take a `GbTree` and a key to delete from the tree. It should return a new `GbTree` with the key and value deleted. + +The function should take two arguments: +1. The `GbTree` to delete from. +2. The key to delete. + +It should use the `gb_trees:delete_any/2` function from the Erlang standard library. + +## Source + +### Created by + +- @lpil \ No newline at end of file diff --git a/gleam/erlang-extraction/gleam.toml b/gleam/erlang-extraction/gleam.toml new file mode 100644 index 0000000..6ac2b17 --- /dev/null +++ b/gleam/erlang-extraction/gleam.toml @@ -0,0 +1,12 @@ +name = "erlang_extraction" +version = "0.1.0" + +[dependencies] +gleam_bitwise = "~> 1.2" +gleam_otp = "~> 0.7 or ~> 1.0" +gleam_stdlib = "~> 0.32 or ~> 1.0" +simplifile = "~> 1.0" +gleam_erlang = ">= 0.25.0 and < 1.0.0" + +[dev-dependencies] +exercism_test_runner = "~> 1.4" diff --git a/gleam/erlang-extraction/manifest.toml b/gleam/erlang-extraction/manifest.toml new file mode 100644 index 0000000..072155d --- /dev/null +++ b/gleam/erlang-extraction/manifest.toml @@ -0,0 +1,27 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "argv", version = "1.0.1", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "A6E9009E50BBE863EB37D963E4315398D41A3D87D0075480FC244125808F964A" }, + { name = "exercism_test_runner", version = "1.7.0", build_tools = ["gleam"], requirements = ["argv", "gap", "glance", "gleam_community_ansi", "gleam_erlang", "gleam_json", "gleam_stdlib", "simplifile"], otp_app = "exercism_test_runner", source = "hex", outer_checksum = "2FC1BADB19BEC2AE77BFD2D3A606A014C85412A7B874CAFC4BA8CF04B0B257CD" }, + { name = "gap", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib"], otp_app = "gap", source = "hex", outer_checksum = "2EE1B0A17E85CF73A0C1D29DA315A2699117A8F549C8E8D89FA8261BE41EDEB1" }, + { name = "glance", version = "0.8.2", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glexer"], otp_app = "glance", source = "hex", outer_checksum = "ACF09457E8B564AD7A0D823DAFDD326F58263C01ACB0D432A9BEFDEDD1DA8E73" }, + { name = "gleam_bitwise", version = "1.3.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_bitwise", source = "hex", outer_checksum = "B36E1D3188D7F594C7FD4F43D0D2CE17561DE896202017548578B16FE1FE9EFC" }, + { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, + { name = "gleam_community_colour", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "A49A5E3AE8B637A5ACBA80ECB9B1AFE89FD3D5351FF6410A42B84F666D40D7D5" }, + { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, + { name = "gleam_json", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "8B197DD5D578EA6AC2C0D4BDC634C71A5BCA8E7DB5F47091C263ECB411A60DF3" }, + { name = "gleam_otp", version = "0.10.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "0B04FE915ACECE539B317F9652CAADBBC0F000184D586AAAF2D94C100945D72B" }, + { name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" }, + { name = "glexer", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glexer", source = "hex", outer_checksum = "4484942A465482A0A100936E1E5F12314DB4B5AC0D87575A7B9E9062090B96BE" }, + { name = "simplifile", version = "1.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "EB9AA8E65E5C1E3E0FDCFC81BC363FD433CB122D7D062750FFDF24DE4AC40116" }, + { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, +] + +[requirements] +exercism_test_runner = { version = "~> 1.4" } +gleam_bitwise = { version = "~> 1.2" } +gleam_erlang = { version = ">= 0.25.0 and < 1.0.0"} +gleam_otp = { version = "~> 0.7 or ~> 1.0" } +gleam_stdlib = { version = "~> 0.32 or ~> 1.0" } +simplifile = { version = "~> 1.0" } diff --git a/gleam/erlang-extraction/src/erlang_extraction.gleam b/gleam/erlang-extraction/src/erlang_extraction.gleam new file mode 100644 index 0000000..d23d969 --- /dev/null +++ b/gleam/erlang-extraction/src/erlang_extraction.gleam @@ -0,0 +1,18 @@ +pub type GbTree(k, v) + +@external(erlang, "gb_trees", "empty") +pub fn new_gb_tree() -> GbTree(k, v) + +pub fn insert(tree: GbTree(k, v), key: k, value: v) -> GbTree(k, v) { + do_insert(key, value, tree) +} + +@external(erlang, "gb_trees", "insert") +fn do_insert(key: k, value: v, tree: GbTree(k, v)) -> GbTree(k, v) + +pub fn delete(tree: GbTree(k, v), key: k) -> GbTree(k, v) { + do_delete(key, tree) +} + +@external(erlang, "gb_trees", "delete_any") +fn do_delete(key: k, tree: GbTree(k, v)) -> GbTree(k, v) diff --git a/gleam/erlang-extraction/test/erlang_extraction_test.gleam b/gleam/erlang-extraction/test/erlang_extraction_test.gleam new file mode 100644 index 0000000..e013206 --- /dev/null +++ b/gleam/erlang-extraction/test/erlang_extraction_test.gleam @@ -0,0 +1,54 @@ +import erlang_extraction.{type GbTree} +import exercism/should +import exercism/test_runner + +pub fn main() { + test_runner.main() +} + +@external(erlang, "gb_trees", "to_list") +fn to_list(tree: GbTree(k, v)) -> List(#(k, v)) + +pub fn new_gb_tree_test() { + erlang_extraction.new_gb_tree() + |> to_list + |> should.equal([]) +} + +pub fn insert_int_string_test() { + erlang_extraction.new_gb_tree() + |> erlang_extraction.insert(1, "one") + |> erlang_extraction.insert(2, "two") + |> erlang_extraction.insert(3, "three") + |> to_list + |> should.equal([#(1, "one"), #(2, "two"), #(3, "three")]) +} + +pub fn insert_string_int_test() { + erlang_extraction.new_gb_tree() + |> erlang_extraction.insert("one", 1) + |> erlang_extraction.insert("two", 2) + |> erlang_extraction.insert("three", 3) + |> to_list + |> should.equal([#("one", 1), #("three", 3), #("two", 2)]) +} + +pub fn delete_test() { + erlang_extraction.new_gb_tree() + |> erlang_extraction.insert(1, "one") + |> erlang_extraction.insert(2, "two") + |> erlang_extraction.insert(3, "three") + |> erlang_extraction.delete(2) + |> to_list + |> should.equal([#(1, "one"), #(3, "three")]) +} + +pub fn delete_non_existing_test() { + erlang_extraction.new_gb_tree() + |> erlang_extraction.insert(1, "one") + |> erlang_extraction.insert(2, "two") + |> erlang_extraction.insert(3, "three") + |> erlang_extraction.delete(4) + |> to_list + |> should.equal([#(1, "one"), #(2, "two"), #(3, "three")]) +}