保持Haskell的秘密

时间:2017-10-12 17:01:58

标签: haskell functor

假设我有一个秘密,我保留在一个类型

data Secret a = Secret a deriving Functor, Show
sec :: Secret String

我希望允许对秘密进行计算,并以某种方式查看结果,例如。

getSecretHash :: Show a => Secret a -> String

askQuestion :: (a->Bool) -> Secret a -> Bool

但我不想直接提取秘密(我知道你可以强行执行上述方法之一,但假设秘密很大,所以这是不可行的)。

当然有人可以写

reveal :: Secret a -> a
reveal (Secret x) = x

我知道我可以通过在模块中放置秘密并且不导出构造函数而是提供makeSecret :: a->Secret a来防止这种情况,但我想知道是否有使用类型系统的方法。

如果不隐藏构造函数,我怎样才能创建一个不能任意提取其值的类型?

3 个答案:

答案 0 :(得分:7)

  

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

因此,或许在这方面,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

实际值隐藏在函数后面。

答案 1 :(得分:5)

  

如果不隐藏构造函数,我怎样才能创建一个不能任意提取其值的类型?

没有。隐藏构造函数恰恰是正确的工具,也是我能想到的唯一合理的方法。

答案 2 :(得分:5)

好吧,如果不需要使用花哨的Haskell类型,那么你可以使用旧的函数闭包技巧。只需将数据类型定义为查询函数:

data Secret a = Secret { query :: (a -> Bool) -> Bool }

导出辅助函数来构造机密可能会有所帮助(虽然它完全是可选的,因为构造函数是公共的,任何人都可以创建自己的makeSecret函数):

makeSecret :: a -> Secret a
makeSecret x = Secret (\f -> f x)   -- or Secret ($x) if you're feeling clever

askQuestion的定义很简单:

askQuestion :: (a -> Bool) -> Secret a -> Bool
askQuestion = flip query

我想这最终类似于danidiaz的回答,但monad机制并不是真正需要在函数中存储秘密。

请注意,如果您需要这个Secret的仿函数实例,Haskell可以导出一个,并且它按预期工作:

> askQuestion (=="Stack Overflow") $ fmap (++" Overflow") $ makeSecret "Stack"
True

我觉得从技术上讲,你可以通过作弊来解决问题,但是我不确定其他任何一种方法都可以避免这种情况:

> askQuestion (\x -> unsafePerformIO (putStrLn (show x) 
     >> return False)) $ makeSecret "secret"
"secret"
False
>