假设我有一个秘密,我保留在一个类型
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
来防止这种情况,但我想知道是否有使用类型系统的方法。
如果不隐藏构造函数,我怎样才能创建一个不能任意提取其值的类型?
答案 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
>