我正在两个模型之间构建 API。我不在乎它是否返回 [] 或 Seq 或任何可折叠的都可以。但如果我尝试这样做,就会出错。
module Main where
import Prelude hiding (foldr)
import Data.Foldable
import Data.Sequence
data Struct = Struct
main = do
print $ foldr (+) 0 $ list Struct
print $ foldr (+) 0 $ listFree Struct
listFree :: Foldable f => a -> f Int
listFree s = singleton 10
class TestClass a where
list :: Foldable f => a -> f Int
instance TestClass Struct where
list s = singleton 10
listFree 和 list 定义都会给出相同的错误:
TestFoldable.hs:19:12:
Could not deduce (f ~ [])
from the context (Foldable f)
bound by the type signature for
list :: Foldable f => Struct -> f Int
at TestFoldable.hs:19:3-15
`f' is a rigid type variable bound by
the type signature for list :: Foldable f => Struct -> f Int
at TestFoldable.hs:19:3
In the expression: [10]
In an equation for `list': list s = [10]
In the instance declaration for `TestClass Struct'
这是为什么?完成我在这里想做的事情的“正确”方法是什么?
我想要完成的是向调用者隐藏实现。实际的数据结构可能是 Seq、IntMap 或其他任何结构,而且很可能不是列表。
我收到的回复是“只需返回一个列表”。但这意味着转变,不是吗?如果它是一个 1,000,000 个元素结构怎么办?仅仅因为 API 的限制而将其转换为中间数据结构似乎是一个糟糕的解决方案。
这是一个普遍的问题。如何获得符合某些 API 的返回值?隐藏具体的实现,以便实现者可以自由选择最适合他们的结构,并且可以更改它而无需更改 API 的用户。
另一种说法是:如何返回接口而不是具体类型?
结束语:
Stack Overflow 上的 Haskell 社区是(Superlative Compliment c => forall c.c)
存在量化似乎是解决这种情况的通用方法。
另一种需要考虑的可能性是为客户端返回折叠的闭包,这不是通用解决方案,但可能适用于这种特定情况,可能会避免存在解决方案所需的额外包装器值:
list :: a -> ((Int -> b -> b) -> b -> b)
list = \f a0 -> foldr f a0 (singleton 10)