This is a little too advanced for me. I think I vaguely understand the
concept,
But I'm not sure how to actually put them together, In fact I couldn't even
get
this to compile. :(
{-# Language DeriveFunctor #-}
{-# Language TypeOperators #-}
{-# Language TypeFamilies #-}
import Data.Functor.Coproduct
import Data.Void
import Control.Monad.Free
import MVC
newtype Remote req resp x = Remote (req, resp -> x)
deriving (Functor)
type a :++: b = Coproduct a b
type a :+: b = Either a b
data VoidF a
type family OutputsOf a
type instance OutputsOf (Remote reqR respR :++: x) = respR :+: OutputsOf x
type instance OutputsOf (VoidF a)= Void
type family InputsOf a
type instance InputsOf (Remote reqR respR :++: x) = reqR :+: InputsOf x
type instance InputsOf (VoidF a)= Void
serve
:: (reqLocal -> Free remote respLocal)
-> Model (reqLocal :+: OutputsOf remote) (respLocal :+: InputsOf remote)
serve = undefined
On Wednesday, April 30, 2014 2:48:27 PM UTC-7, Gabriel Gonzalez wrote:
>
> There are two answers, which depend on whether or not you want to block
> on the request to an external service.
>
> Scenario A) You want to block waiting for a response
>
> The solution for this is that the next generalization of `mvc` will
> replace the `State` layer of the model with a free monad of all possible
> synchronous interactions. See this post of mine, which explains the
> general concept behind this:
>
> http://www.haskellforall.com/2012/07/purify-code-using-free-monads.html
>
> The intended use case for this is interactions with a database (which are
> supposed to be fast and synchronous). However, HTTP requests don't really
> seem appropriate for this scenario since they usually have non-trivial
> latency and you will freeze your entire web service if you block waiting
> for a request.
>
> Scenario B) You want to asynchronously wait for a response
>
> The solution for this is to continue to use a `View` and `Controller` to
> interact with the external service. This forces you to structure your
> model so that it doesn't block waiting for the response.
>
> However, it sounds like this would benefit from layering an orthogonal
> abstraction on top of `mvc` that would let you write code that looks like
> concurrent synchronous requests but gets compiled to the correct
> single-threaded formulation in terms of `Model`s, `View`s, and
> `Controller`s. The rough outline I'm thinking of is something like:
>
> -- The base functor for an external request
> newtype Remote req resp x = Remote (req, respond -> x) deriving
> (Functor)
>
> -- A synonym for `Data.Functor.CoProduct` (the sum of two functors)
> type a :++: b = Data.Functor.Coproduct.CoProduct a b
>
> -- The empty functor, analogous to `Void`
> data VoidF a
>
> -- A synonym for `Either`
> type a :+: b = Either a b
>
> -- See below for how to generalize this to an arbitrary number
> -- of external services using something like type families
> serve
> :: (reqLocal -> Free (Remote reqR1 respR1 :++: Remote reqR2
> respR2 :++: VoidF) respLocal)
> -> Model (reqLocal :+: respR1 :+: respR2 :+: Void) (respLocal :+:
> reqR1 :+: reqR2 :+: Void)
>
> The argument of `serve` would be the handler for each incoming request.
> It takes an argument of type `reqLocal` (i.e. "request local") and makes a
> few calls to two external services, and then returns a result of type
> `respLocal` when it is done handling the request. Then `serve` takes that
> handler and compiles it to the equivalent `Model`.
>
> Generalizing this type signature to any number of services should be
> possible, but I need to think about it some more. The more general
> signature might look like this:
>
> serve
> :: (reqLocal -> Free remote respLocal)
> -> Model (reqLocal :+: OutputsOf remote) (respLocal :+: InputsOf
> remote)
>
> ... where `OutputsOf` and `InputsOf` are type level functions from the
> coproduct of services to sums of outputs or inputs, respectively:
>
> type OutputsOf (Remote reqR respR :++: x) = respR :+: OutputsOf x
> type OutputsOf VoidF = Void
>
> type InputsOf (Remote reqR respR :++: x) = reqR :+: InputsOf x
> type InputsOf VoidF = Void
>
> Yeah, I think that would work. Give me some time and I will try my hand
> at that and see if I can implement something like `serve`. Then ideally
> you would be able to just write something like this:
>
> let model = serve $ \incoming -> do
> ...
> resp1 <- external1 req1
> ...
> resp2 <- external2 req2
> ...
> return outgoing
>
> You'd still be responsible for providing the appropriate `View`s and
> `Controller`s, but the hard part of chaining "synchronous" requests
> together without blocking your `Model` would be done for you and you'd
> still preserve the purity of the `Model`.
>
> In fact, I wonder if I can use this to simultaneously solve the database
> interface, too, without having to modify `mvc` at all. This seems like a
> much more general solution than I anticipated when I first started writing
> out this e-mail.
>
> On 4/30/14, 11:03 AM, Kai Wang wrote:
>
> From the documentation, the "Controller" represents an input to the model,
> and the "View" is an output.
> In the context of an RESTful web app, I would map an processed HTTP
> request (GET/POST/etc) to Controller,
> and rendered HTML/JSON as view.
>
> However, real life web app would need much more than that. Often they
> need additional IO. Say I need to make
> use of some 3rd party web service. Then I would need to send some http
> request. This request I send is
> an output of my model, but I can hardly call it a "View". How would I deal
> with situations like this with mvc
> library? Other additional IOs like logging, state persistence also don't
> fit into the mvc concept very well.
> How can we add these features?
> --
> You received this message because you are subscribed to the Google Groups
> "Haskell Pipes" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected] <javascript:>.
> To post to this group, send email to [email protected]<javascript:>
> .
>
>
>
--
You received this message because you are subscribed to the Google Groups
"Haskell Pipes" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].