Fsharpplus: Handling errors within monad transformer stacks

Created on 20 Mar 2020  路  3Comments  路  Source: fsprojects/FSharpPlus

Edit: See this comment for more concise question and the answer to it.

I've got a delightful stack of monad transformers here:

type Operation<'TResult, 'TError>   = ReaderT<SqlConnection * SqlTransaction, ResultT<Async<Result<'TResult, 'TError list>>>>

and it works well working with it in computation expressions.

  let run
    (get        : 'TSubject -> Operation<'TState, 'TAppError>)
    (subj       : 'TSubject) : Operation<unit, 'TAppError> = monad {
    let! state = get subj |> map (fun x -> x) 
    return ()
  }

x here is 'TState which is lovely that map lifts that lambda all the way. However how do I do maps somewhere within the stack? Like transform the error type, represented here by transformToCommonErrorTypeN:

  let run
    (get        : 'TSubject -> Operation<'TState, 'TAppError>)
    (set        : 'TState   -> Operation<'TState, 'TOtherError>)
    (subj       : 'TSubject) : Operation<unit, 'TError> = monad {
    let! state = get subj |> transformToCommonErrorType1
    let state = state // ... interpret new state
    do! set subj state |> transformToCommonErrorType2
    return ()
  }

Given a function
'TAppError -> 'TError,
how do I lift that to a
Operation<'TResult, 'TAppError> -> Operation<'TResult, 'TError>

(i.e. ReaderT<SqlConnection * SqlTransaction, ResultT<Async<Result<'TResult, 'TAppError list>>>> -> ReaderT<SqlConnection * SqlTransaction, ResultT<Async<Result<'TResult, 'TError list>>>>.)

So far I have:

// AppError :: 'TAppError -> 'TError`
let! state = get subj |> (AppError |> List.map |> mapError |> Async.map |> ResultT.??? |> ReaderT.map)

but I'm not sure what I need at the ResultT.??? bit...

Side note: ResultT.map has an odd return type to my eye. Is ResultT<'``Monad<'Result<('T -> 'U),'E>>``> correct, or a mistaken type parameter name?

question

Most helpful comment

I have the impression that you need to use the catch function to transform the error, but not sure, I can't makes sense of your code.

Maybe if you can contact me in Slack we can discuss it and post the conclusion here. Feel free to ping me directly.

All 3 comments

Re Side note: The generic type variable name is intended to be documentation around the intent of the type variable. So the intention is to guide you on how to use the monad transformer.

I have the impression that you need to use the catch function to transform the error, but not sure, I can't makes sense of your code.

Maybe if you can contact me in Slack we can discuss it and post the conclusion here. Feel free to ping me directly.

After speaking with @gusty on Slack...

Question

A better way of explaining what I'm after was:

let example
  (get : unit -> Result<string, int>)
  (set : string -> Result<unit, string>)
  : Result<unit, string> = monad {
  let! result = get () |> first string
  do!  set result }
  // For completeness
let getter ()  = Ok "yup" // Error 42
let setter str = Ok ()    // Error "nope"
let result = example getter setter

I want to do what first f is doing in example (i.e. Result<_,int> -> Result<_,string>) but in the monad Operation<'TResult, 'TError>, where

type Operation<'TResult, 'TError> = ReaderT<SqlConnection * SqlTransaction, ResultT<Async<Result<'TResult, 'TError list>>>>

but I don't know how to lift (map?) it to that monad.
That is, in example2, what is here?

let example2
  (get : unit -> Operation<string, int>)
  (set : string -> Operation<unit, string>)
  : Operation<unit, string> = monad {
  let! result = get () |> here string
  do!  set result }

Answer

@gusty was right about catch, you can map the error like so:

let example2
  (get : unit -> Operation<string, int>)
  (set : string -> Operation<unit, string>)
  : Operation<unit, string> = monad {
  let! result = get () </catch/> (throw << map string)
  do!  set result }

which is pretty darn delightful. Thank you @gusty!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

btrepp picture btrepp  路  3Comments

pangwa picture pangwa  路  11Comments

cmeeren picture cmeeren  路  5Comments

ShalokShalom picture ShalokShalom  路  4Comments

NickDarvey picture NickDarvey  路  9Comments