在 haskell 中增加环境的应用程序
Applicative that increments the environment in haskell
我想知道是否有一个 Applicative
可以跟踪发生了多少应用操作。我尝试按如下方式实现它:
import Control.Applicative
main :: IO ()
main = print $ run 1 $ (,,,) <$> FromInt id <*> FromInt id <*> FromInt id <*> FromInt id
data FromInt a = FromInt (Int -> a)
run :: Int -> FromInt a -> a
run i (FromInt f) = f i
instance Functor FromInt where
fmap g (FromInt f) = FromInt (g . f)
instance Applicative FromInt where
pure a = FromInt (const a)
FromInt f <*> FromInt g = FromInt (\i -> f i (g (i + 1)))
但是,这当然行不通。如果我们在文件上调用 runhaskell,我们会得到:
(1,2,2,2)
而我想要的是:
(1,2,3,4)
我见过人们通过将增量需求推入实际数据来实现这种效果(这就是 yesod-forms 实现其 formlet 样式的方式)。这或多或少地使用了 State
的变体,如果人们不使用特定的辅助函数,它允许人们打破假设的不变量(我认为是的那个叫做 mhelper
)。我想知道递增是否可以像我尝试的那样被拉入应用实例。这将使违反这个特定的不变量变得不可能。
当 a
是 Monoid
时,(,) a
是 Applicative
。我们可以使用 Data.Functor.Compose
将 (,) (Sum Int)
与其他应用程序组合,并获得一个应用程序,让我们在 运行 之前估计分配给计算的 "cost"。
要计算步数,我们需要来自基础应用程序的提升函数,该函数始终分配 1 的成本:
module Main where
import Data.Monoid
import Control.Applicative
import Data.Functor.Compose
type CountedIO a = Compose ((,) (Sum Int)) IO a
-- lift from IO
step :: IO a -> CountedIO a
step cmd = Compose (Sum 1, cmd)
countSteps :: CountedIO a -> Int
countSteps = getSum . fst . getCompose
exec :: CountedIO a -> IO a
exec = snd . getCompose
program :: CountedIO ()
program = step (putStrLn "aaa") *> step (putStrLn "bbb") *> step (putStrLn "ccc")
main :: IO ()
main = do
putStrLn $ "Number of steps: " ++ show (countSteps program)
exec program
为了更安全,我们可以将组合应用程序隐藏在新类型后面,并且不导出构造函数,只导出 step
函数。
(使用 pure
创建的操作成本为 0,不计为一步。)
我想知道是否有一个 Applicative
可以跟踪发生了多少应用操作。我尝试按如下方式实现它:
import Control.Applicative
main :: IO ()
main = print $ run 1 $ (,,,) <$> FromInt id <*> FromInt id <*> FromInt id <*> FromInt id
data FromInt a = FromInt (Int -> a)
run :: Int -> FromInt a -> a
run i (FromInt f) = f i
instance Functor FromInt where
fmap g (FromInt f) = FromInt (g . f)
instance Applicative FromInt where
pure a = FromInt (const a)
FromInt f <*> FromInt g = FromInt (\i -> f i (g (i + 1)))
但是,这当然行不通。如果我们在文件上调用 runhaskell,我们会得到:
(1,2,2,2)
而我想要的是:
(1,2,3,4)
我见过人们通过将增量需求推入实际数据来实现这种效果(这就是 yesod-forms 实现其 formlet 样式的方式)。这或多或少地使用了 State
的变体,如果人们不使用特定的辅助函数,它允许人们打破假设的不变量(我认为是的那个叫做 mhelper
)。我想知道递增是否可以像我尝试的那样被拉入应用实例。这将使违反这个特定的不变量变得不可能。
a
是 Monoid
时,(,) a
是 Applicative
。我们可以使用 Data.Functor.Compose
将 (,) (Sum Int)
与其他应用程序组合,并获得一个应用程序,让我们在 运行 之前估计分配给计算的 "cost"。
要计算步数,我们需要来自基础应用程序的提升函数,该函数始终分配 1 的成本:
module Main where
import Data.Monoid
import Control.Applicative
import Data.Functor.Compose
type CountedIO a = Compose ((,) (Sum Int)) IO a
-- lift from IO
step :: IO a -> CountedIO a
step cmd = Compose (Sum 1, cmd)
countSteps :: CountedIO a -> Int
countSteps = getSum . fst . getCompose
exec :: CountedIO a -> IO a
exec = snd . getCompose
program :: CountedIO ()
program = step (putStrLn "aaa") *> step (putStrLn "bbb") *> step (putStrLn "ccc")
main :: IO ()
main = do
putStrLn $ "Number of steps: " ++ show (countSteps program)
exec program
为了更安全,我们可以将组合应用程序隐藏在新类型后面,并且不导出构造函数,只导出 step
函数。
(使用 pure
创建的操作成本为 0,不计为一步。)