State monad and strategy pattern

落爺英雄遲暮 提交于 2019-12-21 11:22:14

问题


I am redesigning a library and I am not happy with the current design pattern. This question concerns the use of the strategy pattern in conjunction with a State monad

I have a Filter. All it does, in its basic implementation, is to take a some datafeed of type 'd and update itself, generating a new updated copy of itself.

[<AbstractClass>]
type Filter<'d, 'F> (state: 'F) =
    member val StateVariable = state with get
    abstract member Update: 'd -> Filter<'d, 'F>

I have then a ISignalGenerator, that takes a filter, environmental data and process it to generate a Signal of type 'S.

type ISignalGenerator<'d, 'F, 'S> =
    abstract member GenerateSignal: 'd -> Filter<'d,'F> -> 'S

The SignalGenerator is a strategy pattern object. On SignalGenerator's implementations, the library user mounts the functions that will be used and combined to generate the Signal.

I could wrap my code in a state monad. Together with some environmental variables (the datafeed), the state monad will carry carry along 'Filter' as state. SignalGenerator will then get state updates via the state monad (the datafeed of type 'd and the Filter)

The design issue I have is that I'd like to decouple the SignalGenerator type from the development of the workflow, i.e. I'd like to avoid nesting the state monad in the belly of the SignalGenerator. Is there a functional design pattern to achieve this?

EDIT

Based on Tomas's comment, I have worked on a toy model. The choice of having a strategy class is based on the need to wrap together a number of functions.

/////////////////////////////////////////////////////////////////////////////////////
// Definition of the state 
/////////////////////////////////////////////////////////////////////////////////////
type StateFunc<'State, 'T> = 'State -> 'T * 'State
/////////////////////////////////////////////////////////////////////////////////////
// Definition of the State monad type
/////////////////////////////////////////////////////////////////////////////////////
type StateMonadBuilder<'State>() =

    // M<'T> -> M<'T>
    member b.ReturnFrom a : StateFunc<'State, 'T> = a

    // 'T -> M<'T>
    member b.Return a : StateFunc<'State, 'T> = ( fun s ->  a, s)

    // M<'T> * ('T -> M<'U>) -> M<'U>
    member b.Bind(p : StateFunc<_, 'T>, rest : 'T -> StateFunc<_,_>) : StateFunc<'State, 'U>  = 
        (fun s ->
            let a, s' = p s
            rest a s')

    // Getter for the whole state, this type signature is because it passes along the state & returns the state
    member b.getState : StateFunc<'State, _> = (fun s -> s, s)

    // Setter for the state
    member b.putState (s:'State) : StateFunc<'State, _> = (fun _ -> (), s)

/////////////////////////////////////////////////////////////////////////////////////
// The actual example
/////////////////////////////////////////////////////////////////////////////////////

let state = StateMonadBuilder<int> ()

// DoubleFunctOne defines standard operations that remain always the same
type Strategy (functOne) =
    member this.DoubleFunctOne (x: int) = state {
        let! res = functOne x
        return res * 2 }

// I introduce customization with the definition of this function.
// Whenever I need, I will swap the function with some other   
let myFunctOne x = state {        
    let someOtherFun x = x + 10 
    let! currState = state.getState
    return currState * someOtherFun x}

// Here I mount the custom function on the strategy class, so the Strategy.DoubleFunctOne can produce a result
// In order to do so, I need to keep the construction in the state monad 
let strategy1 = state {
    return Strategy (myFunctOne) }

// Here begins the client side. The client will consume the methods provided by my strategies.
// He should not be concerned by the construction of the strategies
// Ok, then, let's put our work in production
let test1 = (state {
    let! strategy = strategy1
    return! strategy.DoubleFunctOne 10 }) 9

I was wondering if there would be a pattern solution where the Strategy class could consume the mounted functions without nesting the state monad in its belly. In other words, is there a way to postpone the definition of the let state = StateMonadBuilder<int> (), without landing in a type inference headache?

I am relatively new to functional programming and F#. Please let me know if my question even makes sense! Thanks.

来源:https://stackoverflow.com/questions/20238930/state-monad-and-strategy-pattern

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!