Giter Club home page Giter Club logo

fluid-let's Introduction

fluid-let

Build Status Rust Documentation Latest Version

fluid-let implements dynamically scoped variables.

Dynamic or fluid variables are a handy way to define global configuration values. They come from the Lisp family of languages where they are relatively popular for this use case.

Usage

Add this to your Cargo.toml:

[dependencies]
fluid-let = "1"

You can declare global dynamic variables using fluid_let! macro. Suppose you want to have a configurable Debug implementation for your hashes, controlling whether to print out the whole hash or a truncated version:

use fluid_let::fluid_let;

fluid_let!(pub static DEBUG_FULL_HASH: bool);

Enable full print out using the fluid_set! macro. Assignments to dynamic variables are effective for a certain dynamic scope. In this case, while the function is being executed:

use fluid_let::fluid_set;

fn some_important_function() {
    fluid_set!(DEBUG_FULL_HASH, &true);

    // Hashes will be printed out with full precision in this function
    // as well as in all functions that it calls.
}

And here is how you can implement Debug that uses dynamic configuration:

impl fmt::Debug for Hash {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let full = DEBUG_FULL_HASH.copied().unwrap_or(false);

        write!(f, "Hash(")?;
        if full {
            for byte in &self.value {
                write!(f, "{:02X}", byte)?;
            }
        } else {
            for byte in &self.value[..4] {
                write!(f, "{:02X}", byte)?;
            }
            write!(f, "...")?;
        }
        write!(f, ")")
    }
}

Here we print either the full value of the hash, or a truncated version, based on whether debugging mode has been enabled by the caller or not.

License

The code is licensed under MIT license (see LICENSE).

fluid-let's People

Contributors

ilammy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

fluid-let's Issues

Better API

It’s unfortunate that you have to use closures to get and set dynamic variables. It’s really unnatural. It would be awesome if it’s possible to provide RAII-style guard accessors:

let _guard = DEBUG_FULL_HASH.set(true);
// keeps value for the rest of the scope

A macro could be used to hide the unused local guard variable that’s necessary to keep the proper scoping:

fluid_set!(DEBUG_FULL_HASH, true);
// keeps value for the rest of the scope

(I hope...)

Another idea is to provide a copying get() for types that implement Copy, so that you can write the example as

 impl fmt::Debug for Hash {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let full = DEBUG_FULL_HASH.get(|current| current.unwrap_or(false));
+        let full = DEBUG_FULL_HASH.get().unwrap_or(false);
         // ...
     }
 }

No easier way to access value other than by creating a closure?

Interesting project! I think this idea could have great potential especially in the absence of default function parameters in Rust.

However, I find the way to retrieve (generic) dynamic values a bit too cumbersome:

    LOG_FILE.get(|current| {
        if let Some(mut log_file) = current {
            write!(log_file, "{}\n", msg)?;
        }
        Ok(())
    })

Having read the docs, I understand that this is dictated by the need to limit the lifetime of the retrieved value to shorter than the lifetime of the closure where it was set.

I am no guru of Rust lifetimes, but is there really no way to solve the above problem without introducing a new closure? It seems that it would be sufficient if the lifetime would be limited to that of the current code block.

Attribute macro

I think it would be cool to write

#[fluid]
pub static VARIABLE: Type = initial_value;

instead of somewhat awkward

fluid_let! {
    pub static VARIABLE: Type = initial_value;
}

This will have to be a separate (and probably optional) crate, because that's how procedural macros work.

Ideally, this should be a proper generic:

pub static VARIABLE_A: DynamicVariable<Type> = DynamicVariable::new(initial_value);
pub static VARIABLE_B: DynamicVariable<Type> = DynamicVariable::uninitialized();

But we need these statics to be thread-local, and thread_local! macro is the only way to declare thread-local variables. At least, that was the case two years ago when I started this thing. Maybe there is a different way now?

Add benchmarks

It’s really interesting how costly dynamic variables are in comparison with lexical ones.

First-class value support

As I remember, dynamic variables have this weird Option<&T> type and all this UnsafeCell magic inside because I wanted to be able to safely place references inside them.

However, this is kind of an overkill for value types, especially simple ones, like bool. How about putting the actual value inside the variable while still providing scoped get() accessor and scoped assignment guards?

In this way we could get away from using Option and generally simplify the API. The users will still be able to explicitly use Option if they would like it. But they could also use some definite value as the default (e.g., false).

I believe that it won’t be possible to put references to local variables or heap inside as they must ’static:

fluid_let(static MUH_SLICE: Option<&[u8]> = None);

let vec = vec![1, 2, 3];

fluid_set(MUH_SLICE, Some(&vec));

But it seems a safe thing to do so there might we a way.

Also, if you do it in a straightforward manner by moving the old value into the guard, this will result in unnecessary moves when setting and restoring the values. On the other side, if you keep a reference to the local variable then you don’t actually need to move anything, but that’s an additional indirection in getting the value.

Move to GitHub Actions

It’s not cool to see red builds for everything other that CircleCI. Now that GitHub provides an easy to use solution for CI, let’s use it to test for Linux, macOS, and Windows.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.