部分应用一组功能

时间:2014-03-05 19:01:55

标签: haskell

我正在设计一个Haskell模块,该模块设计用于解决数学问题,可能有各种参数化。该模块导出一个函数:

run_and_output_parameterization :: ProblemParams -> String -> IO ()

其中的想法是在某些“控制器”中生成ProblemParams个对象,并按如下方式调用:

map (\(pp, name) -> run_and_output_parameterization pp name) (zip pp_list names_list)

我的问题是,在模块中,有一些函数,如索引函数,我想部分应用于特定的参数化。例如,

evenly_spaced_point_approx :: Int -> Int -> Int -> Double -> Double -> Int
evenly_spaced_point_approx xmin xmax xstep_i xstep_d target = pt
  where
    pt = max (min  (round (target/xstep_d) * xstep_i) xmax) xmin

evenly_spaced_si_approx target = evenly_spaced_point_approx (_pp_si_min pp) (_pp_si_max pp) (_pp_nstep_s pp) (_pp_nstep_sd pp) target
evenly_spaced_wi_approx target = evenly_spaced_point_approx (_pp_wi_min pp) (_pp_wi_max pp) (_pp_nstep_w pp) (_pp_nstep_wd pp) target

我想在模块中使用函数evenly_spaced_si_approxevenly_spaced_wi_approx来处理特定的ProblemParameter数据结构(称为pp)。

有没有办法告诉Haskell部分应用所有依赖函数,还是我必须手工完成的事情?此外,我对函数式编程术语不精确表示歉意。

1 个答案:

答案 0 :(得分:1)

如果您有许多需要相同参数的函数,并且这是他们所采用的唯一(或最后一个)参数,那么您可以利用Monad的{​​{1}}实例。或者,您可以将所有内容包装在(->) r monad中,其定义基本上是

Reader

newtype Reader r a = Reader { runReader :: r -> a } instance Monad (Reader r) where return a = Reader $ \_ -> a m >>= f = Reader $ \r -> runReader (f (runReader m r)) r 的{​​{1}}实例相比:

Monad

你怎么用这个?例如,如果您有一个参数(->) r,那么您可以将函数编写为

instance Monad ((->) r) where
    return a = const a
    m >>= f = \r -> f (m r) r

这非常有效,您只需要确保pp :: ProblemParams-- Some declarations smallFunc1 :: ProblemParams -> Double smallFunc2 :: ProblemParams -> Double smallFunc3 :: Int -> ProblemParams -> Double doStuff :: ProblemParams -> Double -- Just a random return type doStuff = do -- Keep the parameter implicit result1 <- smallFunc1 -- The ProblemParams are automatically passed result2 <- smallFunc2 result3 <- smallFunc3 10 return $ result1 + result2 + result3 smallFunc1的所有内容都以smallFunc2作为最后一个参数(请注意将smallFunc3 10ProblemParams一起包含在内。函数的10实例将在所有绑定中隐式传递该参数。可以把它想象成在计算该值之前返回一个值。您可以将smallFunc3的“未来”返回值绑定到Monad


或者,您可以使用smallFunc1 monad:

result1

我们必须创建一个Reader函数,将我们的基元提升为type Problem a = Reader ProblemParams a reader :: (r -> a) -> Reader r a reader f = do r <- ask return $ f r -- reader f = ask >>= return . f smallFunc1' :: Problem Double smallFunc1' = reader smallFunc1 smallFunc2' :: Problem Double smallFunc2' = reader smallFunc2 smallFunc3' :: Int -> Problem Double smallFunc3' i = reader (smallFunc3 i) doStuff :: ProblemParams -> Double doStuff pp = flip runReader pp $ do result1 <- smallFunc1' result2 <- smallFunc2' result3 <- smallFunc3' 10 return $ result1 + result2 + result3 monad的原因是reader实际上是根据变换器Reader定义的

Reader

围绕ReaderT monad。

无论您决定使用哪个都取决于您。我想大多数人会更熟悉type Reader r a = ReaderT r Identity a 版本,如果你决定再堆叠一些变形金刚以后就会非常简单。 Identity monad基本上有助于使函数签名看起来是monadic,因为Reader看起来不像普通的monad签名。它将使用更多的代码,但它可能会帮助您推理您的程序。


注意:我没有运行任何此代码,因此请注意可能存在小错误。如果有人发现问题,请告诉我,我会解决它。


Reader monad和ProblemParams -> Double的示例:

Par

然后,您只需使用ReaderTtype App a = ReaderT ProblemParams Par a runApp :: ProblemParams -> App a -> a runApp pp app = runPar $ runReaderT app pp 次操作提升为lift次操作:

Par

我大约99%确定monad堆栈根本不会影响你的并行性,因为App monad首先执行,基本上将parReader :: (ProblemParams -> Par a) -> App a parReader f = do r <- ask lift $ f r -- parReader f = ask >>= lift . f doStuff :: ProblemParams -> Double doStuff pp = runApp pp $ do result1 <- parReader parAction1 result2 <- parReader parAction2 result3 <- parReader (parAction3 10) return $ result1 + result2 + result3 应用于所有函数,然后运行Reader行动。