use
در Gleam معمول است که «توابع مرتبهی بالاتر» بنویسیم و به کار ببریم؛ یعنی توابعی که توابع دیگر را بهعنوان آرگومان میگیرند. گاهی وقتی چند تابع مرتبهی بالاتر را همزمان به کار میبریم، کد خواندنش دشوار میشود، چون لایههای تودرتوی تورفتگی زیاد میشود.
برای مثال، اینجا تابعی را میبینیم که چندین تابع را فراخوانی میکند که مقدار Result(Int, Nil) برمیگردانند، و اگر هر چهار تابع موفق باشند، مقادیر را با هم جمع میکند.
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)
})
})
})
})
}
عبارتهای use در 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)
}
عبارت use همهی دستورهای بعدی بلوک را جمع میکند و آنها را به شکل یک تابع callback، بهعنوان آخرین آرگومان، به فراخوانی تابع میفرستد. متغیرهای میان کلیدواژهی use و نماد <- اسم آرگومانهایی هستند که به تابع callback فرستاده میشوند.
// 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
})
تابع callback میتواند هر تعداد آرگومان بگیرد، یا هیچ آرگومانی نگیرد.
use a, b, c, d <- call_4_function()
use <- call_0_function()
برای ساختن تابعی که بتوان آن را با یک عبارت use فراخوانی کرد، هیچ شرط خاصی وجود ندارد، جز اینکه یک تابع callback را بهعنوان آخرین آرگومان بگیرد.
pub fn call_twice(function: fn() -> t) -> #(t, t) {
let first = function()
let second = function()
#(first, second)
}
عبارتهای use در Gleam قابلیت بسیار قدرتمندی هستند که میتوان آنها را برای مسائل زیادی به کار برد، اما اگر بیش از حد استفاده شوند، میتوانند خواندن کد را دشوار کنند. بهطور کلی بهتر است از نحوهی نگارش معمول فراخوانی تابع استفاده کنید و فقط وقتی سراغ عبارتهای use بروید که خواندن کد را آسانتر میکنند.
دافنه روی سیستمی برای اجرا و ثبت نتایج آزمایشهایش کار میکرده است. بخشی از کد کمی طولانی و تکراری شده است، بنابراین از شما خواسته است چند عبارت use بنویسید تا به تمیزتر شدن آن کمک کنید.
with_retry
گاهی آزمایشها ممکن است به دلیل یک اشتباه یکباره شکست بخورند؛ بنابراین اگر آزمایشی شکست بخورد، دافنه میخواهد دوباره آن را امتحان کند تا ببیند بار دوم کار میکند یا نه.
تابع with_retry را تعریف کنید. این تابع، تابعی را به عنوان آرگومان میگیرد که نتیجه برمیگرداند.
اگر تابع مقدار Ok را برگرداند، with_retry باید همان مقدار را برگرداند.
اگر تابع مقدار Error را برگرداند، with_retry باید دوباره تابع را فراخوانی کند و نتیجهی آن فراخوانی را برگرداند.
دافنه از این تابع به این شکل استفاده میکند:
pub fn main() {
use <- with_retry
// Perform the experiment here
}
record_timing
دافنه مدت زمان اجرای هر آزمایش را با فراخوانی یک تابع ثبت زمان، قبل و بعد از هر آزمایش، ثبت میکند.
تابع record_timing را تعریف کنید که دو آرگومان میگیرد:
Nil را برمیگرداند.record_timing باید تابع ثبت زمان را فراخوانی کند، سپس تابع آزمایش را فراخوانی کند، بعد دوباره تابع ثبت زمان را فراخوانی کند و در پایان نتیجهی تابع آزمایش را برگرداند.
دافنه از این تابع به این شکل استفاده میکند:
pub fn main() {
use <- record_timing(time_logger)
// Perform the experiment here
}
run_experiment
آزمایشها از سه مرحله تشکیل میشوند: آمادهسازی، اقدام و ثبت. هر سه مرحله نتیجه برمیگردانند و هر مرحله برای اجرا به نتیجهی موفق مرحلهی قبلی نیاز دارد.
تابع run_experiment را تعریف کنید که چهار آرگومان میگیرد:
String.Ok تابع آمادهسازی را به عنوان آرگومان میگیرد و نتیجهای برمیگرداند.Ok توابع آمادهسازی و اقدام را به عنوان آرگومان میگیرد و نتیجهای برمیگرداند.اگر هر سه تابع موفق شوند، run_experiment باید Ok(#(experiment_name, recording_data)) را برگرداند.
اگر هر یک از توابع مقدار Error را برگرداند، run_experiment باید همان مقدار را برگرداند.
دافنه از این تابع به این شکل استفاده میکند:
pub fn main() {
use setup_data, action_data <- run_experiment("Test 1", setup, action)
// Record the results here
}