在 Haskell 中应用多个参数的更简单方法
Easier way to apply multiple arguments in Haskell
给定值 f
,类型为 :: Applicative f => f (a -> b -> c)
,将参数映射到内部函数的最佳方法是什么。
到目前为止,我发现了以下内容:
(\x -> x a b) <$> f
(flip ($ a) b) <$> f
($ b) <$> ($ a) <$> f
我想我的问题是为什么 Haskell 没有 :: a -> b -> (a -> b -> c) -> c
函数。或者是吗?
Applicative
class 具有 <*>
运算符(通常发音为 "ap",对于大多数 Monad
来说等同于 Control.Monad.ap
),它与 <$>
运算符(本身只是 fmap
的中缀别名)相结合,让您可以编写类似
的代码
-- f :: a -> b -> c
-- fa :: Applicative f => f a
-- fb :: Applicative f => f b
f <$> fa <*> fb :: Applicative f => f c
如果您需要应用纯参数,则使用 Applicative
class:
的 pure
方法
-- f :: a -> b -> c
-- a :: a
-- b :: b
f <$> pure a <*> pure b :: Applicative f => f c
一个例子可能是
sumOfSquares :: Num a => a -> a -> a
sumOfSquares a b = a * a + b * b
> sumOfSquares <$> Just 1 <*> Just 2
Just 5
> sumOfSquares <$> Just 1 <*> Nothing
Nothing
> sumOfSquares <$> pure 1 <*> pure 2 :: Maybe Int
5
> sumOfSquares <$> readLn <*> readLn :: IO Int
1<ENTER>
2<ENTER>
5
这里的Applicative f => f (a -> b -> c)
是由f <$>
构建的,所以如果你已经有了类似
的东西
> let g :: IO (Int -> Int -> Int); g = undefined
那你就可以把它当作
> g <*> pure 1 <*> pure 2
<*>
运算符的类型为
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
所以如果你的函数有类型x -> y -> z
,那么a ~ x
和b ~ y -> z
,所以重复ap<*>
的应用(明白了吗?)将更多参数传递给包装函数。
我们有
(<$>) :: Functor f => (a -> b) -> f a -> f b
但你想要的恰恰相反
(>$<) :: Functor f => f (a -> b) -> a -> f b
我们可以很容易地定义:
(>$<) f a = ($a) <$> f
所以给出
f :: Functor f => f (a -> b -> c)
a :: a
b :: b
然后
f >$< a :: f (b -> c)
f >$< a >$< b :: f c
这不像 <*>
那样地道,但它适用于所有 Functor
,而不仅仅是 Applicative
,这很好。
给定值 f
,类型为 :: Applicative f => f (a -> b -> c)
,将参数映射到内部函数的最佳方法是什么。
到目前为止,我发现了以下内容:
(\x -> x a b) <$> f
(flip ($ a) b) <$> f
($ b) <$> ($ a) <$> f
我想我的问题是为什么 Haskell 没有 :: a -> b -> (a -> b -> c) -> c
函数。或者是吗?
Applicative
class 具有 <*>
运算符(通常发音为 "ap",对于大多数 Monad
来说等同于 Control.Monad.ap
),它与 <$>
运算符(本身只是 fmap
的中缀别名)相结合,让您可以编写类似
-- f :: a -> b -> c
-- fa :: Applicative f => f a
-- fb :: Applicative f => f b
f <$> fa <*> fb :: Applicative f => f c
如果您需要应用纯参数,则使用 Applicative
class:
pure
方法
-- f :: a -> b -> c
-- a :: a
-- b :: b
f <$> pure a <*> pure b :: Applicative f => f c
一个例子可能是
sumOfSquares :: Num a => a -> a -> a
sumOfSquares a b = a * a + b * b
> sumOfSquares <$> Just 1 <*> Just 2
Just 5
> sumOfSquares <$> Just 1 <*> Nothing
Nothing
> sumOfSquares <$> pure 1 <*> pure 2 :: Maybe Int
5
> sumOfSquares <$> readLn <*> readLn :: IO Int
1<ENTER>
2<ENTER>
5
这里的Applicative f => f (a -> b -> c)
是由f <$>
构建的,所以如果你已经有了类似
> let g :: IO (Int -> Int -> Int); g = undefined
那你就可以把它当作
> g <*> pure 1 <*> pure 2
<*>
运算符的类型为
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
所以如果你的函数有类型x -> y -> z
,那么a ~ x
和b ~ y -> z
,所以重复ap<*>
的应用(明白了吗?)将更多参数传递给包装函数。
我们有
(<$>) :: Functor f => (a -> b) -> f a -> f b
但你想要的恰恰相反
(>$<) :: Functor f => f (a -> b) -> a -> f b
我们可以很容易地定义:
(>$<) f a = ($a) <$> f
所以给出
f :: Functor f => f (a -> b -> c)
a :: a
b :: b
然后
f >$< a :: f (b -> c)
f >$< a >$< b :: f c
这不像 <*>
那样地道,但它适用于所有 Functor
,而不仅仅是 Applicative
,这很好。