question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

What exactly do you use Reader<T> for?

See original GitHub issue

I’ve been going through some of the old issues, as there are some gems hidden away there. Along the way, I’ve seen a couple of answers by @louthy where he’s used the Reader<T> monad, apparently to carry state/environment around. See this one and this one for examples.

However, I can’t get a clear picture of exactly what this monad gives us, and how we use it.

Can anyone give me a simple explanation, preferably with code, showing a situation in which Reader<T> provides a specific benefit. This would be really useful.

Thanks in advance.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:13 (13 by maintainers)

github_iconTop GitHub Comments

2reactions
michael-wolfendencommented, Feb 12, 2019

The Reader Monad is commonly used to thread configuration through application code to call sites that depend on it. It offers a form of dependency injection.

Imagine you have a UserRepository used by a number of methods. In typical c# code, you either need to pass the UserRepository to each method or use an IoC container to inject the dependency wherever it’s needed.

The reader monad allows you to compose methods that require a UserRepository (or any dependency) without explicitly passing this dependency around.

For example

public class User
{
	public User(int id, string name)
	{
		Id = id;
		Name = name;
	}

	public int Id { get; }
	public string Name { get; }
}

public class UserRepository
{
	public User GetById(int id) => new User(1, "Bruce Wayne");
	public User GetByName(string name) => new User(2, "Peter Parker");
}

public Reader<UserRepository, User> GetUserByName(string name) =>
	from env in ask<UserRepository>()
	select env.GetByName(name);

public Reader<UserRepository, User> GetUserById(int id) =>
	from env in ask<UserRepository>()
	select env.GetById(id);

public Reader<UserRepository, string> GetUserAandBoss() =>
	from user in GetUserByName("some_name")
	from boss in GetUserById(user.Id)
	select $"user:{user.Name}, boss:{boss.Name}";

Notice that GetUserById and GetUserByName use UserRepository by ask'ing for it but without having to have it explicitly passed to them. Also note GetUserAandBoss is able to compose methods returning readers. Notice also that Reader has two generic types Env and A. Env is always fixed (in our case as UserRepository), but the type of A can change, the A in GetUserAandBoss isstring but the A in GetUserByName and GetUserById is User.

To actually execute, you need to call run passing in the dependency that will now be threaded throughout the composed chain and then matching on the result.

GetUserAandBoss()
	.Run(userRepository)
	.Match(x => Console.WriteLine(x), () => Console.WriteLine("Fail"));
	// outputs => user:Peter Parker, boss:Bruce Wayne

So to sum up, Reader is typically used for dependency injection (as shown in https://github.com/louthy/language-ext/issues/515) or configuration. You have a Configuration object with a bunch a settings that the methods throughout your code base need to access, but you don’t want to have to explicitly pass Configuration throughout your application. Instead you have a Reader<Configuration, A> and then at the entry point of you application, you load your Configuration from somewhere and call Run.

Having said also this, I’m new to all this myself and am happy to be corrected.

1reaction
TysonMNcommented, Feb 17, 2019

It’s just needs it to transparently pass to the other two methods it calls. This is the problem that the reader monad attempts to solve.

I really like this comment.

I have noticed that an expression with (say) two dependencies can either be the entire body of a function that requires both dependencies or part of the curried version of that function, which effectively moves a type from being part of the input to being part of the output.

For example, C Function(A a, B b) curries to Func<B, C> CurriedFunction(A a) and the type B has “moved” from the being part o the input (A and B) to being part of the output (Func<B, C>).

Since you have to “see” B either on the right as part of the input or on the left as part of the output, I don’t see an advantage one way or the other from a syntactic perspective.

What does seem different those is a separation of “what to do” and “when to do it” regarding B.

If your signature is C Function(A a, B b), then when it finishes executing, A and B have (presumably) been used and C has been computed. In particular, any side effects involved with accessing B have occurred.

On the other had, if your signature is Func<B, C> CurriedFunction(A a), then when it finishes executing, how to use B has been specified, but B hasn’t been used yet, so any side effects involved with accessing B have not occurred.

This seems like the real advantage to me.

The example I gave above comparing a function and its curried form is how I first became aware of this situation. However, a curried function is certainly just a special case of separating “what to do” and “when to do it”. My impression is that the “purpose” of the reader monad could be to preciously express the separation “what to do” and “when to do it” in the case of reading (as opposed to the case of writing, which is handled by the writer monad).

Read more comments on GitHub >

github_iconTop Results From Across the Web

The ReaderT Design Pattern
This data type will contain all runtime configuration and global functions that may be mockable (like logging functions or database access). If ...
Read more >
What is the purpose of * in the ReaderT Monad Transformer?
* is the kind of types with values : it stands for things like Int , List Int etc.. forall k means k...
Read more >
Who still uses ReaderT
Who still uses ReaderT. Monad transformers are a pretty amazing skill in any haskellers tool set, and can be a pleasure to work...
Read more >
What is MonadReader for when we have ReaderT? ...
The ReaderT is a newtype , and you can use it to pass a value around your monads such that it can be...
Read more >
Ep 12: ReaderT is good for the environment - YouTube
I implemented and used the WriterT monad a couple of videos ago, ... some data through our program, and instead use the ReaderT...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found