假设我有一个秘密,我保留在一个类型
data Secret a = Secret a deriving Functor, Show
sec :: Secret String
Run Code Online (Sandbox Code Playgroud)
我想允许计算秘密,并以某种方式查看结果,例如
getSecretHash :: Show a => Secret a -> String
Run Code Online (Sandbox Code Playgroud)
要么
askQuestion :: (a->Bool) -> Secret a -> Bool
Run Code Online (Sandbox Code Playgroud)
但是我不想让这个秘密被直接提取出来(我知道你可以强行推出上述方法之一,但是假设秘密很大,所以这是不可行的).
当然有人可以写
reveal :: Secret a -> a
reveal (Secret x) = x
Run Code Online (Sandbox Code Playgroud)
而且我知道我可以通过在模块中放置秘密而不是导出构造函数而是给出一个来防止这种情况makeSecret :: a->Secret a,但我想知道是否有办法使用类型系统来完成它.
在不隐藏构造函数的情况下,如何创建一个不能任意提取其值的类型?
这个
askQuestion ::(a - > Bool) - >秘密a - >布尔
看起来有点像翻转版本 runCont
? import Control.Monad.Trans.Cont
? :t runCont
runCont :: forall r a. Cont r a -> (a -> r) -> r
? :set -XTypeApplications
? :t runCont @Bool
runCont @Bool :: forall a. Cont Bool a -> (a -> Bool) -> Bool
? :t flip (runCont @Bool)
flip (runCont @Bool) :: forall a. (a -> Bool) -> Cont Bool a -> Bool
Run Code Online (Sandbox Code Playgroud)
所以也许在这方面你的Secret类型是Cont Bool,你可以使用cont创建值:
cont :: forall a. ((a -> Bool) -> Bool) -> Cont Bool a
makeSecret :: forall a. a -> Cont Bool a
makeSecret a = cont $ \f -> f a
Run Code Online (Sandbox Code Playgroud)
实际值隐藏在函数后面.
好吧,如果使用花哨的Haskell类型不是必需的,那么你可以使用旧的函数闭包技巧.只需将数据类型定义为查询功能:
data Secret a = Secret { query :: (a -> Bool) -> Bool }
Run Code Online (Sandbox Code Playgroud)
导出辅助函数来构造机密可能会有所帮助(虽然它完全是可选的,因为构造函数是公共的,任何人都可以创建自己的makeSecret函数):
makeSecret :: a -> Secret a
makeSecret x = Secret (\f -> f x) -- or Secret ($x) if you're feeling clever
Run Code Online (Sandbox Code Playgroud)
定义askQuestion很简单:
askQuestion :: (a -> Bool) -> Secret a -> Bool
askQuestion = flip query
Run Code Online (Sandbox Code Playgroud)
我想这最终类似于danidiaz答案,但monad机器并不是真正需要在函数中存储秘密.
请注意,如果您需要一个functor实例Secret,Haskell可以导出一个,它按预期工作:
> askQuestion (=="Stack Overflow") $ fmap (++" Overflow") $ makeSecret "Stack"
True
Run Code Online (Sandbox Code Playgroud)
我想在技术上你可以通过作弊来解决它,就像这样,但我不确定任何其他方法可以避免这种情况:
> askQuestion (\x -> unsafePerformIO (putStrLn (show x)
>> return False)) $ makeSecret "secret"
"secret"
False
>
Run Code Online (Sandbox Code Playgroud)