我想映射申请表格。
类似地图的功能类型如下:
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
答案 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上定义这些(编辑:实际上,你可以定义前者;参见编辑)。为什么?好吧,如果你看fmap
,pure
和<*>
,你会发现你无法摆脱(或重新排列)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
)?