隐藏Haskell中的约束
如果你使用的函数有很多约束条件隐藏Haskell中的约束,haskell,Haskell,如果你使用的函数有很多约束条件 f :: (C_0, ..., C_n) => .... 而f用于g的定义,那么g也需要这些约束。但是,这些约束可能指的是g不应该知道的事情(因为这可能会泄露有关如何实现f的信息)。假设这是一件明智的事情,那么隐藏C_0的明智方式是什么。。。C_n 我试过这样的方法(请不要拔头发): 但是,虽然这样做有效,但它会导致可简化的类约束警告,敦促我使用单本地绑定,或者用C_0,…,C_n替换CanApplyF 在Haskell中有没有实现隐藏约束的方法?老实说,
f :: (C_0, ..., C_n) => ....
而f
用于g
的定义,那么g
也需要这些约束。但是,这些约束可能指的是g
不应该知道的事情(因为这可能会泄露有关如何实现f
的信息)。假设这是一件明智的事情,那么隐藏C_0的明智方式是什么。。。C_n
我试过这样的方法(请不要拔头发):
但是,虽然这样做有效,但它会导致可简化的类约束警告,敦促我使用单本地绑定,或者用C_0,…,C_n
替换CanApplyF
在Haskell中有没有实现隐藏约束的方法?老实说,我认为您所做的应该是正确的方法。实际上,
-wsimpilifiable类约束
有点可疑,尽管有时确实有用。尽管如此,如果对约束的了解实际上是一种安全风险,那么我敢说这可能是没有希望的。(也是,但这是另一个讨论。)
可能的解决办法:
- 使
不是一个新类,而只是一个约束同义词 这应该是可行的,但无论如何它都不是一个严格的封装。找出约束CanApplyF
真正包含的内容并不重要,事实上(与其他类型同义词一样),它们可能会意外地出现在错误消息中CanApplyF
- 添加一个虚拟方法,这样从技术上讲,
就不等同于它的超类的组合CanApplyF
(这不是那样工作的,因为实例是模糊的。您需要使用class (C_0, ..., C_n) => CanApplyF where onlyFUsesThis :: () f = onlyFUsesThis `seq` ...
和-xallowambigioustypes
使其工作,或者引入一些-XTypeApplications
代理来工作。)
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
import Prelude hiding (empty,insert,lookup,toList)
import Data.Foldable (foldl')
import qualified Data.Map.Strict as M
data Mappy m a = Mappy
{ empty :: forall b. m a b,
insert :: forall b. a -> b -> m a b -> m a b,
lookup :: forall b. a -> m a b -> Maybe b,
toList :: forall b. m a b -> [(a, b)]
}
histogram :: Mappy m a -> [a] -> [(a, Int)]
histogram (Mappy {empty, insert, lookup, toList}) = toList . foldl' step empty
where
step acc k = case lookup k acc of
Nothing -> insert k 1 acc
Just count -> insert k (succ count) acc
直方图
不知道它在内部使用的贴图关键点上所需的任何约束
将其付诸实施:
mappy :: Ord a => Mappy M.Map a
mappy =
Mappy
{ empty = M.empty,
insert = M.insert,
lookup = M.lookup,
toList = M.toList
}
main :: IO ()
main = print $ histogram mappy "aabbbccc"
-- [('a',2),('b',3),('c',3)]
也许我们可以使用typeclass,而不是记录,一个拥有所有类型所需的“surface”操作作为方法的类:
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Kind
-- a dictionary
class Mappy a where
type Container a :: Type -> Type
empty :: forall b. Container a b
insert :: a -> b -> Container a b -> Container a b
lookup :: a -> Container a b -> Maybe b
toList :: Container a b -> [(a, b)]
histogram :: forall a. Mappy a => [a] -> [(a, Int)]
histogram xs = toList $ foldl' step (empty @a @Int) xs
where
step acc k = case lookup k acc of
Nothing -> insert k 1 acc
Just count -> insert k (succ count) acc
这些解决方案隐藏了
直方图
中的Eq
和Ord
等约束,但让客户工作得更多:现在他必须组装记录字典,或者声明新的typeclass实例。如果函数的参数不知道它想要什么,人们将如何使用该函数?@berealCanApplyF
的实例将列在Haddocks中。用户只是不知道为什么这些是允许的类型,而其他类型不是。我希望用户仍然必须有一个约束来说明f
所期望的内容,但这样做不会泄露f
实现中的任何信息。因此,CanApplyF
是一个如何隐藏约束的示例,尽管可能很糟糕。为了给出一个附加示例,假设f
在f
使用的类型的子字段中具有Ord
约束。不仅f
会泄露Ord
约束,还会泄露它所使用的数据的子字段。@damianadales把整个f
作为参数传递给g
怎么样?我不太清楚类型类会泄露哪些重要的实现细节,但是,如果有一个可以与函数一起使用的类型的详尽列表,则隐藏泛型实现并仅导出具有具体类型的包装器。“了解依赖项的依赖项不是一个好主意。最好只了解直接依赖项(或者更准确地说,它们的接口)。”
mappy :: Ord a => Mappy M.Map a
mappy =
Mappy
{ empty = M.empty,
insert = M.insert,
lookup = M.lookup,
toList = M.toList
}
main :: IO ()
main = print $ histogram mappy "aabbbccc"
-- [('a',2),('b',3),('c',3)]
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Kind
-- a dictionary
class Mappy a where
type Container a :: Type -> Type
empty :: forall b. Container a b
insert :: a -> b -> Container a b -> Container a b
lookup :: a -> Container a b -> Maybe b
toList :: Container a b -> [(a, b)]
histogram :: forall a. Mappy a => [a] -> [(a, Int)]
histogram xs = toList $ foldl' step (empty @a @Int) xs
where
step acc k = case lookup k acc of
Nothing -> insert k 1 acc
Just count -> insert k (succ count) acc