ServiceEnv and service_env — The Glue

service_key! handles the tag definition. ServiceEnv and service_env provide the glue for accessing a service from the environment inside an effect.

service_env: Access a Service

#![allow(unused)]
fn main() {
use id_effect::{service_env, ServiceEnv};

// Access a service and use it
fn get_user(id: u64) -> Effect<User, DbError, ServiceEnv<UserRepositoryTag>> {
    effect! {
        let repo = ~ service_env::<UserRepositoryTag>();
        ~ repo.get_user(id)
    }
}
}

service_env::<K>() returns an effect that, when run, extracts the Arc<dyn Trait> identified by K from the environment.

ServiceEnv<K> is a type alias for the R required by service_env — effectively Context<Cons<Tagged<K>, Nil>> but more readable.

The ~ Tag Shorthand

Inside effect!, you can use the tag directly with ~:

#![allow(unused)]
fn main() {
fn get_user(id: u64) -> Effect<User, DbError, impl NeedsUserRepository> {
    effect! {
        let repo: Arc<dyn UserRepository> = ~ UserRepositoryTag;
        ~ repo.get_user(id)
    }
}
}

~ UserRepositoryTag is syntactic sugar for ~ service_env::<UserRepositoryTag>(). Both work; the shorthand is more concise.

Multiple Services in One Effect

When a function needs several services:

#![allow(unused)]
fn main() {
fn notify_user(user_id: u64, message: &str)
-> Effect<(), AppError, impl NeedsUserRepository + NeedsEmailService>
{
    effect! {
        let repo  = ~ UserRepositoryTag;
        let email = ~ EmailServiceTag;
        
        let user = ~ repo.get_user(user_id).map_error(AppError::Db);
        ~ email.send(&user.email, message).map_error(AppError::Email);
        ()
    }
}
}

The impl NeedsX + NeedsY syntax is the idiomatic way to express multiple requirements. The compiler verifies that the provided environment satisfies both traits.

ServiceEnv vs Raw Context

ServiceEnv<K> is a convenience type. Under the hood it's just a Context with one element. The difference is ergonomic:

#![allow(unused)]
fn main() {
// With ServiceEnv
fn f() -> Effect<User, E, ServiceEnv<UserRepositoryTag>> { ... }

// With raw Context (equivalent, more verbose)
fn f() -> Effect<User, E, Context<Cons<Tagged<UserRepositoryTag>, Nil>>> { ... }
}

Use ServiceEnv for single-service effects. Use impl NeedsX for effects in library code where the concrete context type shouldn't leak into the signature. The Layers machinery accepts both at runtime.