【问题标题】:how to implement non-reference transparent data structure in Haskell?如何在 Haskell 中实现非引用透明数据结构?
【发布时间】:2016-01-26 03:31:48
【问题描述】:

我想实现一个数据结构,当我注册它时会给我一个“签名”,因此我可以:

  1. 查询签名是否存在
  2. 合并两个这样的 DS,同时保持正确的行为
  3. 删除给定的签名

可能有点模糊,但很容易实现,比如用 Java 实现,如下所示:

class Sig {}

class DesiredDS<A> {
    HashMap<Sig, A> map = new HashMap<>();

    public Sig insertWithNewSig(A a) {
        Sig s = new Sig();
        map.put(s, a);
        return s;
    }
    // ...
    // merge will be only to merge to map and delete will be only to delete the key
}

但我摸不着头脑,不知道如何在 Haskell 中使用参考透明度的属性来做到这一点。

有什么好主意吗?可能对此的等效答案是:如何在 Haskell 中生成一个可以比较相等的数据结构,而我不需要费心决定它的值?

【问题讨论】:

  • 你也可以这样做——唯一的区别是你传递了HashMap(或者你选择在Haskell中保持你的关联的任何结构)——现在你可以变得花哨了,把在您自己的结构中(〜DesiredDS),使用State-Monad(变压器)为您保存地图,...
  • 这些签名应该是什么样的?大随机数?
  • 听起来像(Monoid v, Monad m) =&gt; StateT (HashMap Sig v) m a,具体取决于您的实际merge 行为。

标签: haskell


【解决方案1】:

可能最简单的方法是使用类似的东西

type Signature a = IORef (Maybe a)

您对其进行操作:

  1. 查询签名是否存在

    live :: Signature a -> IO Bool
    live ref = isJust <$> readIORef ref
    
  2. 合并两个这样的 DS,同时保持正确的行为

    (...什么是“正确的行为”?)

  3. 删除给定的签名

    delete :: Signature a -> IO ()
    delete ref = writeIORef ref Nothing
    
  4. 创建新签名

    insertNewSignature :: a -> IO (Signature a)
    insertNewSignature a = newIORef (Just a)
    
  5. 比较等于

    (==) :: Signature a -> Signature a -> Bool
    

而且,虽然您没有要求它,但大概您实际上不仅希望能够检查签名是否存在,而且还希望能够从签名中恢复值,您可以对其进行操作

readIORef :: Signature a -> IO (Maybe a)

【讨论】:

    【解决方案2】:

    你有很多选择!如果您希望随机签名以获得唯一性,我建议使用randomness monad 来处理您的随机种子,并使用StateT 来管理您的HashMap。大致是这样的:

    type Sig = -- Something big enough that's an instance of `Random`
    newtype DS a = DS (HashMap Sig a)
    insert :: (MonadRandom m, MonadState (DS a) m) => a -> m Sig
    insert a = do
      ds <- get
      ds' <- HM.insert <$> getRandom <*> pure a
      put ds'
    

    【讨论】:

      【解决方案3】:

      使用不安全的 I/O 算作弊吗?

      {-# LANGUAGE GeneralizedNewtypeDeriving #-}
      
      module Lib where
      
      import           Data.Map (Map)
      import qualified Data.Map as Map
      import           Data.Monoid
      import           Data.Unique (Unique)
      import qualified Data.Unique as Unique
      import           System.IO.Unsafe
      
      newtype Registrar a =
        Registrar (Map Unique a)
        deriving (Eq, Monoid)
      
      insert :: a -> Registrar a -> (Unique, Registrar a)
      insert value (Registrar amap) =
        (u, Registrar (Map.insert u value amap))
        where
          u = unsafePerformIO Unique.newUnique
      
      merge :: Registrar a -> Registrar a -> Registrar a
      merge = (<>)
      
      delete :: Unique -> Registrar a -> Registrar a
      delete signature (Registrar amap) =
        Registrar (Map.delete signature amap)
      

      【讨论】:

      • 使用 IO 或 St. IO 并不邪恶。请不要在stackoverflow答案上使用unsafe*
      • 这里完全无害,朋友
      • 不,它不是在这里完全无害。它在这里打破了参考透明度。我不会像@ThomasMDuBuisson 那样走得那么远,但我一般会反对这种非法使用不安全函数的行为。
      • 非参照透明是这里的游戏名称,朋友
      • 非参考透明度是一个 XY 问题。这不是正确的做法! unsafePerformIO 是一种工具,用于构建暴露纯 API 的框架的低级内容,并用于调试。这不是为了将 Haskell 变成 Java。
      猜你喜欢
      • 1970-01-01
      • 2018-03-29
      • 2021-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多