FSharpPlus


Reader<'R,'T>

The Reader monad is good for computations which read values from a shared environment.

Related Types

Examples

#r @"nuget: FSharpPlus"

One usage of the Reader monad is an alternative to dependency injection or currying in order to pass around dependencies. The below code comes from F# Online - Josef Starýchfojtů - FSharpPlus - Advanced FP concepts in F#. You can find the presenter on github as @starychfojtu.

Why would you want to do this style?

Why wouldn't you want to use this style?

Note:

open System
open FSharpPlus
open FSharpPlus.Data

type IUserRepository =
    abstract GetUser : email : string -> string

type IShoppingListRepository =
    abstract AddToCart : shoppingList : string list -> string

let getUser email =
    Reader(fun (env : #IUserRepository) -> env.GetUser email)

let addToShoppingList shoppingListItems =
    Reader(fun (env : #IShoppingListRepository) -> env.AddToCart shoppingListItems)

let addShoppingListM email = monad {
    let! user = getUser email
    // 
    let shoppingListItems = ["Apple"; "Pear";]
    return! addToShoppingList shoppingListItems
}

type MockDataEnv() = // This is how an environment could be constructed
    interface IUserRepository with
        member this.GetUser email =
                "Sandeep"
    interface IShoppingListRepository with
            member this.AddToCart shoppingListItems =
                sprintf "Added the following items %A to the cart" shoppingListItems

Reader.run (addShoppingListM "sandeep@test.com")  (MockDataEnv())

Sample from The Reader monad on Haskell Wiki

open System
open FSharpPlus
open FSharpPlus.Data

/// This the abstract syntax representation of a template
type Template =
    /// Text
    | T of string
    /// Variable
    | V of Template
    /// Quote
    | Q of Template
    /// Include
    | I of Template*(Definition list)
    /// Compound
    | C of Template list
and Definition = | D of Template*Template

/// Our environment consists of an association list of named templates and
/// an association list of named variable values.
type Environment = {templates: Map<string,Template>
                    variables: Map<string,string>}

/// lookup a variable from the environment
let lookupVar (name:string) (env:Environment) : string option = tryItem name env.variables

/// lookup a template from the environment
let lookupTemplate (name:string) (env:Environment) : Template option = tryItem name env.templates

/// add a list of resolved definitions to the environment
let addDefs (defs:(string*string) list) env = { env with variables = plus (Map.ofList defs) env.variables}

/// resolve a template into a string
let rec resolve : Template -> Reader<Environment,string>  = function 
                       | T s -> result s
                       | V t -> monad {
                                   let! varName = resolve t
                                   let! env = ask
                                   let varValue = lookupVar varName env
                                   return option id "" varValue }
                        | Q t -> monad {
                                   let! tmplName = resolve t
                                   let! env = ask
                                   let body = lookupTemplate tmplName env
                                   return option string "" body }
                        | I (t,ds) -> monad {
                                    let! tmplName = resolve t
                                    let! env = ask
                                    let body = lookupTemplate tmplName env
                                    match body with
                                    | Some t' ->
                                                let! defs = List.traverse resolveDef ds
                                                return! local (addDefs defs) (resolve t')
                                    | None -> return ""
                                    }
                        | C ts   -> monad {
                                      let! resolved = List.traverse resolve ts
                                      return String.Concat<string> resolved
                                    }
and
   /// resolve a Definition and produce a (name,value) pair
   resolveDef: Definition -> Reader<Environment,string*string> = 
                                      function 
                                      | D (t,d) -> monad {
                                        let! name = resolve t
                                        let! value = resolve d
                                        return (name,value) }

Recommended reading

namespace System
namespace FSharpPlus
namespace FSharpPlus.Data
type IUserRepository = abstract GetUser: email: string -> string
Multiple items
val string: value: 'T -> string

--------------------
type string = String
type IShoppingListRepository = abstract AddToCart: shoppingList: string list -> string
type 'T list = List<'T>
val getUser: email: string -> Reader<#IUserRepository,string>
val email: string
Multiple items
union case Reader.Reader: ('r -> 't) -> Reader<'r,'t>

--------------------
module Reader from FSharpPlus.Data
<summary> Basic operations on Reader </summary>

--------------------
[<Struct>] type Reader<'r,'t> = | Reader of ('r -> 't) static member ( *> ) : x: Reader<'R,'T> * y: Reader<'R,'U> -> Reader<'R,'U> static member (<!>) : f: ('T -> 'U) * x: Reader<'R,'T> -> Reader<'R,'U> static member ( <* ) : x: Reader<'R,'U> * y: Reader<'R,'T> -> Reader<'R,'U> static member (<*>) : f: Reader<'R,('T -> 'U)> * x: Reader<'R,'T> -> Reader<'R,'U> static member (=>>) : Reader<'Monoid,'T> * f: (Reader<'Monoid,'T> -> 'U) -> Reader<'Monoid,'U> (requires member ``+``) static member (>>=) : x: Reader<'R,'T> * f: ('T -> Reader<'R,'U>) -> Reader<'R,'U> static member Delay: body: (unit -> Reader<'R,'T>) -> Reader<'R,'T> static member Extract: Reader<'Monoid,'T> -> 'T (requires member Zero) static member Return: x: 'T -> Reader<'R,'T> static member TryFinally: Reader<'a2,'a3> * f: (unit -> unit) -> Reader<'a2,'a3> ...
<summary> Computation type: Computations which read values from a shared environment. <para /> Binding strategy: Monad values are functions from the environment to a value. The bound function is applied to the bound value, and both have access to the shared environment. <para /> Useful for: Maintaining variable bindings, or other shared environment.</summary>
val env: #IUserRepository
abstract IUserRepository.GetUser: email: string -> string
val addToShoppingList: shoppingListItems: string list -> Reader<#IShoppingListRepository,string>
val shoppingListItems: string list
val env: #IShoppingListRepository
abstract IShoppingListRepository.AddToCart: shoppingList: string list -> string
val addShoppingListM: email: string -> Reader<'a,string> (requires 'a :> IShoppingListRepository and 'a :> IUserRepository)
val monad<'monad<'t>> : MonadFxBuilder<'monad<'t>>
<summary> Creates a (lazy) monadic computation expression with side-effects (see http://fsprojects.github.io/FSharpPlus/computation-expressions.html for more information) </summary>
val user: string
Multiple items
type MockDataEnv = interface IShoppingListRepository interface IUserRepository new: unit -> MockDataEnv

--------------------
new: unit -> MockDataEnv
val this: MockDataEnv
val sprintf: format: Printf.StringFormat<'T> -> 'T
val run: Reader<'R,'T> -> ('R -> 'T)
type Template = | T of string | V of Template | Q of Template | I of Template * Definition list | C of Template list
 This the abstract syntax representation of a template
union case Template.T: string -> Template
 Text
union case Template.V: Template -> Template
 Variable
union case Template.Q: Template -> Template
 Quote
union case Template.I: Template * Definition list -> Template
 Include
type Definition = | D of Template * Template
union case Template.C: Template list -> Template
 Compound
union case Definition.D: Template * Template -> Definition
type Environment = { templates: Map<string,Template> variables: Map<string,string> }
 Our environment consists of an association list of named templates and
 an association list of named variable values.
Environment.templates: Map<string,Template>
Multiple items
module Map from FSharpPlus
<summary> Additional operations on Map&lt;'Key, 'Value&gt; </summary>

--------------------
module Map from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> = interface IReadOnlyDictionary<'Key,'Value> interface IReadOnlyCollection<KeyValuePair<'Key,'Value>> interface IEnumerable interface IComparable interface IEnumerable<KeyValuePair<'Key,'Value>> interface ICollection<KeyValuePair<'Key,'Value>> interface IDictionary<'Key,'Value> new: elements: seq<'Key * 'Value> -> Map<'Key,'Value> member Add: key: 'Key * value: 'Value -> Map<'Key,'Value> member Change: key: 'Key * f: ('Value option -> 'Value option) -> Map<'Key,'Value> ...

--------------------
new: elements: seq<'Key * 'Value> -> Map<'Key,'Value>
Environment.variables: Map<string,string>
val lookupVar: name: string -> env: Environment -> string option
 lookup a variable from the environment
val name: string
val env: Environment
Multiple items
val option: f: ('g -> 'h) -> n: 'h -> _arg1: 'g option -> 'h
<summary> Takes a function, a default value and a option value. If the option value is None, the function returns the default value. Otherwise, it applies the function to the value inside Some and returns the result. </summary>
<category index="0">Common Combinators</category>


--------------------
type 'T option = Option<'T>
val tryItem: n: 'K -> source: 'Indexed<'T> -> 'T option (requires member TryItem)
<summary> Tries to get an item from the given index. </summary>
<category index="16">Indexable</category>
val lookupTemplate: name: string -> env: Environment -> Template option
 lookup a template from the environment
val addDefs: defs: (string * string) list -> env: Environment -> Environment
 add a list of resolved definitions to the environment
val defs: (string * string) list
val plus: x: 'Monoid -> y: 'Monoid -> 'Monoid (requires member ``+``)
<summary> Combines two monoids in one. </summary>
<category index="4">Monoid</category>
val ofList: elements: ('Key * 'T) list -> Map<'Key,'T> (requires comparison)
val resolve: _arg1: Template -> Reader<Environment,string>
 resolve a template into a string
val s: string
val result: x: 'T -> 'Functor<'T> (requires member Return)
<summary> Lifts a value into a Functor. Same as return in Computation Expressions. </summary>
<category index="2">Applicative</category>
val t: Template
val varName: string
val ask<'MonadReader<'R,'T> (requires member Ask)> : 'MonadReader<'R,'T> (requires member Ask)
<summary>The environment from the monad.</summary>
<category index="18">Monad Transformers</category>
val varValue: string option
val id: x: 'T -> 'T
val tmplName: string
val body: Template option
val ds: Definition list
union case Option.Some: Value: 'T -> Option<'T>
val t': Template
Multiple items
module List from FSharpPlus.Data
<summary> Additional operations on List </summary>

--------------------
module List from FSharpPlus
<summary> Additional operations on List </summary>

--------------------
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
val traverse: f: ('T -> 'Applicative<'U>) -> xs: 'T list -> 'Applicative<list<'U>> (requires member Map and member ``<*>`` and member Traverse and member Return)
<summary> Maps each element of the list to an action, evaluates these actions from left to right and collect the results. </summary>
val resolveDef: _arg10: Definition -> Reader<Environment,(string * string)>
 resolve a Definition and produce a (name,value) pair
val local: f: ('R1 -> 'R2) -> m: 'MonadReader<'R2,'T> -> 'MonadReader<'R1,'T> (requires member Local)
<summary> Executes a computation in a modified environment. </summary>
<category index="18">Monad Transformers</category>
<param name="f"> The function to modify the environment. </param>
<param name="m"> Reader to run in the modified environment. </param>
union case Option.None: Option<'T>
val ts: Template list
val resolved: string list
Multiple items
type String = interface IEnumerable<char> interface IEnumerable interface ICloneable interface IComparable interface IComparable<string> interface IConvertible interface IEquatable<string> new: value: nativeptr<char> -> unit + 8 overloads member Clone: unit -> obj member CompareTo: value: obj -> int + 1 overload ...
<summary>Represents text as a sequence of UTF-16 code units.</summary>

--------------------
String(value: nativeptr<char>) : String
String(value: char array) : String
String(value: ReadOnlySpan<char>) : String
String(value: nativeptr<sbyte>) : String
String(c: char, count: int) : String
String(value: nativeptr<char>, startIndex: int, length: int) : String
String(value: char array, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : String
String.Concat<'T>(values: Collections.Generic.IEnumerable<'T>) : string
   (+0 other overloads)
String.Concat([<ParamArray>] values: string array) : string
   (+0 other overloads)
String.Concat([<ParamArray>] args: obj array) : string
   (+0 other overloads)
String.Concat(arg0: obj) : string
   (+0 other overloads)
String.Concat(values: Collections.Generic.IEnumerable<string>) : string
   (+0 other overloads)
String.Concat(str0: string, str1: string) : string
   (+0 other overloads)
String.Concat(str0: ReadOnlySpan<char>, str1: ReadOnlySpan<char>) : string
   (+0 other overloads)
String.Concat(arg0: obj, arg1: obj) : string
   (+0 other overloads)
String.Concat(str0: string, str1: string, str2: string) : string
   (+0 other overloads)
String.Concat(str0: ReadOnlySpan<char>, str1: ReadOnlySpan<char>, str2: ReadOnlySpan<char>) : string
   (+0 other overloads)
val d: Template
val value: string