Skip to content

Commit

Permalink
[gleam] complete expert-experiments
Browse files Browse the repository at this point in the history
  • Loading branch information
joaofnds committed May 13, 2024
1 parent df96294 commit 5438324
Show file tree
Hide file tree
Showing 8 changed files with 413 additions and 0 deletions.
4 changes: 4 additions & 0 deletions gleam/expert-experiments/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.beam
*.ez
build
erl_crash.dump
32 changes: 32 additions & 0 deletions gleam/expert-experiments/HELP.md
Original file line number Diff line number Diff line change
@@ -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/expert_experiments.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.
15 changes: 15 additions & 0 deletions gleam/expert-experiments/HINTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Hints

## 1. Define the `with_retry` function

- A `case` expression can be used to pattern match on a result.

## 2. Define the `record_timing` function

- The `time_logger` function should be called even if the `experiment` function returns an `Error` value.

## 3. Define the `run_experiment` function

- The [`result.try` function][result-try] can be used in a `use` expression to stop if a result is an `Error` value.

[result-try]: https://hexdocs.pm/gleam_stdlib/gleam/result.html#try
149 changes: 149 additions & 0 deletions gleam/expert-experiments/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Expert Experiments

Welcome to Expert Experiments 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

## Use Expressions

In Gleam it is common to write and use higher order functions, that is functions that take other functions as arguments. Sometimes when using many higher order functions at once the code can become difficult to read, with many layers of indentation.

For example, here is a function that calls several functions that return `Result(Int, Nil)`, and sums the values if all four are successful.

```gleam
import gleam/result
pub fn main() -> Result(Int, Nil) {
result.try(function1(), fn(a) {
result.try(function2(), fn(b) {
result.try(function3(), fn(c) {
result.try(function4(), fn(d) {
Ok(a + b + c + d)
})
})
})
})
}
```

Gleam's `use` expressions allow us to write this code without the indentation, often making it easier to read.

```gleam
import gleam/result
pub fn main() -> Result(Int, Nil) {
use a <- result.try(function1())
use b <- result.try(function2())
use c <- result.try(function3())
use d <- result.try(function4())
Ok(a + b + c + d)
}
```

A `use` expression collects all the following statements in the block and passes it as a callback function as the final argument to the function call. The variables between the `use` keyword and the `<-` symbol are the names of the arguments that will be passed to the callback function.

```gleam
// This use expression
use a <- function(1, 2)
io.println("Hello!")
a
// Is equivalent to this normal function call
function(1, 2, fn(a) {
io.println("Hello!")
a
})
```

The callback function can take any number of arguments, or none at all.

```gleam
use a, b, c, d <- call_4_function()
use <- call_0_function()
```

There are no special requirements to create a function that can be called with a `use` expression, other than taking a callback function as the final argument.

```gleam
pub fn call_twice(function: fn() -> t) -> #(t, t) {
let first = function()
let second = function()
#(first, second)
}
```

Gleam's `use` expressions are a very powerful feature that can be applied to lots of problems, but when overused they can make code difficult to read. It is generally preferred to use the normal function call syntax and only reach for `use` expressions when they make the code easier to read.

## Instructions

Daphne has been working on a system to run and record the results of her experiments. Some of the code has become a bit verbose and repetitive, so she's asked you to write some `use` expressions to help clean it up.

## 1. Define the `with_retry` function

Sometimes experiments can fail due to a one-off mistake, so if an experiment fails Daphne wants to retry it again to see if it works the second time.

Define the `with_retry` function that takes a result returning function as an argument.

If the function returns an `Ok` value then `with_retry` should return that value.

If the function returns an `Error` value then `with_retry` should call the function again and return the result of that call.

Daphne will use the function like this:

```gleam
pub fn main() {
use <- with_retry
// Perform the experiment here
}
```

## 2. Define the `record_timing` function

Daphne records how long each experiment takes to run by calling a time logging function before and after each experiment.

Define the `record_timing` function that takes two arguments:
- A time logging function which takes no arguments and returns `Nil`.
- An experiment function which takes no arguments and returns a result.

`record_timing` should call the time logging function, then call the experiment function, then call the time logging function again, and finally return the result of the experiment function.

Daphne will use the function like this:

```gleam
pub fn main() {
use <- record_timing(time_logger)
// Perform the experiment here
}
```

## 3. Define the `run_experiment` function

Experiments are made up of three phases. The setup, the action, and the recording. All three phases return results, and each phase needs the successful result of the previous phase to run.

Define the `run_experiment` function that takes four arguments:
- The name of the experiment as a `String`.
- A setup function which takes no arguments and returns a result.
- An action function which takes the `Ok` value of the setup function as an argument and returns a result.
- A recording function which takes the `Ok` value of the setup and action functions as arguments and returns a result.

If all three functions succeed then `run_experiment` should return `Ok(#(experiment_name, recording_data))`.

If any of the functions return an `Error` value then `run_experiment` should return that value.

Daphne will use the function like this:

```gleam
pub fn main() {
use setup_data, action_data <- run_experiment("Test 1", setup, action)
// Record the results here
}
```

## Source

### Created by

- @lpil
12 changes: 12 additions & 0 deletions gleam/expert-experiments/gleam.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name = "expert_experiments"
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"
27 changes: 27 additions & 0 deletions gleam/expert-experiments/manifest.toml
Original file line number Diff line number Diff line change
@@ -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" }
31 changes: 31 additions & 0 deletions gleam/expert-experiments/src/expert_experiments.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import gleam/result

pub fn with_retry(experiment: fn() -> Result(t, e)) -> Result(t, e) {
case experiment() {
Ok(value) -> Ok(value)
Error(_) -> experiment()
}
}

pub fn record_timing(
time_logger: fn() -> Nil,
experiment: fn() -> Result(t, e),
) -> Result(t, e) {
time_logger()
let result = experiment()
time_logger()
result
}

pub fn run_experiment(
name: String,
setup: fn() -> Result(t, e),
action: fn(t) -> Result(u, e),
record: fn(t, u) -> Result(v, e),
) -> Result(#(String, v), e) {
use setup_data <- result.try(setup())
use action_data <- result.try(action(setup_data))
use recording <- result.try(record(setup_data, action_data))

Ok(#(name, recording))
}
Loading

0 comments on commit 5438324

Please sign in to comment.