如何映射申请表格?

时间:2011-05-17 13:51:35

标签: haskell applicative

我想映射申请表格。

类似地图的功能类型如下:

mapX :: (Applicative f) => (f a -> f b) -> f [a] -> f [b]

用作:

result :: (Applicative f) => f [b]
result = mapX f xs
  where f  :: f a -> f b
        f = ...
        xs :: f[a]
        xs = ...

作为这篇文章的背景,我尝试用Paul Haduk的“The Haskell School of Expression”使用Applicative样式编写流体模拟程序,我想用Applicative样式表达模拟如下:

x, v, a :: Sim VArray
x = x0 +: integral (v * dt)
v = v0 +: integral (a * dt)
a = (...calculate acceleration with x v...)

instance Applicative Sim where
  ...

其中Sim类型表示模拟计算的过程,VArray表示向量数组(x,y,z)。 X,v a分别是位置,速度和加速度的数组。

在定义一个。

时,可以使用Applicative表单进行映射

我找到了一个问题的答案。

毕竟,我的问题是“如何解除高阶函数(如地图 ::(a - > b) - > [a] - > [b])到应用世界?“和答案 我发现是“使用提升的一阶函数构建它们。”

例如,“mapX”定义为提升的一阶函数 (headA,tailA,consA,nullA,condA)如下:

mapX :: (f a -> f b) -> f [a] -> f [b]
mapX f xs0 = condA (nullA xs0) (pure []) (consA (f x) (mapA f xs))
 where
   x = headA xs0
   xs = tailA xs0

headA = liftA head

tailA = liftA tail

consA = liftA2 (:)

nullA = liftA null

condA b t e = liftA3 aux b t e
  where aux b t e = if b then t else e

3 个答案:

答案 0 :(得分:7)

首先,我认为您提出的类型签名没有多大意义。给定一个应用列表f [a],没有将其转换为[f a]的一般方法 - 因此不需要类型为f a -> f b的函数。为了理智,我们将该函数减少到a -> f b(将其转换为另一个是微不足道的,但仅当f是monad时)。

现在我们想要:

mapX :: (Applicative f) => (a -> f b) -> f [a] -> f [b]

现在立即想到的是traverse,这是mapM的概括。 Traverse,专门列出:

traverse :: (Applicative f) => (a -> f b) -> [a] -> f [b]

关闭,但没有雪茄。同样,我们可以将遍历提升到所需的类型签名,但这需要monad约束:mapX f xs = xs >>= traverse f

如果你不介意monad约束,这很好(事实上你可以用mapM更直接地做到这一点)。如果你需要限制自己使用applicative,那么这应该足以说明你提出签名的原因是不可能的。

编辑:根据更多信息,我将开始解决潜在问题。

-- your sketch
a = liftA sum $ mapX aux $ liftA2 neighbors (x!i) nbr 
   where aux :: f Int -> f Vector3
   -- the type of "liftA2 neighbors (x!i) nbr" is "f [Int]

-- my interpretation
a = liftA2 aux x v
    where
       aux :: VArray -> VArray -> VArray 
       aux xi vi = ...

如果你不能像那样编写辅助 - 作为从一个时间点的位置和速度到加速度的纯函数,那么你有更大的问题......

这是一个直观的草图,为什么。流应用程序仿函数接受一个值并随着时间的推移将其提升为一个值 - 一个序列或值流。如果您可以访问一段时间内的值,则可以派生它的属性。因此,速度可以根据加速度来定义,位置可以根据速度和较低来定义。大!但现在你想根据位置和速度来定义加速度。也很棒!但是在这种情况下,不应该根据速度定义加速度。为什么,你可能会问?因为速度随着时间的推移所有加速都是从一开始。因此,如果您根据a定义dv,并根据v定义integral(a),那么您将获得一个闭环,并且您的方程式无法通过属性确定 - 即使在初始条件下,也有无限多的解决方案,或者根本没有解决方案。

答案 1 :(得分:6)

如果我正在考虑这个问题,那么你就不能只使用一个应用程序;你需要一个monad。如果您有Applicative - 请将其称为f - 您可以使用以下三种功能:

fmap  :: (a -> b) -> f a -> f b
pure  :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b

所以,考虑到一些f :: f a -> f b,您可以用它做什么?好吧,如果您有一些xs :: [a],那么您可以将其映射到map (f . pure) xs :: [f b]。如果您改为拥有fxs :: f [a],那么您可以改为fmap (map (f . pure)) fxs :: f [f b] 1 然而,您仍然坚持这一点。你想要一些[f b] -> f [b]类型的函数,可能还有f (f b) -> f b类型的函数;但是,你不能在applicative functors上定义这些(编辑:实际上,你可以定义前者;参见编辑)。为什么?好吧,如果你看fmappure<*>,你会发现你无法摆脱(或重新排列)f类型的构造函数,所以一旦你[f a],你就会陷入那种形式。

幸运的是,这就是monad的用途:计算可以“改变形状”,可以这么说。如果您有一个monad m,那么除了上述内容之外,您还会获得两个额外的方法(return作为pure的同义词):

(>>=) :: m a -> (a -> m b) -> m b
join  :: m (m a) -> m a

虽然join仅在Control.Monad中定义,但它与>>=一样基本,有时可以更清晰地思考。 现在我们可以定义您的[m b] -> m [b]功能或m (m b) -> m b。后者只是join;前言是sequence,来自序曲。因此,使用monad m,您可以将mapX定义为

mapX :: Monad m => (m a -> m b) -> m [a] -> m [b]
mapX f mxs = mxs >>= sequence . map (f . return)

然而,这将是一种奇怪的定义方式。前奏中的monad还有一些其他有用的函数:mapM :: Monad m => (a -> m b) -> [a] -> m [b],相当于mapM f = sequence . map f;和(=<<) :: (a -> m b) -> m a -> m b,相当于flip (>>=)。使用这些,我可能会将mapX定义为

mapX :: Monad m => (m a -> m b) -> m [a] -> m [b]
mapX f mxs = mapM (f . return) =<< mxs

编辑:实际上,我的错误:正如John L在评论中所指出的,Data.Traversable(这是一个基础包)提供了函数sequenceA :: (Applicative f, Traversable t) => t (f a) => f (t a);由于[]Traversable的一个实例,因此可以对应用仿函数进行排序。不过,您的类型签名仍然需要join=<<,因此您仍然会被卡住。我可能会建议重新考虑你的设计;我认为sclv可能有正确的想法。


1:map (f . pure) <$> fxs,使用来自Control.Applicative的<$>的{​​{1}}同义词。

答案 2 :(得分:-1)

以下是ghci中的会话,我按照您希望的方式定义mapX

Prelude> 
Prelude> import Control.Applicative
Prelude Control.Applicative> :t pure
pure :: Applicative f => a -> f a
Prelude Control.Applicative> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Prelude Control.Applicative> let mapX fun ma = pure fun <*> ma
Prelude Control.Applicative> :t mapX
mapX :: Applicative f => (a -> b) -> f a -> f b

但我必须补充一点,fmap最好使用,因为Functor的表达力不如Applicative(这意味着使用fmap会更频繁地工作)。< / p>

Prelude> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b

修改 哦,你有mapX的其他签名,无论如何,你可能意味着我建议的那个(fmap)?