Haskell 数据加载/卸载与处理逻辑的分离
有时,为了检索或保存正在处理的数据,需要执行一些复杂的例程。在这种情况下,需要将数据生成和数据处理逻辑分开。常用的方法是使用类似iteratee的功能。有很多像样的库:管道、导管等。在大多数情况下,它们可以完成这项工作。但它们(可能除了管道)受到加工顺序的限制Haskell 数据加载/卸载与处理逻辑的分离,haskell,haskell-pipes,Haskell,Haskell Pipes,有时,为了检索或保存正在处理的数据,需要执行一些复杂的例程。在这种情况下,需要将数据生成和数据处理逻辑分开。常用的方法是使用类似iteratee的功能。有很多像样的库:管道、导管等。在大多数情况下,它们可以完成这项工作。但它们(可能除了管道)受到加工顺序的限制 但是考虑一个日志查看器的例子:人类可能想要随机地来回漫游。他也可以放大和缩小。恐怕你在这里帮不了忙 简单的解决方案可能如下所示: -- True is for 'right', 'up', etc. and vice versa typ
但是考虑一个日志查看器的例子:人类可能想要随机地来回漫游。他也可以放大和缩小。恐怕你在这里帮不了忙
简单的解决方案可能如下所示:-- True is for 'right', 'up', etc. and vice versa
type Direction = Bool
class Frame (f :: * -> *) where
type Dimension f :: *
type Origin f :: * -> *
grow', shrink' move' :: Monad m => Dimension f -> Direction -> f a -> m (f a)
move' dim dir f = grow' dim dir f >>= shrink' dim (not dir)
liftF' :: (Origin f a -> b) -> f a -> b
class Frame f => MFrame f where
liftMF' :: (Origin f a -> (b, Origin f a)) -> f a -> (b, f a)
-- Example instance: infinite stream.
data LF a = LF [a] [a] [a]
instance Frame LF where
type Dimension LF = () -- We have only one dimension to move in...
type Origin LF = [] -- User see piece of stream as a plain list
liftF' f (LF _ m _) = f m
grow' () True (LF l m (h:r)) = return $ LF l (m++[h]) r
...
data Asset a b = Asset
{ _art :: a
, _sound :: b
}
然后我们可以把它包装成StateT等等。因此,问题是:
0)我是否完全忽略了迭代对象的要点,它们在这里是适用的
1) 我是不是重新发明了一个著名的轮子
2) 很明显,增长和收缩操作非常无效,因为它们的复杂性与帧大小成正比。有没有更好的方法像这样扩展拉链?您需要镜头,特别是
sequenceOf
功能。以下是3元组的目标加载示例:
sequenceOf _2 :: (IO a, IO b, IO c) -> IO (IO a, b, IO c)
sequenceOf
将镜头移到包含加载操作的多态字段,运行该操作,然后用操作结果替换该字段。您可以在自己的自定义类型上使用sequenceOf
,只需在要加载的字段中使类型具有多态性,如下所示:
-- True is for 'right', 'up', etc. and vice versa
type Direction = Bool
class Frame (f :: * -> *) where
type Dimension f :: *
type Origin f :: * -> *
grow', shrink' move' :: Monad m => Dimension f -> Direction -> f a -> m (f a)
move' dim dir f = grow' dim dir f >>= shrink' dim (not dir)
liftF' :: (Origin f a -> b) -> f a -> b
class Frame f => MFrame f where
liftMF' :: (Origin f a -> (b, Origin f a)) -> f a -> (b, f a)
-- Example instance: infinite stream.
data LF a = LF [a] [a] [a]
instance Frame LF where
type Dimension LF = () -- We have only one dimension to move in...
type Origin LF = [] -- User see piece of stream as a plain list
liftF' f (LF _ m _) = f m
grow' () True (LF l m (h:r)) = return $ LF l (m++[h]) r
...
data Asset a b = Asset
{ _art :: a
, _sound :: b
}
。。。并使您的镜头使用完整的四种类型参数(这是它们存在的原因之一):
。。。或者,您可以使用makelens
自动生成镜头,它们将足够通用
然后你可以写:
sequenceOf art :: Asset (IO Art) b -> IO (Asset Art b)
。。。加载多个资产就像编写Kleisli箭头一样简单:
sequenceOf art >=> sequenceOf sound
:: Asset (IO Art) (IO Sound) -> IO (Asset Art Sound)
。。。当然,您可以嵌套资源并组合镜头以访问嵌套资源,而所有内容仍然“正常工作”
现在您有了一个纯资产
类型,您可以使用纯函数来处理它,并且所有的加载逻辑都被分解到透镜中
我在手机上写了这封信,所以可能会出现一些错误,但我稍后会修复它们。一个非常具体的评论:您可能希望将函数
中的monad增长“
,收缩“
,移动”
作为关联类型。如果你像现在一样一直保持它,你最好不要一个单子,因为它几乎没有什么有趣的东西可以用。