当前位置:  开发笔记 > 后端 > 正文

多态返回类型取决于上下文

如何解决《多态返回类型取决于上下文》经验,为你挑选了2个好方法。

我正在玩Haskell中实现Redis客户端库,我的目标是尽可能地编码Haskell类型系统中Redis命令的语义.对于那些不知道的人来说,Redis是一个通过网络访问的数据存储区.我会用它来举例说明我的问题,但Redis并不是这个问题的焦点.

示例函数

考虑这个功能

get :: (RedisValue a) => Key -> Redis a
get k = decodeValue <$> sendCommand ["GET", key]

它向数据存储区发送命令并返回存储在给定下的值Key(对于此示例,您可以考虑type Key = String).至于返回类型:

Redis是的实例MonadMonadIO.它封装了有关网络连接的信息.sendCommand发送请求并返回数据存储区的回复.

a是多态的,例如可以返回Strings或ByteStrings,具体取决于上下文.

以下代码应阐明上述内容.

data Redis a = ...

instance MonadIO Redis where ...
instance Monad Redis where ...

sendCommand :: [String] -> Redis String

class RedisValue a where
    decodeValue :: String -> a

-- example instances
instance RedisValue String where ...
instance RedisValue ByteString where ...
不同的语境,不同的类型

Redis支持一种简单的交易形式.在事务中,大多数命令可以与事务外部相同地发送.但是,它们的执行会延迟,直到用户发送提交命令(exec在Redis中调用).在事务内部,数据存储区仅返回存储命令以供以后执行的确认.在commit(exec)后,返回所有存储命令的所有结果.

这意味着get上面的-function在事务上下文中看起来有点不同:

get :: (RedisStatus a) => Key -> RedisTransaction a
get k = decodeStatus <$> sendCommand ["GET", key]

注意:

monadic类型现在RedisTransaction用于指示事务上下文.

a现在返回类型的任何实例RedisStatus.RedisValue和的实例之间存在重叠RedisStatus.例如String,在两个类中.专用Status数据类型可能只在RedisStatus类中.

实际问题

我的问题是,如何编写一个get在两个上下文中都有效的函数,以及适合上下文的返回类型类.我需要的是

一种给出get返回类型"Redis或RedisTransaction"的方法,

类型a是的一个实例RedisValue中的Redis上下文和的一个实例RedisStatus中的RedisTransaction上下文.

decode根据上下文自动执行正确操作的函数.我认为这必须来自(多参数)类型.

如果您知道我如何做到这一点或者指向一些示例代码甚至是文章,那么您将得到我的谢意!



1> sclv..:

首先,我认为最好有两个不同的get命令.那就是说,这是一种方法.

class RedisGet m a where
    get :: Key -> m a

instance (RedisValue a) => RedisGet Redis a where...

instance (RedisStatus a) => RedisGet RedisTransaction a where...

你需要MPTC,但没有FunDeps或Type Families.每次使用get都需要有足够的信息来确定ma唯一.



2> Stefan Holde..:

我同意多参数类型类很适合这里.这是一种方法:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE FunctionalDependencies     #-}
{-# LANGUAGE MultiParamTypeClasses      #-}

newtype Redis a = Redis (IO a) deriving Monad
newtype RedisTransaction a = RedisTransaction (IO a) deriving Monad

newtype Key    = Key {unKey :: String}
newtype Value  = Value {unValue :: String}
newtype Status = Status {unStatus :: String}

class Monad m => RedisMonad m a | m -> a where
  sendCommand :: [String] -> m a

instance RedisMonad Redis Value where
  sendCommand = undefined -- TODO: provide implementation                       

instance RedisMonad RedisTransaction Status where
  sendCommand = undefined -- TODO: provide implementation                       

class Decodable a b where
  decode :: a -> b

instance Decodable Status String where
  decode = unStatus

instance Decodable Value String where
  decode = unValue

get :: (RedisMonad m a, Decodable a b) => Key -> m b
get k = do
  response <- sendCommand ["GET", unKey k]
  return (decode response)

注意类型isomorphisms的使用ValueStatus:它使得String你的实现产生的东西稍微强一些sendCommand显然不仅仅是任意字符序列,而是遵循一些固定格式的返回值和状态.

推荐阅读
依然-狠幸福
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有