-module(glimp). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([switch/3, while/3, do_while/3, for/4]). -export_type([case_block/2, loop_state/1, transformed_case_block/2, switch_state/2]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " The intent of this module called `Glimp`, short for `Gleam Imperative` is\n" " to provide close alternatives to the traditional C features `while`,\n" " `do while`, `for` and `switch` but in a functional language. This will\n" " allow you to quickly port complex algorithms implemented in C or other\n" " imperative languages to Gleam. Of course, it would be best to reimplement\n" " these as functional algorithms. However, this is typically hard,\n" " error-prone and very time consuming. With this module, an initial\n" " `make-it-work` port becomes much easier. Once you get this initial port\n" " working, you can focus your energy on implementing this in a more functional way.\n" "\n" " For examples of how to use this library see glimp_test.gleam in the test\n" " folder of the project.\n" ). -type case_block(EZM, EZN) :: {case_block, EZM, fun((EZM) -> {EZN, boolean()})}. -type loop_state(EZO) :: {loop_state, EZO, boolean()}. -type transformed_case_block(EZP, EZQ) :: {transformed_case_block, EZP, fun((EZP) -> switch_state(EZP, EZQ))}. -type switch_state(EZR, EZS) :: {switch_state, EZR, gleam@option:option(EZS), boolean()}. -file("src/glimp.gleam", 97). -spec switch_helper( switch_state(FAA, FAB), list(transformed_case_block(FAA, FAB)), fun((FAA) -> switch_state(FAA, FAB)) ) -> switch_state(FAA, FAB). switch_helper(Switch_state, Transformed_cases, Transformed_default_function) -> Current_state = erlang:element(2, Switch_state), Break = erlang:element(4, Switch_state), case Break of true -> Switch_state; false -> case Transformed_cases of [] -> Transformed_default_function(Current_state); [First | Rest] -> Comparison = erlang:element(2, First) =:= Current_state, case Comparison of true -> New_state = (erlang:element(3, First))( Current_state ), switch_helper( New_state, Rest, Transformed_default_function ); false -> switch_helper( Switch_state, Rest, Transformed_default_function ) end end end. -file("src/glimp.gleam", 169). -spec while_helper( loop_state(FAN), fun((FAN) -> boolean()), fun((loop_state(FAN)) -> loop_state(FAN)) ) -> loop_state(FAN). while_helper(State, Pre_run_condition, Transformed_code_to_run) -> case erlang:element(3, State) of true -> State; false -> case Pre_run_condition(erlang:element(2, State)) of false -> State; true -> New_state = Transformed_code_to_run(State), while_helper( New_state, Pre_run_condition, Transformed_code_to_run ) end end. -file("src/glimp.gleam", 228). -spec do_while_helper( loop_state(FAU), fun((loop_state(FAU)) -> loop_state(FAU)), fun((FAU) -> boolean()) ) -> loop_state(FAU). do_while_helper(State, Transformed_code_to_run, Post_run_condition) -> New_state = Transformed_code_to_run(State), Extended_post_run_condition_check = Post_run_condition( erlang:element(2, New_state) ) andalso gleam@bool:negate(erlang:element(3, New_state)), case Extended_post_run_condition_check of false -> New_state; true -> do_while_helper( New_state, Transformed_code_to_run, Post_run_condition ) end. -file("src/glimp.gleam", 291). -spec for_helper( loop_state(FBC), fun((loop_state(FBC)) -> boolean()), fun((loop_state(FBC)) -> loop_state(FBC)), fun((loop_state(FBC)) -> loop_state(FBC)) ) -> loop_state(FBC). for_helper( State, Transformed_pre_run_condition, Transformed_code_to_run, Transformed_increment_code ) -> case Transformed_pre_run_condition(State) of false -> State; true -> Intermediate_state = Transformed_code_to_run(State), New_state = Transformed_increment_code(Intermediate_state), for_helper( New_state, Transformed_pre_run_condition, Transformed_code_to_run, Transformed_increment_code ) end. -file("src/glimp.gleam", 328). -spec transform_case(case_block(FBK, FBL)) -> transformed_case_block(FBK, FBL). transform_case(Case_block) -> Value = erlang:element(2, Case_block), Original_fn = erlang:element(3, Case_block), New_fn = fun(Val) -> Result = erlang:element(1, Original_fn(Val)), Flag = erlang:element(2, Original_fn(Val)), {switch_state, Value, {some, Result}, Flag} end, {transformed_case_block, Value, New_fn}. -file("src/glimp.gleam", 347). ?DOC( " Transforms the default function in the switch statement into a more complex\n" " structure for the switch_helper function.\n" ). -spec transform_default(fun((FBQ) -> FBR)) -> fun((FBQ) -> switch_state(FBQ, FBR)). transform_default(Original_fn) -> New_fn = fun(Val) -> Result = Original_fn(Val), {switch_state, Val, {some, Result}, true} end, New_fn. -file("src/glimp.gleam", 73). -spec switch(EZT, list(case_block(EZT, EZU)), fun((EZT) -> EZU)) -> {ok, EZU} | {error, nil}. switch(Expression, Cases, Default) -> Switch_state = {switch_state, Expression, none, false}, Transformed_cases = gleam@list:map(Cases, fun transform_case/1), Transformed_default_function = transform_default(Default), Result = erlang:element( 3, switch_helper( Switch_state, Transformed_cases, Transformed_default_function ) ), case Result of {some, Val} -> {ok, Val}; none -> {error, nil} end. -file("src/glimp.gleam", 358). -spec transform_code_to_run(fun((FBU) -> loop_state(FBU))) -> fun((loop_state(FBU)) -> loop_state(FBU)). transform_code_to_run(Original_fn) -> fun(State) -> case State of {loop_state, Value, Flag} -> Result = Original_fn(Value), case Result of {loop_state, New_value, New_flag} -> {loop_state, New_value, Flag orelse New_flag} end end end. -file("src/glimp.gleam", 149). ?DOC( "2. IMPLEMENTING WHILE WITH BREAK\n" " In C syntax, `while` is defined as:\n" "\n" " while (condition) {\n" " // code block to be executed\n" "}\n" "\n" " The `while` function in Gleam is stack-safe and checks the pre_run_condition\n" " based on the communicated state at the beginning of an iteration of the while loop.\n" " If the condition is true then the code in the code block handed to the `while`\n" " function is run and generates a new LoopState in preparation for the next iteration.\n" " The code in the code block can throw a Break just like in C.\n" ). -spec while(FAL, fun((FAL) -> boolean()), fun((FAL) -> loop_state(FAL))) -> FAL. while(State, Pre_run_condition, Code_to_run) -> Transformed_state = {loop_state, State, false}, Transformed_code_to_run = transform_code_to_run(Code_to_run), Result = while_helper( Transformed_state, Pre_run_condition, Transformed_code_to_run ), case Result of {loop_state, A, _} -> A end. -file("src/glimp.gleam", 209). ?DOC( "3. IMPLEMENTING DO WHILE WITH BREAK\n" " In C, the syntax for `do while` is:\n" "\n" " do {\n" " // code block to be executed\n" " }\n" " while (condition);\n" "\n" " This corresponding `do while` function in Gleam is stack-safe and checks the\n" " condition based on the communicated state at the end of an iteration of the\n" " while loop. It therefore runs the code in the `do while` function AT LEAST ONCE.\n" " If the condition is true then the new state generated by the code is handed to\n" " the next iteration of the `do while` loop and used with the next run of the code.\n" " The code in the code block can throw a Break just like in C. If the condition is\n" " false or break is true then the new state is returned and the loop stops.\n" " Note that ending the loop returns the new state. This may or may not be desired.\n" " The alternative is returning the old state. Let me know if there is a desire for\n" " that feature.\n" ). -spec do_while(FAS, fun((FAS) -> loop_state(FAS)), fun((FAS) -> boolean())) -> FAS. do_while(State, Code_to_run, Post_run_condition) -> Transformed_state = {loop_state, State, false}, Transformed_code_to_run = transform_code_to_run(Code_to_run), Result = do_while_helper( Transformed_state, Transformed_code_to_run, Post_run_condition ), case Result of {loop_state, A, _} -> A end. -file("src/glimp.gleam", 375). -spec transform_pre_run_condition(fun((FBY) -> boolean())) -> fun((loop_state(FBY)) -> boolean()). transform_pre_run_condition(Original_fn) -> fun(State) -> case State of {loop_state, Value, Flag} -> Original_fn(Value) andalso gleam@bool:negate(Flag) end end. -file("src/glimp.gleam", 264). ?DOC( "4. IMPLEMENTING FOR WITH BREAK\n" " In C, a `for`loop is defined as:\n" "\n" " for (initialization; condition; increment) {\n" " // code block to be executed\n" " }\n" " The initialization is executed (one time) before the execution of the code block.\n" " The condition defines the condition for executing the code block.\n" " And the increment code is executed (every time) after the code block has run.\n" "\n" " In Gleam, the `for` function checks the condition using the communicated state at the\n" " beginning of an iteration.\n" " If the condition is true then the code block handed to the `for` function is run.\n" " At the end of a run, a new state is generated that is used for the next\n" " iteration.\n" " If the condition is false or break is true then the code returns the final state and stops.\n" " Just as the while functions, this `for` function is stack-safe.\n" ). -spec for( FAZ, fun((FAZ) -> boolean()), fun((FAZ) -> loop_state(FAZ)), fun((FAZ) -> loop_state(FAZ)) ) -> FAZ. for(State, Pre_run_condition, Code_to_run, Post_run_code) -> Transformed_state = {loop_state, State, false}, Transformed_pre_run_condition = transform_pre_run_condition( Pre_run_condition ), Transformed_code_to_run = transform_code_to_run(Code_to_run), Transformed_increment_code = transform_code_to_run(Post_run_code), Result = for_helper( Transformed_state, Transformed_pre_run_condition, Transformed_code_to_run, Transformed_increment_code ), case Result of {loop_state, A, _} -> A end.