Haskell 安全使用GADT中的不安全资源?

Haskell 安全使用GADT中的不安全资源?,haskell,Haskell,说我有 {-# LANGUAGE GADTs #-} import Unsafe.Coerce data Any where Any :: a -> Any type Index = Int newtype Ref a = Ref Index mkRef :: a -> Index -> (Any, Ref a) mkRef x idx = (Any x, Ref idx) (any0, ref0) = mkRef "hello" 0 (any1, ref

说我有

{-# LANGUAGE GADTs #-}

import Unsafe.Coerce 

data Any where
    Any :: a -> Any

type Index = Int

newtype Ref a = Ref Index

mkRef :: a -> Index -> (Any, Ref a)
mkRef x idx = (Any x, Ref idx)

(any0, ref0) = mkRef "hello" 0
(any1, ref1) = mkRef 'x' 1
(any2, ref2) = mkRef (666 :: Int) 2

anys :: [Any]
anys = [any0, any1, any2]

derefFrom :: Ref a -> [Any] -> a
(Ref idx) `derefFrom` pool = case pool !! idx of
    Any x -> unsafeCoerce x
如果我只使用带有适当构造的参数的
derefFrom
,那么这段代码会按预期工作吗?看起来是这样,但我不知道可能有什么问题

通过适当构造的参数,我的意思是:

ref0 `derefFrom` anys
ref1 `derefFrom` anys
ref2 `derefFrom` anys

(我会将
mkRef
的使用封装在monad中,以确保
Ref
s正确生成,并带有相应的列表,从而使此操作更加安全。)

是;只要您能够确保调用
unsecfectorce
仅强制一个实际属于目标类型的值,那么它是安全的。

我不会使用GADT存在主义。这不是使用文档明确表示有效的
unsafeccerce
。我会按照他们说的,使用
GHC.Prim
中的
Any
作为中间类型
Any
在GHC中有几种特殊的方式,其中一种是保证每种类型的值都能够在
Unsafeccerce
的情况下安全地往返于GHC中

但是还有很多要考虑的。一元包装并不像你想象的那么简单。假设你用最简单的方式写了它,比如:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

import qualified Data.IntMap.Strict as M

import Control.Applicative

import Control.Monad.State.Strict

import GHC.Prim (Any)
import Unsafe.Coerce


newtype Ref a = Ref Int

newtype Env a = Env (State (M.IntMap Any, Int) a)
    deriving (Functor, Applicative, Monad)


runEnv :: Env a -> a
runEnv (Env s) = evalState s (M.empty, 0)


mkRef :: a -> Env (Ref a)
mkRef x = Env $ do
    (m, c) <- get
    let m' = M.insert c (unsafeCoerce x) m
        c' = c + 1
    put (m', c')
    return $ Ref c


readRef :: Ref a -> Env a
readRef (Ref c) = Env $ do
    (m, _) <- get
    return . unsafeCoerce $ m M.! c


writeRef :: Ref a -> a -> Env ()
writeRef (Ref c) x = Env $ do
    (m, c') <- get
    let m' = M.insert c (unsafeCoerce x) m
    put (m', c')


-- a stupid example of an exceedingly imperative fib function
fib :: Int -> Env Int
fib x = do
    res <- mkRef 1
    let loop i = when (i <= x) $ do
            r <- readRef res
            writeRef res $ r * i
            loop (i + 1)
    loop 2
    readRef res


main :: IO ()
main = print $ runEnv (fib 5)
{-# LANGUAGE RankNTypes, GeneralizedNewtypeDeriving #-}

import qualified Data.IntMap.Strict as M

import Control.Applicative

import Control.Monad.State.Strict

import GHC.Prim (Any)
import Unsafe.Coerce


newtype Ref s a = Ref Int

newtype Env s a = Env (State (M.IntMap Any, Int) a)
    deriving (Functor, Applicative, Monad)


runEnv :: (forall s. Env s a) -> a
runEnv (Env s) = evalState s (M.empty, 0)


mkRef :: a -> Env s (Ref s a)
mkRef x = Env $ do
    (m, c) <- get
    let m' = M.insert c (unsafeCoerce x) m
        c' = c + 1
    put (m', c')
    return $ Ref c


readRef :: Ref s a -> Env s a
readRef (Ref c) = Env $ do
    (m, _) <- get
    return . unsafeCoerce $ m M.! c


writeRef :: Ref s a -> a -> Env s ()
writeRef (Ref c) x = Env $ do
    (m, c') <- get
    let m' = M.insert c (unsafeCoerce x) m
    put (m', c')


-- a stupid example of an exceedingly imperative fib function
fib :: Int -> Env s Int
fib x = do
    res <- mkRef 1
    let loop i = when (i <= x) $ do
            r <- readRef res
            writeRef res $ r * i
            loop (i + 1)
    loop 2
    readRef res


main :: IO ()
main = print $ runEnv (fib 5)
幸运的是,我们不需要从头开始解决这个问题——我们可以从历史的教训中吸取教训
ST
可能在
STRef
值在上下文之间泄漏时出现类似问题。解决方案在这一点上是众所周知的:确保
Ref
s不能通过使用通用量化类型变量从
runEnv
中逃脱

该代码看起来更像这样:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

import qualified Data.IntMap.Strict as M

import Control.Applicative

import Control.Monad.State.Strict

import GHC.Prim (Any)
import Unsafe.Coerce


newtype Ref a = Ref Int

newtype Env a = Env (State (M.IntMap Any, Int) a)
    deriving (Functor, Applicative, Monad)


runEnv :: Env a -> a
runEnv (Env s) = evalState s (M.empty, 0)


mkRef :: a -> Env (Ref a)
mkRef x = Env $ do
    (m, c) <- get
    let m' = M.insert c (unsafeCoerce x) m
        c' = c + 1
    put (m', c')
    return $ Ref c


readRef :: Ref a -> Env a
readRef (Ref c) = Env $ do
    (m, _) <- get
    return . unsafeCoerce $ m M.! c


writeRef :: Ref a -> a -> Env ()
writeRef (Ref c) x = Env $ do
    (m, c') <- get
    let m' = M.insert c (unsafeCoerce x) m
    put (m', c')


-- a stupid example of an exceedingly imperative fib function
fib :: Int -> Env Int
fib x = do
    res <- mkRef 1
    let loop i = when (i <= x) $ do
            r <- readRef res
            writeRef res $ r * i
            loop (i + 1)
    loop 2
    readRef res


main :: IO ()
main = print $ runEnv (fib 5)
{-# LANGUAGE RankNTypes, GeneralizedNewtypeDeriving #-}

import qualified Data.IntMap.Strict as M

import Control.Applicative

import Control.Monad.State.Strict

import GHC.Prim (Any)
import Unsafe.Coerce


newtype Ref s a = Ref Int

newtype Env s a = Env (State (M.IntMap Any, Int) a)
    deriving (Functor, Applicative, Monad)


runEnv :: (forall s. Env s a) -> a
runEnv (Env s) = evalState s (M.empty, 0)


mkRef :: a -> Env s (Ref s a)
mkRef x = Env $ do
    (m, c) <- get
    let m' = M.insert c (unsafeCoerce x) m
        c' = c + 1
    put (m', c')
    return $ Ref c


readRef :: Ref s a -> Env s a
readRef (Ref c) = Env $ do
    (m, _) <- get
    return . unsafeCoerce $ m M.! c


writeRef :: Ref s a -> a -> Env s ()
writeRef (Ref c) x = Env $ do
    (m, c') <- get
    let m' = M.insert c (unsafeCoerce x) m
    put (m', c')


-- a stupid example of an exceedingly imperative fib function
fib :: Int -> Env s Int
fib x = do
    res <- mkRef 1
    let loop i = when (i <= x) $ do
            r <- readRef res
            writeRef res $ r * i
            loop (i + 1)
    loop 2
    readRef res


main :: IO ()
main = print $ runEnv (fib 5)
{-#语言等级,泛化newtypederiving}
导入符合条件的Data.IntMap.Strict作为M
导入控制
进口控制。单子。状态。严格
导入GHC.Prim(任何)
导入不安全。强制
新类型Ref s a=Ref Int
newtype Env s a=Env(State(M.IntMap Any,Int)a)
派生(函子、应用程序、单子)
runEnv::(对于所有s.Env s a)->a
runEnv(Env s)=evalState s(M.empty,0)
mkRef::a->Env s(Ref s a)
mkRef x=Env$do
(m,c)环境署
readRef(Ref c)=环境$do
(m,u)a->Env s()
writeRef(Ref c)x=Env$do

(m,c')您可能还喜欢,这是一种已经封装好的安全使用
非安全性
,类似于您在这里所做的。@DanielWagner
Dynamic
与这里所做的完全不同
Dynamic
使用
Typeable
在运行时验证每个强制的正确性。这里使用的方法是使用幻影类型变量,以便在编译时获得所有必要的类型信息。您可能对
vault
感兴趣,它使用类型注释键实现这种异构容器: