[Haskell-beginners] grouping functions together
Dimitri DeFigueiredo
defigueiredo at ucdavis.edu
Tue Jun 9 04:04:15 UTC 2015
I used typeclasses because I want to have a "default version" of the run
function.
I want that function to be able to call (specialized versions) of the
other functions in the group. This is the only way *I know* to "factor
out" common code in Haskell while still allowing the "factored out" code
to call specialized versions of the other functions. In my view, this is
very similar to inheritance and specialization is Object-Oriented
Programming. Is there another way to do this?
I don't see how I could do this with a record. If the run function were
mostly the same for all types except for calls to specialized versions
of the others. I think I would have to write a completely separate
version of run for each instance. The example below shows what I mean.
Also, my apologies, but my code was wrong. I now realize it did not
capture what I need. I don't need the functions to be polymorphic for
all types within a single instance. Within a single instance, I just
need them to work for a few specific types. So, here's a better version
(my current one):
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Monad
class Reliable m s where
type Req s :: * -- the type for requests
type Atp s :: * -- the type for attempts
type Ack s :: * -- the type for acknowledgments
type Res s :: * -- the type for results (Success)
type Fai s :: * -- the type for failures
getRequests :: Monad m => s -> m [Req s]
mkAttempt :: Monad m => s -> Req s -> m (Maybe (Atp s))
action :: Monad m => s -> Atp s -> m (Maybe (Ack s))
getAcks :: Monad m => s ->[Atp s] -> m [Ack s]
mkResult :: Monad m => s -> Req s -> Ack s -> m (Either (Fai s)
(Res s))
run :: Monad m => s -> Req s -> m (Res s)
data RemoteCall = RemoteCall
instance Reliable IO RemoteCall where
type Req RemoteCall = Int
type Atp RemoteCall = String
type Ack RemoteCall = Bool
type Res RemoteCall = String
type Fai RemoteCall = Int
getRequests = undefined -- these can be specialized for each instance
mkAttempt = undefined
action = undefined
getAcks = undefined
mkResult = undefined
run s req = do -- dummy version
mAtp <- mkAttempt s req
mAck <- action s (fromJust mAtp)
eRes <- mkResult s req (fromJust mAck)
return $ case eRes of
Left f -> error "failure"
Right s -> s
I don't know how I would write the 'run' function above only once if I
were using records. It seems I would have to duplicate code, no?
Thank you!
Dimitri
On 08/06/15 16:36, Rein Henrichs wrote:
> This seems like a case where you only really need a record, not a
> typeclass.
>
> On Mon, Jun 8, 2015 at 2:47 PM Dimitri DeFigueiredo
> <defigueiredo at ucdavis.edu <mailto:defigueiredo at ucdavis.edu>> wrote:
>
> Hello!
>
> I am trying to tie together a group of functions that turn an
> unreliable
> remote network call into a reliable one. For each different network
> request I make, a specific group of these functions should always work
> together, but their type signatures are quite different. My first
> thought was to put them all in a typeclass:
>
> import Control.Monad
>
> class Reliable1 m req attempt ack failure result where
> getRequests1 :: Monad m => m [req]
> mkAttempt1 :: Monad m => req -> m (Maybe attempt)
> action1 :: Monad m => attempt -> m (Maybe ack)
> getAcks1 :: Monad m => [attempt] -> m [ack]
> mkResult1 :: Monad m => req -> ack -> m (Either failure
> result)
> run1 :: Monad m => req -> m result
>
> That doesn't work because not all functions use all parameters. For
> example, getAcks1 has no idea of what the final 'result' type
> parameter
> is. This lead me to my second attempt. Defining a 'service' type with
> the sole purpose of tying them all together. Here's my current
> attempt:
>
> {-# LANGUAGE MultiParamTypeClasses #-}
>
> import Control.Monad
>
> class Reliable m service where
> getReqs :: Monad m => service -> m [req]
> mkAttempt :: Monad m => service -> req -> m (Maybe attempt)
> action :: Monad m => service -> attempt -> m (Maybe ack)
> getAcks :: Monad m => service -> [attempt] -> m [ack]
> mkResult :: Monad m => service -> req -> ack -> m (Either
> failure result)
> run :: Monad m => service -> req -> m result
>
> data RemoteCall = RemoteCall
>
> instance Reliable IO RemoteCall where
> getReqs = undefined
> mkAttempt = undefined
> action = undefined
> getAcks = undefined
> mkResult = undefined
> run = undefined
>
> This works, but I have to explicitly pass the 'service' argument in
> every call.
> Can I avoid passing this parameter every time?
> Question, is there a better way to do this?
> I wanted to have a wrapper to make my remote calls reliable.
>
> Thanks,
>
> Dimitri
>
>
>
> _______________________________________________
> Beginners mailing list
> Beginners at haskell.org <mailto:Beginners at haskell.org>
> http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
>
>
>
> _______________________________________________
> Beginners mailing list
> Beginners at haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/beginners/attachments/20150608/7eec8fec/attachment.html>
More information about the Beginners
mailing list