在Haskell中部分应用几个函数

Ste*_*zel 3 haskell function function-composition

假设,在Haskell中,我有一堆函数都依赖于相同的参数类型:

f :: Par -> a -> b
g :: Par -> b -> c
Run Code Online (Sandbox Code Playgroud)

由于我正在编写更多依赖于此参数类型的函数,我可以执行类似的操作

h :: Par -> a -> c
h par = myg . myf
    where myf = f par
          myg = g par
Run Code Online (Sandbox Code Playgroud)

但是我不得不写这些where线.问题是:这可以避免吗?

[编辑:我试图提供一个最小的例子来说明问题,但显然这个例子太小而无法说明我想要的东西.在实际问题中,h当然不仅仅是f和g的组成.所以这是一些实际的代码:

有功能

apply :: ChamberLattice -> ChLatword -> ChLatWord
reduce :: ChamberLattice -> ChLatWord -> ChLatWord
Run Code Online (Sandbox Code Playgroud)

我正在定义一个函数

chaseTurn :: ChamberLattice -> Turn -> Parity -> ChLatWord -> ChLatWord
chaseTurn cl Straight _ xs = xs
chaseTurn cl t parity xs = if ((turn parity xs) == t)
                           then case myApply xs of
                               (y1:y2:ys) -> (y1:y2:(myChaseTurn t parity ys))
                               ys -> ys
                           else myReduce xs
where myApply = apply cl
      myChaseTurn = chaseTurn cl
      myReduce = reduce cl
Run Code Online (Sandbox Code Playgroud)

]

(这个问题与Haskell中的分组函数基本相同, 但在那里我使用了一些让人分心的不幸词.)

Mar*_*ann 6

在Haskell中,所有函数都接受一个输入参数.但有时候,应用函数的返回值是一个新函数.作为第一步,那么,你就可以说更明确的通过将围绕你的函数的返回值括号fg:

f :: Par -> (a -> b)
g :: Par -> (b -> c)
Run Code Online (Sandbox Code Playgroud)

功能类型为好,这样我们就可以随意决定别名a -> b?(皮皮而不是˚F),并b -> c?(伽马代替g ^).(是的,当你用完字母时,你会找到希腊字母!)

这意味着您可以将您的功能视为具有类型

f :: Par -> ?
g :: Par -> ?
Run Code Online (Sandbox Code Playgroud)

这些都是所谓的阅读器monad的自动实例,它也是一个(应用)函子.特别是,(->) Par或者,如果有帮助Par ->,则是一个Applicative实例.这意味着您可以使用它pure<*>使用它.

作为第一次尝试,你可以写一些像

pure (\x y -> (x, y)) <*> f <*> g
Run Code Online (Sandbox Code Playgroud)

为了简单地理解该组合是如何工作的.可以Par -> (?, ?)这么说,那个表达式就是这种类型.即λ表达式简单地取xf"容器",并yg"容器",并且将它们组合在一元组.元组的第一个元素具有类型?,第二个元素具有类型?.

在定义堵??,你得到的类型Par -> (a -> b, b -> c).

您需要组合这些函数,而不是将返回值作为函数元组.您可以使用函数组合运算符.:

h = pure (\x y -> y . x) <*> f <*> g
Run Code Online (Sandbox Code Playgroud)

请注意,该功能由右至左构成,所以x(a -> b)至上,其次是y(b -> c).

你可以,但是,翻转fg左右:

h = pure (\y x -> y . x) <*> g <*> f
Run Code Online (Sandbox Code Playgroud)

然后可以将显式lambda表达式简化为:

h = pure (.) <*> g <*> f
Run Code Online (Sandbox Code Playgroud)

最后,pure (.) <*>您可以使用中缀<$>运算符代替写入:

h = (.) <$> g <*> f
Run Code Online (Sandbox Code Playgroud)

此功能具有类型Par -> a -> c.


che*_*ner 5

Reader如果您可以稍微调整您的签名,您已经发现了monad 的用例.如果你有

f :: a -> Par -> b
g :: b -> Par -> c
Run Code Online (Sandbox Code Playgroud)

你可以将它们重新定义为

import Control.Monad.Trans.Reader

f :: a -> Reader Par b
g :: b -> Reader Par c
Run Code Online (Sandbox Code Playgroud)

然后,您可以h使用普通的Kleisli合成运算符进行定义.

import Control.Monad

h :: a -> Reader Par c
h = f >=> g
Run Code Online (Sandbox Code Playgroud)

(即使不改变签名,我想你也可以写h = flip (flip f >=> flip g).)