tutorial programar ejemplos f# monads monad-transformers state-monad

programar - ¿Puedo usar diferentes flujos de trabajo simultáneamente en F#?



f# tutorial (3)

En F # no puede mezclar fácilmente diferentes tipos de expresiones de cálculo como lo haría en Haskell usando Monad Transformers o técnicas similares. Sin embargo, podría construir su propia Monad, enhebrar el estado de inserción y valores opcionales, como en:

type StateMaybe<''T> = MyState -> option<''T> * MyState // Runs the computation given an initial value and ignores the state result. let evalState (sm: StateMaybe<''T>) = sm >> fst // Computation expression for SateMaybe monad. type StateMaybeBuilder() = member this.Return<''T> (x: ''T) : StateMaybe<''T> = fun s -> (Some x, s) member this.Bind(sm: StateMaybe<''T>, f: ''T -> StateMaybe<''S>) = fun s -> let mx,s'' = sm s match mx with | Some x -> f x s'' | None -> (None, s) // Computation expression builder. let maybeState = new StateMaybeBuilder() // Lifts an optional value to a StateMaybe. let liftOption<''T> (x: Option<''T>) : StateMaybe<''T> = fun s -> (x,s) // Gets the current state. let get : StateMaybe<MyState> = fun s -> (Some s,s) // Puts a new state. let put (x: MyState) : StateMaybe<unit> = fun _ -> (Some (), x)

Aquí hay un ejemplo de cálculo:

// Stateful computation let computation = maybeState { let! x = get let! y = liftOption (Some 10) do! put (x + y) let! x = get return x } printfn "Result: %A" (evalState computation 1)

StateMaybe puede generalizarse más al hacer genérico el tipo de componente de estado.

Necesito que mi estado se transfiera mientras puedo encadenar funciones con el flujo de trabajo tal vez. ¿Hay alguna manera de que 2 flujos de trabajo compartan el mismo contexto? Si no, ¿cuál es la forma de hacerlo?

ACTUALIZAR:

Bueno, tengo un estado que representa un segmento de ID disponibles para las entidades que voy a crear en la base de datos. Entonces, una vez que se adquiere una ID, el estado debe transformarse a un estado más nuevo con la siguiente identificación disponible y desecharse para que nadie pueda volver a utilizarla. No quiero mutar el estado por el hecho de ser idiomático. La mónada estatal parece una forma de ir, ya que oculta la transformación y transmite el estado. Una vez que el flujo de trabajo del estado está en su lugar, no puedo usar el flujo de trabajo Maybe, que es algo que uso en todas partes.


Otros ya le dieron una respuesta directa a su pregunta. Sin embargo, creo que la forma en que se plantea la pregunta conduce a una solución que no es muy idiomática desde la perspectiva de F #: esto podría funcionar para usted siempre y cuando usted sea la única persona que trabaje en el código, pero recomendaría no hacerlo. .

Incluso con los detalles agregados, la pregunta sigue siendo bastante general, pero aquí hay dos sugerencias:

  • No hay nada de malo con el estado mutable razonablemente utilizado en F #. Por ejemplo, está perfectamente bien crear una función que genere ID y la transmita:

    let createGenerator() = let currentID = ref 0 (fun () -> incr currentID; !currentID)

  • ¿Realmente necesita generar los ID mientras construye las entidades? Parece que podría generar una lista de entidades sin ID y luego usar Seq.zip para comprimir la lista final de entidades con una lista de ID.

  • En cuanto al cálculo tal vez, ¿lo está usando para manejar estados regulares y válidos o para manejar estados excepcionales? (Suena como el primero, que es la manera correcta de hacer las cosas, pero si necesita manejar estados verdaderamente excepcionales, entonces puede usar excepciones ordinarias de .NET).


Como se indicó en la respuesta anterior, una forma de combinar flujos de trabajo en F # (Mónadas en Haskell) es mediante el uso de una técnica llamada Monad Transformers.

En F # esto es realmente complicado, aquí hay un proyecto que trata con esa técnica.

Es posible escribir el ejemplo de la respuesta anterior combinando automáticamente State y Maybe (opción), usando esa biblioteca:

#r @"C:/Packages/FSharpPlus.2.0.0-CI00075/FsControl.dll" #r @"C:/Packages/FSharpPlus.1.0.0-CI00018/FSharpPlus.dll" open FSharpPlus // Stateful computation let computation = monad { let! x = get let! y = OptionT (result (Some 10)) do! put (x + y) let! x = get return x } printfn "Result: %A" (State.eval (OptionT.run computation) 1)

Esta es la otra alternativa, en lugar de crear su flujo de trabajo personalizado, usar un flujo de trabajo genérico que se deducirá automáticamente (a-la Haskell).